2016-08-05 27 views
1

我使用下面的C++代码来计算我在画布上绘制的椭圆的外侧周围的360个点。我通过选择一个左边和右边作为x1,y1,x2,y2来设置椭圆的大小。当使用零度的水平主轴计算椭圆时,椭圆接触左边和右边。当我用45度长轴计算椭圆时,椭圆的顶部和底部不再接触左边和右边。我需要以一个角度绘制椭圆,以便触及左边界和右边界。要做到这一点,我需要绘制更大的椭圆,但我不知道如何计算更大的尺寸。有没有办法计算出较大的椭圆,这样成角度的椭圆会碰到原始的左右边界?如何调整椭圆的大小,使其始终接触左右边界?

zero degree ellipse 45 degree ellipse

double x1=0, y1=0, x2=0, y2=0, x3=0, y3=0, phi=0; 
int ZeroX=0, ZeroY=0; 
int NUM_POINTS_PER_CONTOUR = 360; 
int p=0; 
int Major_Axis_Center_X=0, Full_Major_Axis_X=0; 
int Major_Axis_Center_Y=0, Full_Major_Axis_Y=0; 
int Full_Minor_Axis_X=0; 
double AngleOfMajorAxis=0; 
UnicodeString temp; 


struct ell { 
double a; //e.a is semi-major size 
double b; //e.b is semi-minor size 
double theta; 
double x0; //major axis center X 
double y0; //major axis center Y 
} e; 



//seed Values 
x1=50; x2=250; 
y1=75; y2=275; 

//Vertical Line LEFT edge 
Canvas->MoveTo(x1, y1); 
Canvas->LineTo(x1, y2); 

//Vertical Line RIGHT edge 
Canvas->MoveTo(x2, y1); 
Canvas->LineTo(x2, y2); 

Full_Major_Axis_X = (x2 - x1); 
Full_Major_Axis_Y = (y2 - y1); 
Major_Axis_Center_X = (x1 + (Full_Major_Axis_X/2)); 
Major_Axis_Center_Y = (y2 - (Full_Major_Axis_Y/2)); 
Full_Minor_Axis_X = (Full_Major_Axis_X/2); 

//Seed values 
e.a = (Full_Major_Axis_X/2); //e.a is semi-major size 
e.b = (Full_Minor_Axis_X/2); //e.b is semi-minor size 
e.x0 = Major_Axis_Center_X; 
e.y0 = Major_Axis_Center_Y; 

AngleOfMajorAxis = 45.0; 
e.theta = DegToRad(AngleOfMajorAxis); 

//Calculate 360 points around edge of ellipse 
for (p=0; p<NUM_POINTS_PER_CONTOUR; p++) { 
phi = p*2*M_PI/(double)NUM_POINTS_PER_CONTOUR; 
x1 = e.a * std::sin(phi); 
y1 = e.b * std::cos(phi); 
x2 = x1 * std::cos(e.theta) + y1 * std::sin(e.theta); 
y2 = y1 * std::cos(e.theta) - x1 * std::sin(e.theta); 
x3 = x2 + e.x0; 
y3 = y2 + e.y0; 
if(p==0){ 
Canvas->MoveTo(x3, y3); 
} 
Canvas->LineTo(x3, y3); 
} 

编辑:这是最好的答案

我已经添加了两行代码,我上面的for循环

phiMax = atan(e.b/e.a * std::tan(e.theta)); // {1} 
Coeff = e.a/(e.a * std::cos(phiMax) * std::cos(e.theta) + e.b * std::sin(phiMax) * std::sin(e.theta)); // {2} 

现在我加把系数成for循环

//Calculate 360 points around edge of ellipse 
for (p=0; p<NUM_POINTS_PER_CONTOUR; p++) { 
phi = p*2*M_PI/(double)NUM_POINTS_PER_CONTOUR; 
x1 = Coeff * e.a * std::sin(phi); 
y1 = Coeff * e.b * std::cos(phi); 
x2 = x1 * std::cos(e.theta) + y1 * std::sin(e.theta); 
y2 = y1 * std::cos(e.theta) - x1 * std::sin(e.theta); 
x3 = x2 + e.x0; 
y3 = y2 + e.y0; 
if(p==0){ 
Canvas->MoveTo(x3, y3); 
} 
Canvas->LineTo(x3, y3); 
} 

结果是椭圆的最宽部分总是接触左边界和右边界。这是最简单和最接近的答案。下面的图片显示了一个65度椭圆,调整后可以触及边界。

65 degree ellipse

+0

不熟悉计算几何,但是有没有一种有效的方法来验证ecllipse是否触及两侧,给出ecllipse的大小?如果是的话,也许你可以做一个二分搜索:不断调整椭圆的大小,如果它接触/相交的边,减小其大小,否则增加其大小,因为边界不是整数,你可以简单地运行这个切断百次,以便精度足够高,最终的大小就是答案 – shole

+0

@shole,是的,我可以找到一个for循环的答案,但它很慢,我不知道它是正确的。我希望有一个公式可以给出确切的答案。 – homebase

+0

即使你有一个公式,因为你没有处理整数,double/float数据类型会给你自己的精度问题(取决于你需要多少精度)。因此,使用或不使用公式不是主要问题,精度错误是。使用循环进行二进制搜索200次,精度将比1e-30高得多,这非常精确,基本上瓶颈又是数据类型的精度。如果您的验证过程高效,此方法非常有效。当然,使用公式会给你一个最好的O(1)算法:) – shole

回答

3

椭圆在(0,0)中心的,由t(您的theta)转动时,具有方程x坐标

x = a * cos(phi) * cos(t) + b * sin(phi) * sin(t) 

极值点(左和右)是达到衍生物为零时x'=0

dx/dphi = - a * sin(phi) * cos(t) + b * cos(phi) * sin(t) = 0 
a * sin(phi) * cos(t) = b * cos(phi) * sin(t) 
tg(phi) = b/a * tg(t) 
phiMax = atan(b/a * tg(t))  {1} 

替换第一个方程中,且该值找到放大系数(极值半尺寸应等于半轴)

Coeff * (a * cos(phiMax) * cos(t) + b * sin(phiMax) * sin(t)) = a 
Coeff = a/(a * cos(phiMax) * cos(t) + b * sin(phiMax) * sin(t)) {2} 

现在,您可以实现公式{1}{2}和使用该系数的像这样的计算:

x1 = Coeff * e.a * std::sin(phi); 
y1 = Coeff * e.b * std::cos(phi); 
+0

这看起来正确。今天让我测试一下,今晚我会问我有没有问题。 – homebase

+0

我编辑了我的问题,以显示您的答案是正确的。 – homebase

0

我觉得会帮助你的是,如果你看看你的第一幅图,你可以使用的(X,Y)的原点平分原来的椭圆在x & y方向等等您沿着x轴和y轴有两条对称线。然后,您将在您的原始椭圆上有4个兴趣点,这些位置将是椭圆相交或触及+ x轴的位置以及+ y轴位置的位置。这将给你这个椭圆短半径和长半径的2个半径。您可以并将需要使用这些参考和计算;在旋转椭圆后,我想从它的原点(中心点)开始想象,然后需要使用一些三角函数和矢量代数来确定与垂直相交的45度旋转线上椭圆边缘点的矢量长度差异line(x2y1,x2y2)找到这个距离,一旦你知道距离,然后使用一些矢量计算,你应该能够计算出多少扭曲这个椭圆直到它有一个边缘点接触这条垂直线。由于我们从这个椭圆的原点平分线旋转并且有2条对称线,所以您不必担心其他垂直线(x1y1,x2y1)。您可以执行所有这些计算预处理。一旦你有了所有旋转后的歪斜点的新坐标,你就可以刷新你的更新点或绘图缓冲点。我会试着与一对夫妇图片来证明这一点:

Does Not Touch

Zoomed In View

要找到P2的距离 - P1应该是相当容易。您应该已经知道垂直线的x值;并且找到旋转线的y值应该很容易,因为您说它是45度旋转,它的斜率为1,该线的方程为y = x。这应该能够以最小的计算相当快地给你P2。要找到P1,它应该几乎一样简单,因为它再次沿着y = x的线,并且知道这个椭圆的长半径的大小。然后你减去两点给你这个距离。从那里你可以使用这个距离来知道沿y = x线倾斜或拉伸这个椭圆的程度。然后重新计算所有点并使用基于偏斜值的新计算点更新绘图缓冲区。现在有两种方法来扭曲这种情况。一个是短半径不变的地方,它变得更长,看起来更拉伸,而另一个是比例偏斜,如果长半径变大,短半径按比例变大。我会留给你,以确定你想如何扭曲这个对象。

1

如果线是一段距离d开,偏心e的椭圆的半长轴a的长度可以通过a = d/2 (1 - e^2 * sin^2 (theta)),其中theta是旋转的椭圆的角度进行说明。

例如,角度0给出a = d/2,根据定义这是正确的。在角度为pi/445 degrees的情况下,这转换为a = d/(2 - e^2)

我不完全确定这是否正确,但我稍后将编辑此帖子,以便您可以检查我的作品,如果您喜欢。另外,如果您不知道,椭圆的偏心率可由e^2 = 1 - (b/a)^2给出,其中ab分别是半长轴和半短轴长度。对于椭圆,它的值介于0和1之间。

编辑:更正式

相关问题