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

Java 如何在椭圆上找到扫掠给定区域的点?,java,geometry,ellipse,Java,Geometry,Ellipse,我正在研究把一个椭圆分成大小相等的段的问题。有人问过这个问题,但答案是数值积分,所以我知道我在尝试什么。此代码会使扇区短路,因此集成本身的覆盖度不应超过90度。积分本身是通过将中间三角形的面积相加来完成的。下面是我尝试过的代码,但在某些情况下,它的范围超过了90度 public class EllipseModel { protected double r_x; protected double r_y; private double a,a2; privat

我正在研究把一个椭圆分成大小相等的段的问题。有人问过这个问题,但答案是数值积分,所以我知道我在尝试什么。此代码会使扇区短路,因此集成本身的覆盖度不应超过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;
    }
}
有没有办法修正积分公式,创建一个函数,返回扫掠给定区域的椭圆上的点?使用此代码的应用程序将总面积除以所需的段数,然后使用此代码确定每个段开始和结束的角度。不幸的是,它没有按预期工作

*编辑*
我相信上面的积分失败了,因为基数和高度公式不正确。

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

无需变换,请使用椭圆的参数方程

x=x0+rx*cos(a)
y=y0+ry*sin(a)
其中a=<0,2.0*M_PI>

  • 若你们把椭圆除以从中心到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);
    }
展位:

  • x、 y是重点
  • 段分割段的数目
  • 线段为扫掠区域(小于0,线段)

如果它有帮助的话,对于寻找椭圆段面积的相关问题-向下滚动到最后一篇文章,因为最初的答案有一些错误。我尝试了一下,确实有所帮助,[double theta=Math.atan(Math.tan((sweep_area*2)/(a*b))*b/a;]但它给了我一些不正确的负角度。getSweep(61261.056745)a=325.000000 b=200.000000偏差=0.166667面积=204203.522483-θ=62.166192。如果有清洁arctan的解决方案?使用atan2(a sin,B cos)谢谢,我能够得到一个函数算法,而不是atan2,如果θ为负值,我只需添加Math.PI,如果扫描面积大于面积的一半,则再次添加Math.PI。这给了我0:谢谢,这很有帮助,但没有回答问题。事实证明,我并不完全需要这个问题的答案,因为我的应用程序只需要确定给定的点是否在给定的扇区内。如果你能反向使用这个算法来求解double[]point=myFunc(double area,rx,ry),我很乐意接受这个答案。好的,我也编辑了我的答案。但我不确定你想要什么,所以如果这不包括它,请指定封闭库。我很惊讶它的工作原理,因为它是如此简单相比,我正在做的!你应该明确你的代码是用C编写的,而不是用Java编写的,但这对我来说没有问题,因为我也知道C。要明确,最后一个函数#2就是我想要的。但其他答案也非常有用。我希望能够进入一个区域并返回积分,但您提供的内容足以解决该问题。我试图修复答案,但版主否决了它。。。实际答案似乎是。。。。
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);
    }
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;
        }
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;
 }
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));
 }