javascript - 在网络中绘制斐波那契数错误

标签 javascript math geometry 2d computational-geometry

目前我有来自 Blindman67 的 fiddle,它绘制了 Golden spiral 图 1(见下图)。

function renderSpiral(pointA, pointB, turns){
var dx, dy, rad, i, ang, cx, cy, dist, a, c, angleStep, numberTurns, nTFPB, scale, styles;
// clear the canvas
ctx.clearRect(0, 0, ctx.canvas.width,ctx.canvas.height)

// spiral stuff
a = 1;         // the larger this number the larger the spiral
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 = 2;   //  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);
// 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 requiered.
scale = dist / rad;


// ajust the number of turns so that the spiral fills the canvas
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


// 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 requiered
// ready to draw                               
ctx.beginPath();
ctx.moveTo(0, 0)        // start at center
ctx.lineTo((pointB.x-pointA.x)*(1/scale),(pointB.y-pointA.y)*(1/scale) );  // add line
ctx.stroke();  // draw it all

// Now draw the sporal. 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 requiered
    // 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(0, 0)        // start at center
        }else{
            ctx.lineTo(dx * rad, dy * rad );  // add line
        }
    }
    ctx.stroke();  // draw it all
});
ctx.setTransform(1,0,0,1,0,0); // reset tranfrom to default;
}

我想要获得的是图2(见下图)。

enter image description here

Q1。 如何更改我的螺旋线,以便线 AB 适合第一个和第二个螺丝之间,而 A 是螺旋线的起点?

您还可以引用我之前的 question 以更好地理解我的问题。

最佳答案

要实现您需要的属性,您需要调整螺旋,如下所示:

  1. 选择线的右 Angular 位置AB

    我为 A 选择 1.5*M_PI [rad],为 B 选择 3.5*M_PI [rad] (在未旋转的螺旋上)

  2. AB 线的 Angular 旋转螺旋

    这很简单,只需将 Angular 添加到最终的极 -> 笛卡尔坐标转换中,这将旋转整个螺旋,因此计算出的 A,B 在螺旋上的 Angular 位置将匹配真实点AB方向

  3. 重新缩放螺旋线以匹配 AB 尺寸

    因此,计算螺旋上 Angular 点 AB 位置的半径,然后计算 scale=|AB|-(r(b)-r (a))。现在只需将其相乘即可计算每个渲染点的半径...

我玩了一下黄金比例和螺旋线,这就是结果

golden spiral

  • 黄色螺旋线是四分之一圆弧的近似值
  • Aqua 是黄金螺旋

如您所见,它们不太匹配(这是使用 ratio*0.75 使它们更相似,但它应该只是 ratio)某处出现错误,或者螺旋的原点发生了偏移(但看起来不像),或者我有错误的比率常数ratio = 0.3063489,或者黄金矩形引入了比我教或我更高的 float 圆误差错过了一些愚蠢的事情。

这里是 C++ 源代码,以便您可以提取所需的内容:

//---------------------------------------------------------------------------
#include <Math.h>
//---------------------------------------------------------------------------
bool _redraw=false;                     // just signal to repaint window after spiral change
double Ax,Ay,Bx,By;                     // mouse eddited points
double gr=0.75;                         // golden spiral ratio scale should be 1 !!!

void GoldenSpiral_draw(TCanvas *can)    // GDI draw
    {
    double a0,a,b,l,x,y,r=5,ratio;

    // draw AB line
    can->Pen->Color=clWhite;
    can->MoveTo(Ax,Ay);
    can->LineTo(Bx,By);
    // draw A,B points
    can->Pen->Color=clBlue;
    can->Brush->Color=clAqua;
    can->Ellipse(Ax-r,Ay-r,Ax+r,Ay+r);
    can->Ellipse(Bx-r,By-r,Bx+r,By+r);
    // draw golden ratio rectangles
    can->Pen->Color=clDkGray;
    can->Brush->Style=bsClear;
    ratio=1.6180339887;
    a=5.0; b=a/ratio; x=Ax; y=Ay;
    y-=0.5*b; x-=0.5*b; // bias to match real golden spiral
    can->Rectangle(x,y,x+a,y+b); y-=a;
    for (int i=0;i<5;i++)
        {
        can->Rectangle(x,y,x+a,y+a);             b=a; a*=ratio; x-=a;
        can->Rectangle(x,y,x+a,y+a); y+=a;       b=a; a*=ratio;
        can->Rectangle(x,y,x+a,y+a); x+=a; y-=b; b=a; a*=ratio;
        can->Rectangle(x,y,x+a,y+a); x-=b;       b=a; a*=ratio; y-=a;
        }
    // draw circle arc approximation of golden spiral
    ratio=1.6180339887;
    a=5.0; b=a/ratio; x=Ax; y=Ay; r=10000; y-=a;
    y-=0.5*b; x-=0.5*b; // bias to match real golden spiral
    can->Pen->Color=clYellow;
    for (int i=0;i<5;i++)
        {
        can->Arc(x-a,y,x+a,y+a+a,+r, 0, 0,-r);             b=a; a*=ratio; x-=a;
        can->Arc(x,y,x+a+a,y+a+a, 0,-r,-r, 0); y+=a;       b=a; a*=ratio;
        can->Arc(x,y-a,x+a+a,y+a,-r, 0, 0,+r); x+=a; y-=b; b=a; a*=ratio;
        can->Arc(x-a,y-a,x+a,y+a, 0,+r,+r, 0); x-=b;       b=a; a*=ratio; y-=a;
        }
    can->Brush->Style=bsSolid;

    // compute golden spiral parameters
    ratio=0.3063489*gr;
    x=Bx-Ax;
    y=By-Ay;
    l=sqrt(x*x+y*y);    // l=|AB|
    if (l<1.0) return;  // prevent domain errors
    a0=atan2(-y,x);     // a=atan2(AB)
    a0+=0.5*M_PI;       // offset so direction of AB matches the normal
    a=1.5*M_PI; r=a*exp(ratio*a); b=r;
    a+=2.0*M_PI; r=a*exp(ratio*a); b=r-b;
    b=l/r;              // b=zoom of spiral to match AB screw distance
    // draw golden spiral
    can->Pen->Color=clAqua;
    can->MoveTo(Ax,Ay);
    for (a=0.0;a<100.0*M_PI;a+=0.001)
        {
        r=a*b*exp(ratio*a); if (r>512.0) break;
        x=Ax+r*cos(a0+a);
        y=Ay-r*sin(a0+a);
        can->LineTo(x,y);
        }
    }
//---------------------------------------------------------------------------
  • 你可以忽略黄金比例矩形和圆弧...
  • 将基于 can-> 的绘图更改为您的 gfx API。它只是 GDI Canvas

很难说你的螺旋是否正确......你可以用黄金比例矩形检查(就像我所做的那样)。如果你得到了正确的螺旋,那么只需将子弹 #1,#2,#3 应用于它就可以了。

关于javascript - 在网络中绘制斐波那契数错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33274795/

相关文章:

Javascript:变量范围和使用顺序

Python:使用列表中的运算符进行数学运算

java - 找出所有总和为 n 的自然数对中能量最小的一对自然数

java - 如何根据 X,Y 的集合确定多边形的周长

javascript - Jquery 排队动画

javascript - Angular Directive(指令)的路由问题

javascript - Angular.js 服务函数未定义

c++ - 如果我有一条从点 x1、y1 到 x2、y2 的线,我怎样才能从点 x2、y2 以 45 度角绘制 2 条线来制作箭头?

javascript - 如何在移动网络上使用 CSS、Javascript 创建半圆菜单(子项)?

javascript - 创建点、弹出窗口、圆、转换坐标问题(Arcgis JS 4.6 API)