2011-09-08 15 views
8

Para una de mis aplicaciones, necesitaría dibujar una curva discontinua en el camino bezier en el lienzo Html5 ... La longitud del tablero y los huecos intermedios deberían ser variables ... Es posible en JavaFx, see this link ... Me gustaría lograr el mismo efecto utilizando el lienzo Html5. Sé cómo dibujar líneas discontinuas, pero no líneas curvas a lo largo del bezier ...Curvas rotas en Html5 Lienzo Bezier

Aunque no soy un experto, conozco el bezier drawing algorithm, el problema que veo con este algoritmo es que te permite identificar las coordenadas en el bezier usando el parámetro de tiempo que va de 0 a 1 ...

Esto no es suficiente porque para dibujar un bezier discontinuo, necesitaría dibujar muchos beziers pequeños, con un parámetro de longitud especificado y a una distancia de separación dada, en el camino principal bezier. Debe haber algún algoritmo utilizado por JavaFx. Si alguien puede ayudarme, sería genial.

+0

Hay una aplicación inteligente de algo similar que puede ser capaz de adaptarse a las curvas discontinuas aquí: http://stackoverflow.com/questions/ 4576724/stroke-in-canvas en puntos Ejemplo en vivo aquí: http://phrogz.net/tmp/canvas_dashed_line.html – unmounted

+0

Como dije, sé cómo trazar la línea punteada, el problema es cómo dibujar curvas discontinuas en el bezier path ... –

+0

Supongo que podrías usar el mod op (%) en tu bez drawing algo. Ajuste alfa a cero para la posición par y alfa regular para la posición impar en la curva relativa a su longitud. Si me puedes dar tu bezier algo, no me importa tapar esta matemática. :) –

Respuesta

4

Supongo que JavaFX está utilizando una técnica general para dibujar cualquier curva discontinua y que simplemente está usándola en un bezier en ese ejemplo.

La parte difícil es saber por dónde empezar y parar cada guión, que requiere conocer la arc length de su curva de Bezier en varios puntos a lo largo de ella.

Hay un enfoque analítico, pero sugeriría lo siguiente:

var bezier = function(controlPoints, t) { 
    /* your code here, I'll presume it returns a 2-element array of x and y. */ 
}; 

//just figure out the coordinates of all the points in each dash, don't draw. 
//returns an array of arrays, each sub-array will have an even number of nu- 
//merical elements, to wit, x and y pairs. 

//Argument dashPattern should be an array of alternating dash and space 
//lengths, e.g., [10, 10] would be dots, [30, 10] would be dashes, 
//[30, 10, 10, 10] would be 30-length dash, 10-length spaces, 10-length dash 
// and 10-length space. 
var calculateDashedBezier = function(controlPoints, dashPattern) { 
    var step = 0.001; //this really should be set by an intelligent method, 
        //rather than using a constant, but it serves as an 
        //example. 

    //possibly gratuitous helper functions 
    var delta = function(p0, p1) { 
    return [p1[0] - p0[0], p1[1] - p0[1]]; 
    }; 
    var arcLength = function(p0, p1) { 
    var d = delta(p0, p1); 
    return Math.sqrt(d[0]*d[0] + d[1] * d[1]); 
    }; 

    var subPaths = []; 
    var loc = bezier(controlPoints, 0); 
    var lastLoc = loc; 

    var dashIndex = 0; 
    var length = 0; 
    var thisPath = []; 
    for(var t = step; t <= 1; t += step) { 
    loc = bezier(controlPoints, t); 
    length += arcLength(lastLoc, loc); 
    lastLoc = loc; 

    //detect when we come to the end of a dash or space 
    if(length >= dashPattern[dashIndex]) { 

     //if we are on a dash, we need to record the path. 
     if(dashIndex % 2 == 0) 
     subPaths.push(thisPath); 

     //go to the next dash or space in the pattern 
     dashIndex = (dashIndex + 1) % dashPattern.length; 

     //clear the arclength and path. 
     thisPath = []; 
     length = 0; 
    } 

    //if we are on a dash and not a space, add a point to the path. 
    if(dashIndex % 2 == 0) { 
     thisPath.push(loc[0], loc[1]); 
    } 
    } 
    if(thisPath.length > 0) 
    subPaths.push(thisPath); 
    return subPaths; 
}; 

//take output of the previous function and build an appropriate path 
var pathParts = function(ctx, pathParts) { 
    for(var i = 0; i < pathParts.length; i++) { 
    var part = pathParts[i]; 
    if(part.length > 0) 
     ctx.moveTo(part[0], part[1]); 
    for(var j = 1; j < part.length/2; j++) { 
     ctx.lineTo(part[2*j], part[2*j+1]); 
    } 
    } 
}; 

//combine the above two functions to actually draw a dashed curve. 
var drawDashedBezier = function(ctx, controlPoints, dashPattern) { 
    var dashes = calculateDashedBezier(controlPoints, dashPattern); 
    ctx.beginPath(); 
    ctx.strokeStyle = /* ... */ 
    ctx.lineWidth = /* ... */ 
    pathParts(ctx, dashes); 
    ctx.stroke(); 
}; 

El principal problema con este enfoque es su granularidad poco inteligente. Cuando el paso es demasiado grande para los guiones (pequeños) o la (gran) curva, el tamaño del paso no funcionará bien y los límites del trazo no caerán exactamente donde usted desea. Cuando el paso es demasiado pequeño, puede terminar haciendo lineTo() s en puntos que están a una distancia de un píxel de distancia uno del otro, lo que hace que los artefactos de AA a veces. Filtrar las coordenadas de distancia subpíxel no es difícil, pero es ineficiente generar más 'vértices' de los que realmente necesitas. Venir con un mejor tamaño de paso es en realidad algo que consideraría atacar más analíticamente.

Hay una bonificación para usar este enfoque: si reemplaza bezier(controlPoints, t) con cualquier otra cosa que se evalúe como una curva, ¡estará dibujando elementos punteados! - nuevamente con los mismos problemas potenciales enumerados en el párrafo anterior. Pero una solución realmente buena para el problema de granularidad podría funcionar para todas las curvas de 'buen comportamiento'.

+1

Has identificado con razón que, presumir los pasos para el paso = 0.001 es un gran riesgo porque es posible que no sepas el tamaño de bezier en avanzado. Sería mejor si los pasos se calculan recursivamente encontrando el punto medio hasta que la distancia de dos puntos se vuelva cero o la curva se vuelva recta ... –

+0

Otra manera más fácil de hacerlo sería establecer 'step' igual a una aproximación de la longitud del arco de la curva bezier dividida por la longitud más pequeña del tablero. Aún así, un 'paso' fijo funcionará bien si sus curvas son tales que '1/paso 'es mucho mayor que la longitud del arco dividida por la longitud más pequeña del tablero. – ellisbben

+0

Ayudó, gracias ... –