-1

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

Community
  • 1
  • 1
  • possible duplicate of [Algorithm for shape calculation (Ellipse)](http://stackoverflow.com/questions/19556544/algorithm-for-shape-calculation-ellipse) – Spektre Jan 10 '15 at 09:31
  • This problem is not solvable algebraicaly you can find just approximations. that duplicate question handle it by iteration which is precise enough but a bit slow. Also if you do not need very precise solution then look at this simple approximation http://stackoverflow.com/a/26779231/2521214 Now if you want to skip ellipse and use Bezier instead that is not a good idea because Bezier is even worse for this (only recursive iteration can solve it)... – Spektre Jan 10 '15 at 09:34
  • Thank you very much, sorry, but I didn't found that method on last search even doing it deeply. Thank you for Bezier curve explanation, it is very timely as far. P.S. I can't think that so simple problem have no simple solution, but as I said — I'm not very good at math. :) – AnswerSearcher Jan 10 '15 at 15:53
  • Spektre, I found ellipse shape code interesting, but absolutely can't understand you C++ code. Do you have some kind of working sources? Or can you translate it in JavaScript, of course if it not too difficult. – AnswerSearcher Jan 10 '15 at 16:59
  • Spektre, I've faced with problem using your approx algorithm with "[edit2] little precision boost" - http://s57.radikal.ru/i156/1501/47/d400792f82aa.png there is a picture of it. It's pic of 300 sectors. – AnswerSearcher Jan 10 '15 at 17:47
  • 1. if you cant understand the C++ code then read the algorithm description after the code block ... sorry but I do not code in JAVA at all 2. I think the easiest way for you is to use my bruteforce attack from the second link (I need an equation for equal movement along an ellipse). It is simple enough and as precise as you need. 3. the approx version is not precise that is why you have the hole in the points set of yours. The only problem with ellipse is that we do not know how to compute its perimeter (circumference) length algebraically that is why the more precise versions use fitting... – Spektre Jan 10 '15 at 20:23
  • I fix that hole by changing last y=y0+ry*Math.cos(a); to y=y0+ry*Math.sin(a); , is this ok (not typo)? – AnswerSearcher Jan 11 '15 at 09:21
  • in that case the for loop should just go 1 iteration more but that can be just coincidence try less number of segments if the hole is still 2 segments wide if yes then it is that if not then it is the precision issue and your fix is not working ... – Spektre Jan 11 '15 at 09:46
  • Less segments number didn't solve the problem, problem just shifts. Drawing are going from the last point to first, so it occurs with first iteration in for and last by number. One iteration more can't solve problem too, because of inverse order or other reason – AnswerSearcher Jan 11 '15 at 09:58
  • Then I was right and it is precision issue not a typo. There are more ways how to handle it 1. use brute force attack instead as I recommended before 2. get the error and scale the angle deltas to minimize it to zero 3. use more precise circumference approximation – Spektre Jan 11 '15 at 10:03
  • Spektre, I've realized brute force method, but still getting some glitch, http://s019.radikal.ru/i620/1501/04/231a785c171d.png It become even worse on bigger a/b ratios http://s020.radikal.ru/i704/1501/14/9623779f8e88.png – AnswerSearcher Jan 11 '15 at 10:54
  • That is to be expected because used formula for circumference `l` is less precise for higher eccentricities. so you can add scaling just compute the distance error your polygon should be (shorter longer) as `dl` and recompute the original `l` ... or use anothe brute force fitting ... like scale up scale down and see what is closer to what you want and recursively change the scale factor to higher precisions – Spektre Jan 11 '15 at 11:03

1 Answers1

0

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

ellipse result

That is all

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • @AnswerSearcher This is also dependent on the eccentricity so do not use too eccentric ellipses. I have a tought that your problem may be solvable by creating polygon from lines and compute the angles only to match ellipse but not I do not know if all combinations segment size vs. segment number vs. a,b will lead to ellipse instead may be some simmilar shape ... – Spektre Jan 11 '15 at 13:27
  • 1
    I've get up your code and do some mods to fit my task, I'll public it when it will be ready enough. I've changed calculation of the last point from formula to simply close the ellipse (compare with first point) and then it become ideal algorithm (in practise). Although - brute force not working good in this way. I don't know why, but only last approx algorithm working ideally for most cases (for more a/b ratios, and for absolutely all n>~10). – AnswerSearcher Jan 12 '15 at 14:38