Question

I am working on the problem of dividing an ellipse into equal sized segments. This question has been asked but the answers suggested numerical integration so that I what I'm attempting. This code short-circuits the sectors so the integration itself should never cover more than 90 degrees. The integration itself is being done by totaling the area of intermediate triangles. Below is the code I have tried, but it is sweeping more than 90 degrees in some cases.

public class EllipseModel {

    protected double r_x;
    protected double r_y;

    private double a,a2;
    private double b,b2;

    boolean flip;

    double area;
    double sector_area;

    double radstep;
    double rot;

    int xp,yp;

    double deviation;

    public EllipseModel(double r_x, double r_y, double deviation) 
    {
        this.r_x = r_x;
        this.r_y = r_y;
        this.deviation = deviation;

        if (r_x < r_y) {
            flip = true;
            a = r_y;
            b = r_x;
            xp = 1;
            yp = 0;
            rot = Math.PI/2d;
        } else {
            flip = false;
            xp = 0;
            yp = 1;
            a = r_x;
            b = r_y;
            rot = 0d;
        }

        a2 = a * a;
        b2 = b * b;

        area = Math.PI * r_x * r_y;
        sector_area = area / 4d;

        radstep = (2d * deviation) / a; 
    }

    public double getArea() {
        return area;
    }

    public double[] getSweep(double sweep_area) 
    {
        System.out.println(String.format("getSweep(%f) a = %f b = %f deviation = %f",sweep_area,a,b,deviation));
        double[] ret = new double[2];
        double[] next = new double[2];
        double t_base, t_height, swept,x_mid,y_mid;
        double t_area;

        sweep_area = sweep_area % area;

        if (sweep_area < 0d) {
            sweep_area = area + sweep_area;
        }

        if (sweep_area == 0d) {
            ret[0] = r_x;
            ret[1] = 0d;
            return ret;
        }

        double sector = Math.floor(sweep_area/sector_area);
        double theta = Math.PI * sector/2d;
        double theta_last = theta;

        System.out.println(String.format("- Theta start = %f",Math.toDegrees(theta)));

        ret[xp] = a * Math.cos(theta + rot);
        ret[yp] = (1 + (((theta / Math.PI) % 2d) * -2d)) * Math.sqrt((1 - ( (ret[xp] * ret[xp])/a2)) * b2);

        next[0] = ret[0];
        next[1] = ret[1];

        swept = sector * sector_area;

        System.out.println(String.format("- Sweeping for %f sector_area=%f",sweep_area-swept,sector_area));

        int c = 0;

        while(swept < sweep_area) {

            c++;
            ret[0] = next[0];
            ret[1] = next[1];

            theta_last = theta;
            theta += radstep;

            // calculate next point
            next[xp] = a * Math.cos(theta + rot);
            next[yp] = (1 + (((theta / Math.PI) % 2d) * -2d)) * // selects +/- sqrt
                        Math.sqrt((1 - ( (ret[xp] * ret[xp])/a2)) * b2);

            // calculate midpoint
            x_mid = (ret[xp] + next[xp]) / 2d;
            y_mid = (ret[yp] + next[yp]) / 2d;

            // calculate triangle metrics
            t_base = Math.sqrt( ( (ret[0] - next[0]) * (ret[0] - next[0]) ) + ( (ret[1] - next[1]) * (ret[1] - next[1])));
            t_height = Math.sqrt((x_mid * x_mid) + (y_mid * y_mid));

            // add triangle area to swept           
            t_area = 0.5d * t_base * t_height;
            swept += t_area;

        }

        System.out.println(String.format("- Theta end = %f (%d)",Math.toDegrees(theta_last),c));

        return ret;
    }
}

In the output I see the following case where it sweeps over 116 degrees.

getSweep(40840.704497) a = 325.000000 b = 200.000000 deviation = 0.166667
- Theta start = 0.000000
- Sweeping for 40840.704497 sector_area=51050.880621
- Theta end = 116.354506 (1981)

Is there any way to fix the integration formula to create a function that returns the point on an ellipse that has swept a given area? The application that is using this code divides the total area by the number of segments needed, and then uses this code to determine the angle where each segment starts and ends. Unfortunately it doesn't work as intended.

* edit * I believe the above integration failed because the base and height formula's aren't correct.

Was it helpful?

Solution

No transformation needed use parametric equations for ellipse ...

x=x0+rx*cos(a)
y=y0+ry*sin(a)

where a = < 0 , 2.0*M_PI >

  • if you divide ellipse by lines from center to x,y from above equation
  • and angle a is evenly encreased
  • then the segments will have the same size

btw. if you apply affine transform you will get the same result (even the same equation)

This code will divide ellipse to evenly sized chunks:

double a,da,x,y,x0=0,y0=0,rx=50,ry=20;  // ellipse x0,y0,rx,ry
int i,N=32;                                // divided to N = segments
da=2.0*M_PI/double(N);

for (a=0.0,i=0;i<N;i++,a+=da)
    {
    x=x0+(rx*cos(a));
    y=y0+(ry*sin(a));
    // draw_line(x0,y0,x,y);
    }

This is what it looks like for N=5

ellipse division

[edit1]

I do not understood from your comment what exactly you want to achieve now

  • sorry but my English skills are horrible
  • ok I assume these two possibilities (if you need something different please specify closer)

0.but first some global or member stuff needed

double x0,y0,rx,ry; // ellipse parameters

// [Edit2] sorry forgot to add these constants but they are I thin straight forward
const double pi=M_PI;
const double pi2=2.0*M_PI;
// [/Edit2]

double atanxy(double x,double y) // atan2 return < 0 , 2.0*M_PI >
        {
        int sx,sy;
        double a;
        const double _zero=1.0e-30;
        sx=0; if (x<-_zero) sx=-1; if (x>+_zero) sx=+1;
        sy=0; if (y<-_zero) sy=-1; if (y>+_zero) sy=+1;
        if ((sy==0)&&(sx==0)) return 0;
        if ((sx==0)&&(sy> 0)) return 0.5*pi;
        if ((sx==0)&&(sy< 0)) return 1.5*pi;
        if ((sy==0)&&(sx> 0)) return 0;
        if ((sy==0)&&(sx< 0)) return pi;
        a=y/x; if (a<0) a=-a;
        a=atan(a);
        if ((x>0)&&(y>0)) a=a;
        if ((x<0)&&(y>0)) a=pi-a;
        if ((x<0)&&(y<0)) a=pi+a;
        if ((x>0)&&(y<0)) a=pi2-a;
        return a;
        }

1.is point inside segment ?

bool is_pnt_in_segment(double x,double y,int segment,int segments) 
 {
 double a;
 a=atanxy(x-x0,y-y0); // get sweep angle
 a/=2.0*M_PI; // convert angle to a = <0,1>
 if (a>=1.0) a=0.0; // handle extreme case where a was = 2 Pi
 a*=segments; // convert to segment index a = <0,segments)
 a-=double(segment );
 // return floor(a); // this is how to change this function to return points segment id
 // of course header should be slightly different: int get_pnt_segment_id(double x,double y,int segments) 
 if (a< 0.0) return false; // is lower then segment
 if (a>=1.0) return false; // is higher then segment
 return true;
 }

2.get edge point of segment area

void get_edge_pnt(double &x,double &y,int segment,int segments)
 {
 double a;
 a=2.0*M_PI/double(segments);
 a*=double(segment);       // this is segments start edge point
 //a*=double(segment+1);  // this is segments end edge point
 x=x0+(rx*cos(a));
 y=y0+(ry*sin(a));
 }

for booth:

  • x,y is point
  • segments number of division segments.
  • segment is sweep-ed area < 0,segments )

OTHER TIPS

Apply an affine transformation to turn your ellipse into a circle, preferrably the unit circle. Then split that into equal sized segments, before you apply the inverse transform. The transformation will scale all areas (as opposed to lengths) by the same factor, so equal area translates to equal area.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top