如何在椭圆上找到扫过给定区域的点?

时间:2014-01-22 08:05:14

标签: java geometry ellipse

我正在研究将椭圆划分为相等大小的段的问题。这个问题已被提出,但答案提示数值整合,以便我正在尝试。此代码使扇区短路,因此集成本身不应超过90度。整合本身是通过总计中间三角形的面积来完成的。下面是我尝试过的代码,但在某些情况下它会超过90度。

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

在输出中,我看到以下情况,它扫过116度。

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)

有没有办法修复积分公式来创建一个函数,该函数返回扫描给定区域的椭圆上的点?使用此代码的应用程序将总面积除以所需的段数,然后使用此代码确定每个段开始和结束的角度。不幸的是,它没有按预期工作。

*编辑* 我认为上面的集成失败了,因为基数和高度公式不正确。

2 个答案:

答案 0 :(得分:1)

应用仿射变换将椭圆变成圆形,最好是单位圆。然后在应用逆变换之前将其拆分为相等大小的段。转换将按相同因子缩放所有区域(而不是长度),因此相等区域转换为相等区域。

答案 1 :(得分:1)

不需要转换使用椭圆的参数方程......

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

其中a =&lt; 0,2.0 * M_PI&gt;

  • 如果您将椭圆从中心划分为x,则从上面的等式
  • 划分y
  • 和角度a均匀增加
  • 然后段将具有相同的大小

顺便说一句。如果你应用仿射变换,你将得到相同的结果(即使是相同的等式)

此代码将椭圆划分为大小均匀的块:

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

这是N = 5

的样子

ellipse division

<强> [EDIT1]

我不明白你的评论到底想要实现什么目标

  • 抱歉,但我的英语技能很糟糕
  • 好吧我假设这两种可能性(如果你需要不同的东西请说明更接近)

0.但首先需要一些全球或成员的东西

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.分段内部?

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 segment segment of section 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));
 }

对于展位:

  • x,y是点
  • 细分细分受众群的数量。
  • 段是扫描区域&lt; 0,段)