2011-12-08 10 views
5

Quiero dibujar manchas curvas de aspecto aleatorio en un lienzo, pero parece que no se me ocurre un algoritmo para hacerlo. He intentado crear curvas de Bézier al azar como esto:formas curvas "aleatorias" de lienzo

context.beginPath(); 
// Each shape should be made up of between three and six curves 
var i = random(3, 6); 
var startPos = { 
    x : random(0, canvas.width), 
    y : random(0, canvas.height) 
}; 
context.moveTo(startPos.x, startPos.y); 
while (i--) { 
    angle = random(0, 360); 
    // each line shouldn't be too long 
    length = random(0, canvas.width/5); 
    endPos = getLineEndPoint(startPos, length, angle); 
    bezier1Angle = random(angle - 90, angle + 90) % 360; 
    bezier2Angle = (180 + random(angle - 90, angle + 90)) % 360; 
    bezier1Length = random(0, length/2); 
    bezier2Length = random(0, length/2); 
    bezier1Pos = getLineEndPoint(startPos, bezier1Length, bezier1Angle); 
    bezier2Pos = getLineEndPoint(endPos, bezier2Length, bezier2Angle); 
    context.bezierCurveTo(
     bezier1Pos.x, bezier1Pos.y 
     bezier2Pos.x, bezier2Pos.y 
     endPos.x, endPos.y 
    ); 
    startPos = endPos; 
} 

(Esta es una simplificación ... añadí trozos limitan a las líneas dentro del lienzo, etc.) El problema con esto se está poniendo a la cabeza volver al punto de partida, y no solo hacer montones de curvas incómodas. ¿Alguien sabe de un mejor algoritmo para hacer esto, o puede pensar en uno?

Editar: He hecho algunos progresos. Empecé de nuevo, trabajando con líneas rectas (creo que sé qué hacer para convertirlos en suaves Beziers una vez que he trabajado en este aspecto). Lo configuré para que antes de dibujar cada punto, resuelva la distancia y el ángulo al inicio desde el punto anterior. Si la distancia es menor que cierta cantidad, cierra la curva. De lo contrario, el ángulo posible se estrecha en función del número de iteraciones, y la longitud máxima de la línea es la distancia al inicio. Así que aquí hay un código.

start = { 
    // start somewhere within the canvas element 
    x: random(canvas.width), 
    y: random(canvas.height) 
}; 
context.moveTo(start.x, start.y); 
prev = {}; 
prev.length = random(minLineLength, maxLineLength); 
prev.angle = random(360); 
prev.x = start.x + prev.length * Math.cos(prev.angle); 
prev.y = start.y + prev.length * Math.sin(prev.angle); 

j = 1; 

keepGoing = true; 
while (keepGoing) { 
    j++; 
    distanceBackToStart = Math.round(
     Math.sqrt(Math.pow(prev.x - start.x, 2) + Math.pow(prev.y - start.y, 2))); 
    angleBackToStart = (Math.atan((prev.y - start.y)/(prev.x - start.x)) * 180/Math.pi) % 360; 
    if (isNaN(angleBackToStart)) { 
     angleBackToStart = random(360); 
    } 
    current = {}; 
    if (distanceBackToStart > minLineLength) { 
     current.length = random(minLineLength, distanceBackToStart); 
     current.angle = random(angleBackToStart - 90/j, angleBackToStart + 90/j) % 360; 
     current.x = prev.x + current.length * Math.cos(current.angle); 
     current.y = prev.y + current.length * Math.sin(current.angle); 
     prev = current; 
    } else { 
     // if there's only a short distance back to the start, join up the curve 
     current.length = distanceBackToStart; 
     current.angle = angleBackToStart; 
     current.x = start.x; 
     current.y = start.y; 
     keepGoing = false; 
    } 
    context.lineTo(current.x, current.y); 
} 
console.log('Shape complexity: ' + j); 
context.closePath(); 
context.fillStyle = 'black'; 
context.shadowColor = 'black'; 
context.shadowOffsetX = -xOffset; 
context.shadowOffsetY = -yOffset; 
context.shadowBlur = 50; 
context.fill(); 

El problema que tengo ahora es que el contorno de la forma a menudo se cruza consigo mismo, lo que parece estar mal. La única forma en que puedo pensar para resolver esto es hacer un seguimiento de un cuadro delimitador, y cada nuevo punto siempre debe salir del cuadro delimitador. Eso es complicado porque calcular el ángulo disponible agrega un nivel completo de complejidad.

+0

Debería publicar una página HTML completa, con JavaScript en línea. – Fantius

+1

http://stackoverflow.com/questions/2956967/drawing-path-by-bezier-curves – Fantius

+0

Aquí está un jsfiddle con su código: http://jsfiddle.net/EnZX4/ –

Respuesta

3

Una posibilidad sería utilizar coordenadas polares, y hacer que el radio sea una función del ángulo. Para manchas lisas desea que la radio sea suave, y tienen el mismo valor en 0 y 2 * pi, lo que puede hacerse utilizando un polinomio trigonométrico:

radius(theta) = a_0 + a_1*sin(theta) + a_2*sin(2*theta) + ... + b_1*cos(theta) + ... 

donde los coeficientes son "al azar". Para controlar qué tan grande y pequeño es el radio, puede buscar el máximo y el mínimo de la función de radio, y luego cambiar y escalar los coeficientes de manera apropiada (es decir, si quiere rlo < = r < = rhi, y ha encontrado min y max, luego reemplace cada coeficiente a + b * original, donde b = (rhi-rlo)/(max-min) y a = rlo-b * min).

+0

¡Es una idea increíble! Tendré una jugada y luego veré si puedo hacer algo que funcione. –