在椭圆或贝塞尔曲线上找到等距点

时间:2015-01-10 09:26:35

标签: javascript math geometry 2d

目前我正在编写JavaScript代码,将对象放在椭圆上的屏幕上。

我试图找到解决其中一个问题的算法,椭圆将是完美的,但如果太昂贵,Beizier曲线也可以。

对不起,但不幸的是我的数学不允许我使用我找到的答案(https://mathoverflow.net/questions/28070/finding-n-points-that-are-equidistant-around-the-circumference-of-an-ellipseEquidistant points across Bezier curves),所以我需要帮助才能将其翻译成代码或者只是建议如何这样做。

如果您需要查看我的问题,可以查看此文档的第二页: http://www.saccade.com/writing/graphics/RE-PARAM.PDF

1 个答案:

答案 0 :(得分:0)

好的,我撤回了我的近距离投票你的问题与我链接的那些有点不同

  • 但正如你所看不到的那样:)

已经使用了我的代码,所以这里是等距点的结果

//---------------------------------------------------------------------------
void draw()
    {
    TCanvas *scr=Form1->Canvas;
    //if (scr->FHandle==NULL) return;
    scr->Brush->Color=clWhite;
    scr->Rectangle(0,0,Form1->ClientWidth,Form1->ClientHeight);

    double x0,y0,rx,ry,n,l0,ll0;
    x0=Form1->ClientWidth>>1;   // ellipse position (midle of form)
    y0=Form1->ClientHeight>>1;
    rx=200;                     // ellipse a
    ry=50;                      // ellipse b
    n=33.0;                     // segments
    //l0=2.0*M_PI*sqrt(0.5*((rx*rx)+(ry*ry)));
    l0=(rx-ry)/(rx+ry); l0*=3.0*l0; l0=M_PI*(rx+ry)*(1.0+(l0/(10.0+sqrt(4.0-l0))));
    // compute segment size
    l0/=n; ll0=l0*l0;

    int i,j,k,kd;
    AnsiString s;
    double a,da,x,y,xx,yy,ll,mm,r=2.0;

    for (kd=10,k=0;;k++)    // kd+1 passes
        {
        a=0.0; if (!k) da=0.0;
        xx=rx*sin(a+0.5*da);
        yy=ry*cos(a+0.5*da);
        da=l0/sqrt((xx*xx)+(yy*yy));
        x=x0+rx*cos(a);
        y=y0+ry*sin(a);
        if (k==kd) // draw in last pass only
            {
            scr->Pen->Color=clRed;
            scr->MoveTo(x ,y );
            scr->LineTo(x0,y0);
            scr->Ellipse(x-r,y-r,x+r,y+r);
            scr->Pen->Color=clBlue;
            scr->Font->Color=clBlue;
            }
        for (i=n;i>0;i--)
            {
            // approximate angular step to match l0 (as start point for fitting)
            xx=rx*sin(a+0.5*da);
            yy=ry*cos(a+0.5*da);
            da=l0/sqrt((xx*xx)+(yy*yy));
            // next point position
            xx=x; yy=y; a+=da;
            x=x0+rx*cos(a);
            y=y0+ry*sin(a);

            // fit it to be really l0
            ll=((xx-x)*(xx-x))+((yy-y)*(yy-y)); ll=fabs(ll0-ll);
            for (da*=0.1,a-=da,j=0;j<5;) // accuracy recursion layers
                {
                a+=da;
                x=x0+rx*cos(a);
                y=y0+ry*sin(a);
                mm=((xx-x)*(xx-x))+((yy-y)*(yy-y)); mm=fabs(ll0-mm);
                if (mm>ll) { a-=da; da=-0.1*da; j++; } else ll=mm;  // if acuracy stop lovering change direction
                }
            x=x0+rx*cos(a);
            y=y0+ry*sin(a);

            if (k==kd) // draw in last pass only
                {
                // draw the lines and dots
                scr->MoveTo(xx,yy);
                scr->LineTo(x ,y );
                scr->Ellipse(x-r,y-r,x+r,y+r);
                // print the difference^2
                ll=((xx-x)*(xx-x))+((yy-y)*(yy-y));
                s=AnsiString().sprintf("%.2lf",ll0-ll);
                xx=0.5*(x+xx)+20.0*cos(a)-0.5*double(scr->TextWidth(s));
                yy=0.5*(y+yy)+20.0*sin(a)-0.5*double(scr->TextHeight(s));
                scr->TextOutA(xx,yy,s);
                }
            }
        if (k==kd)
            {
            scr->MoveTo(x ,y );
            scr->LineTo(x0,y0);
            s=AnsiString().sprintf("%.4lf",2.0*M_PI-a);
            xx=x+60.0*cos(a)-0.5*double(scr->TextWidth(s));
            yy=y+60.0*sin(a)-0.5*double(scr->TextHeight(s));
            scr->TextOutA(xx,yy,s);
            break;
            }
        // rescale l0
        a=2.0*M_PI/a;       // a should be 2*PI if no error -> 1.0
        l0*=0.5+0.5*a;      // just iterate
        ll0=l0*l0;
        }
    }
//---------------------------------------------------------------------------

它来自第二个链接 Q / A 的代码,但无论如何这都是它的作用

  1. k/kd循环整个事件kd - 次

    并且通过重新调整段大小l0,ll0,它们更接近结果。在最后一次传球中,它也会绘制分段......传球次数越多,获得的精度就越高。使用当前的过度杀伤,它甚至可以处理rx=4.0*ry偏心椭圆(反之亦然)。对于常见的省略号,kd=1,2 or 3

  2. 就足够了
  3. i循环遍历所有细分

    首先通过链接 Q / A 的公式估算步骤,然后通过大多数内部&#39; j使用段大小到l0的迭代拟合。环

  4. 内部j循环

    只是步进角,看看段是否更接近所需大小,如果不改变步长方向及其大小10次以提高准确度,j的递归层越多,它就越精确

  5. k/kd循环

    结束时
  6. 角度应为2*PI,因此如果它或多或少,则相应地重新缩放l0,但要避免振荡,请使用原始l0尺寸的平均值

  7. ellipse result

    这就是全部