seccion rectangulo proporcion perfeccion hacer espiral dibujar como aureo aurea javascript jquery html5 math

javascript - rectangulo - Dibujo en espiral de Fibonacci en lienzo HTML5



proporcion aurea (2)

Actualmente estoy viendo este código pero no puedo descubrir qué está mal.

function fibNumbers() { return [0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] } function continiusFib(a) { var b = fibNumbers(), c = Math.floor(a), d = Math.ceil(a); if (d >= b.length) return null; a = Math.pow(a - c, 1.15); return b[c] + (b[d] - b[c]) * a } function drawSpiral(pointA, pointB) { var b = pointA; var c = pointB; ctx.translate(b.x, b.y); b = Math.sqrt(((c.x - b.x) * (c.x - b.x)) + ((c.y - b.y) * (c.y - b.y))); d = 1 / Math.sqrt(((c.x - b.x) * (c.x - b.x)) + ((c.y - b.y) * (c.y - b.y))); c = Math.acos(c.x - b.x); 0 > Math.asin(c.y - b.y) && (c = 2 * Math.PI - c); ctx.rotate(c); ctx.scale(b / 5, b / 5); var d = Math.PI / 100; ctx.moveTo(0, 0); for (var e = 0; e < 50 * (fibNumbers().length - 1) ; e++) { var f = e * d, g = continiusFib(e / 50), h = Math.cos(f) * g, f = Math.sin(f) * g; ctx.lineTo(h, f); } ctx.scale(5 / b, 5 / b); ctx.rotate(-c); //ctx.stroke(); }

Lo que quiero es dibujar Fibonacci Spiral, que es diferente de Golden Spiral

También tengo esta pregunta para otra referencia.


Así es como lo hice. Lo que hay que hacer es encontrar el radio de la espiral en el ángulo desde el punto A hasta el B y luego escalar la espiral para ajustarla.

La función representa la espiral en el lienzo centrado en el punto A y el punto de intersección B. Utiliza ctx.setTransform para posicionar la espiral para que se ajuste a las restricciones o puede usar la escala y las compensaciones centrales para transformar los puntos sirales y mantener la transformación de lienzo predeterminada (en caso de que esté dibujando otras cosas);

Advertencias

  • No dibuja si pointB === pointA ya que no hay solución.
  • Puede no dibujar si el punto A está muy lejos del lienzo (no lo he probado).
  • Siempre se dibuja desde el centro hacia afuera. No considera el recorte de la espiral más que dónde detenerse.

Entonces para el código. (Actualizado)

// Assume ctx is canvas 2D Context and ready to render to var cx = ctx.canvas.width / 2; var cy = ctx.canvas.height / 2; var font = "Verdana"; // font for annotation var fontSize = 12; // font size for annotation var angleWind = 0; var lastAng; function getScale(){ // gets the current transform scale // assumes transform is square. ie Y and X scale are equal and at right angles var a = ctx.currentTransform.a; // get x vector from current trans var b = ctx.currentTransform.b; return Math.sqrt(a * a + b * b); // work out the scale } // Code is just a quicky to annotate line and aid visualising current problem // Not meant for anything but this example. Only Tested on Chrome // This is needed as the canvas text API can not handle text at very small scales // so need to draw at unit scale over existing transformation function annotateLine(pA, pB, text, colour, where){ var scale, size, ang, xdx, xdy, len, textStart, ox, oy; scale = getScale(); // get the current scale size = fontSize; // get font size // use scale to create new origin at start of line ox = ctx.currentTransform.e + pA.x * scale ; oy = ctx.currentTransform.f + pA.y * scale; // get direction of the line ang = Math.atan2(pB.y - pA.y, pB.x - pA.x); xdx = Math.cos(ang); // get the new x vector for transform xdy = Math.sin(ang); // get the length of the new line to do annotation positioning len = Math.sqrt( Math.pow(pB.y - pA.y, 2) + Math.pow(pB.x - pA.x, 2) ) * scale; ctx.save(); // save current state //Set the unit scaled transform to render in ctx.setTransform(xdx, xdy, -xdy, xdx, ox, oy); // set fint ctx.font= size + "px " + font; // set start pos textStart = 0; where = where.toLowerCase(); // Because I can never get the cap right if(where.indexOf("start") > -1){ textStart = 0; // redundent I know but done }else if(where.indexOf("center") > -1 || where.indexOf("centre") > -1 ){ // both spellings // get the size of text and calculate where it should start to be centred textStart = (len - ctx.measureText(text).width) / 2; }else{ textStart = (len - ctx.measureText(text).width); } if(where.indexOf("below") > -1){ // check if below size = -size * 2; } // draw the text ctx.fillStyle = colour; ctx.fillText(text, textStart,-size / 2); ctx.restore(); // recall saved state } // Just draws a circle and should be self exlainatory function circle(pA, size, colour1, colour2){ size = size * 1 / getScale(); ctx.strokeStyle = colour1; ctx.fillStyle = colour2; ctx.beginPath(); ctx.arc(pA.x, pA.y, size , 0, Math.PI * 2); ctx.fill(); ctx.stroke(); } function renderSpiral(pointA, pointB, turns){ var dx, dy, rad, i, ang, cx, cy, dist, a, c, angleStep, numberTurns, nTFPB, scale, styles, pA, pB; // clear the canvas ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // spiral stuff c = 1.358456; // constant See https://en.wikipedia.org/wiki/Golden_spiral angleStep = Math.PI/20; // set the angular resultion for drawing numberTurns = 6; // total half turns drawn nTFPB = 0; // numberOfTurnsForPointB is the number of turns to point // B should be integer and describes the number off // turns made befor reaching point B // get the ang from pointA to B ang = (Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) + Math.PI * 2) % (Math.PI *2 ); // Check for winding. If the angle crosses 2PI boundary from last call // then wind up or wind down the number of turns made to get to current // solution. if(lastAng !== undefined){ if(lastAng > Math.PI * 1.5 && ang < Math.PI * 0.5 ){ angleWind += 1; }else if(lastAng < Math.PI * 0.5 && ang > Math.PI * 1.5 ){ if(angleWind > 0){ angleWind -= 1; } } } lastAng = ang; // save last angle // Add the windings nTFPB += angleWind; // get the distance from A to B dist = Math.sqrt(Math.pow(pointB.y-pointA.y,2)+Math.pow((pointB.x)-pointA.x,2)); if(dist === 0){ return; // this makes no sense so exit as nothing to draw } // get the spiral radius at point B rad = Math.pow(c,ang + nTFPB * 2 * Math.PI); // spiral radius at point2 // now just need to get the correct scale so the spiral fist to the // constraints required. scale = dist / rad; while(Math.pow(c,Math.PI*numberTurns)*scale < ctx.canvas.width){ numberTurns += 2; } // set the scale, and origin to centre ctx.setTransform(scale, 0, 0, scale, pointA.x, pointA.y); // make it look nice create some line styles styles = [{ colour:"black", width:6 },{ colour:"gold", width:5 } ]; // Now draw the spiral. draw it for each style styles.forEach( function(style) { ctx.strokeStyle = style.colour; ctx.lineWidth = style.width * ( 1 / scale); // because it is scaled invert the scale // can calculate the width required // ready to draw ctx.beginPath(); for( i = 0; i <= Math.PI *numberTurns; i+= angleStep){ dx = Math.cos(i); // get the vector for angle i dy = Math.sin(i); var rad = Math.pow(c, i); // calculate the radius if(i === 0) { ctx.moveTo(dx * rad , dy * rad ); // start at center }else{ ctx.lineTo(dx * rad , dy * rad ); // add line } } ctx.stroke(); // draw it all }); // first just draw the line A-B ctx.strokeStyle = "black"; ctx.lineWidth = 2 * ( 1 / scale); // because it is scaled invert the scale // can calculate the width required // some code to help me work this out. Having hard time visualising solution pA = {x: 0, y: 0}; pB = {x: 1, y: 0}; pB.x = ( pointB.x - pointA.x ) * ( 1 / scale ); pB.y = ( pointB.y - pointA.y ) * ( 1 / scale ); // ready to draw ctx.beginPath(); ctx.moveTo( pA.x, pA.y ); // start at center ctx.lineTo( pB.x, pB.y ); // add line ctx.stroke(); // draw it all if(scale > 10){ ctx.strokeStyle = "blue"; ctx.lineWidth = 1 * ( 1 / scale); ctx.beginPath(); ctx.moveTo( 0, 0 ); // start at center ctx.lineTo( 1, 0 ); // add line ctx.stroke(); // draw it all } annotateLine(pA, pB, "" + ((ang + angleWind * Math.PI * 2) / Math.PI).toFixed(2) + "π", "black", "centre"); annotateLine(pA, pB, "" + rad.toFixed(2), "black", "centre below"); if(scale > 10){ annotateLine({x: 0, y: 0}, {x: 1, y: 0}, "1 Unit", "blue", "centre"); } circle(pA, 5, "black", "white"); circle(pB, 5, "black", "white"); ctx.setTransform(1,0,0,1,0,0); // reset transform to default; } var centerMove = 0; canvasMouseCallBack = function(){ centerMove += 0.0; renderSpiral( { x:cx+Math.sin(centerMove)*100, y:cy+Math.cos(centerMove)*100 }, {x:mouse.x,y:mouse.y} ); };

Espero que esto ayude. Perdón por la fruta extra pero tuve que probarla, así que solo copiaría todo como la respuesta.

He añadido un violín para aquellos que quieren verlo funcionando. PointA be se mueve automáticamente (por lo que se ve un poco extraño al mover el mouse) ya que no me molestó en agregar una interfaz adecuada.

ACTUALIZACIÓN: He actualizado la respuesta y el violín tratando de encontrar una mejor solución para la pregunta actualizada. Desafortunadamente no pude cumplir con los nuevos requisitos, aunque de mi análisis me parece que los requisitos presentan un problema sin solución. A saber, como el ángulo de la espiral se acerca a cero, la escala (en la solución) se acerca al infinito, la asíntota está en algún lugar cerca de PI / 4, pero como esto es solo una aproximación, todo pierde sentido. Hay un conjunto de ubicaciones para los puntos A y B donde no se puede instalar la espiral. Esta es mi interpretación y no significa que no haya solución, ya que no he presentado ninguna prueba.

Fiddle (actualizado)


En tu función drawSpiral , en la cuarta línea haces:

b = Math.sqrt(((c.x - b.x) * (c.x - b.x)) + ((c.y - b.y) * (c.y - b.y)));

Entonces, b debería ser un escalar ahora, pero luego intenta acceder a bx y by en la línea siguiente, que ya no existe:

d = 1 / Math.sqrt(((c.x - b.x) * (c.x - b.x)) + ((c.y - b.y) * (c.y - b.y)));

Esto ocurre nuevamente con c en las líneas 6-7a. Esta podría ser la razón por la cual su código no está funcionando.

Traté de hacerlo funcionar con mi propio código. No estoy seguro en absoluto sobre las matemáticas, pero basé mi algoritmo en el fragmento que publicaste sobre la pregunta, usando parte del código de seguimiento del mouse de la respuesta de @ Blindman67.

La espiral

Esta es la parte importante. Devuelve una matriz con los puntos de la espiral (uso otra función para renderizarlos). La idea es dibujar una espiral usando la función continua de fibonacci que proporcionó. Comienza en el punto A y fuerza el escalado para que el radio en un giro sea la distancia entre el punto A y el punto B. También agrega un desplazamiento del ángulo, por lo que el ángulo en un giro es el ángulo entre los puntos A y B.

Editado para abordar el comentario: Cambié el ciclo for por un ciclo while que continúa dibujando hasta que la espiral alcanza un radio máximo. También cambié algunos nombres y agregué comentarios para tratar de aclarar el algoritmo.

var getSpiral = function(pA, pB, maxRadius){ // 1 step = 1/4 turn or 90º var precision = 50; // Lines to draw in each 1/4 turn var stepB = 4; // Steps to get to point B var angleToPointB = getAngle(pA,pB); // Angle between pA and pB var distToPointB = getDistance(pA,pB); // Distance between pA and pB var fibonacci = new FibonacciGenerator(); // Find scale so that the last point of the curve is at distance to pB var radiusB = fibonacci.getNumber(stepB); var scale = distToPointB / radiusB; // Find angle offset so that last point of the curve is at angle to pB var angleOffset = angleToPointB - stepB * Math.PI / 2; var path = []; var i, step , radius, angle; // Start at the center i = step = radius = angle = 0; // Continue drawing until reaching maximum radius while (radius * scale <= maxRadius){ path.push({ x: scale * radius * Math.cos(angle + angleOffset) + pA.x, y: scale * radius * Math.sin(angle + angleOffset) + pA.y }); i++; // Next point step = i / precision; // 1/4 turns at point radius = fibonacci.getNumber(step); // Radius of Fibonacci spiral angle = step * Math.PI / 2; // Radians at point } return path; };

secuencia Fibonacci

El código para generar los números continuos de fibonacci es básicamente tuyo, pero cambié algunos nombres para ayudarme a entenderlo. También agregué una función de generador para que funcione con cualquier número:

var FibonacciGenerator = function(){ var thisFibonacci = this; // Start with 0 1 2... instead of the real sequence 0 1 1 2... thisFibonacci.array = [0, 1, 2]; thisFibonacci.getDiscrete = function(n){ // If the Fibonacci number is not in the array, calculate it while (n >= thisFibonacci.array.length){ var length = thisFibonacci.array.length; var nextFibonacci = thisFibonacci.array[length - 1] + thisFibonacci.array[length - 2]; thisFibonacci.array.push(nextFibonacci); } return thisFibonacci.array[n]; }; thisFibonacci.getNumber = function(n){ var floor = Math.floor(n); var ceil = Math.ceil(n); if (Math.floor(n) == n){ return thisFibonacci.getDiscrete(n); } var a = Math.pow(n - floor, 1.15); var fibFloor = thisFibonacci.getDiscrete(floor); var fibCeil = thisFibonacci.getDiscrete(ceil); return fibFloor + a * (fibCeil - fibFloor); }; return thisFibonacci; };

Distancia y ángulo entre puntos

Para hacer que el código sea más claro, utilicé un par de funciones auxiliares para trabajar con puntos 2D:

var getDistance = function(p1, p2){ return Math.sqrt(Math.pow(p1.x-p2.x, 2) + Math.pow(p1.y-p2.y, 2)); }; var getAngle = function(p1, p2){ return Math.atan2(p2.y-p1.y, p2.x-p1.x); };

Todo el asunto: JSFiddle y Updated-to-address-comment JSFiddle