Find equidistant points on ellipse or Beizier curv

2019-12-16 19:41发布

问题:

Currently I writing JavaScript code that will place objects on screen accross the ellipse.

I trying to find algorithm that will solve one of this problems, ellipse will be perfect, but if it is too expensive Beizier curve will be ok too.

I'm sorry, but unforunately my math doesn't let me to use answers that I found (https://mathoverflow.net/questions/28070/finding-n-points-that-are-equidistant-around-the-circumference-of-an-ellipse , Equidistant points across Bezier curves ), so I need help to translate it to code or just advice how to do this.

If you need visualisation of my question you can look at second page of this doc: http://www.saccade.com/writing/graphics/RE-PARAM.PDF

回答1:

Ok I retracted mine close vote your question is a slight different then those I linked

  • but as you can see not much :)

Have played with mine code a bit so here is the result for equidistant points

//---------------------------------------------------------------------------
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;
        }
    }
//---------------------------------------------------------------------------

It compose from the code from the second linked Q/A but anyway this is what it does

  1. k/kd loop loops the whole thing kd-times

    and in each it go a bit closer to the result by rescaling segment size l0,ll0. In the last pass it also draws the segments ... The more passes there are the more precision you get. With current overkill it can handle even rx=4.0*ry eccentric ellipses (or vice versa). For common ellipses is sufficient that kd=1,2 or 3

  2. i loop go through all segments

    first estimate step by the formula from linked Q/A and then use iterative fitting of segment size to l0 via most inner 'j' loop

  3. inner most j loop

    just step angle and see if segment is closer to wanted size if not change direction of step and its magnitude 10 times to increase accuracy the more recursion layers for j it is the more precise this gets

  4. at the end of k/kd loop

    the angle should be 2*PI so if it is more or less then rescale l0 accordingly but to avoid oscillations use also averaging with original l0 size

That is all