在中学或大学时代,我们应该在数学中都学过椭圆方程、双曲线方程等等,当然那个时候学习这些知识的目的就是为了考试,为了能够拿个好成绩,上个好大学等。那么除此之外,这些知识对于我们今后的生活或者工作又带来什么便利呢?

 

巧合的是作为程序员,尤其是偏向算法方面的人员,经常会有这种需求,例如实现一个物体做椭圆运动的效果,或者做一个圆形轨迹的效果,亦或者做一个看似毫无规律的曲线运动,那么这就用到了椭圆方程、圆形方程及贝塞尔曲线方程等等。

 

在此着重介绍下椭圆方程的应用。

 

一、 标准椭圆方程公式:


二、 中心点在 ( h , k ),主轴平行于x轴时公式:


三、 常见的名词概念及椭圆形状:

       

          图1                        图2      

  • 长轴:通过连接椭圆上的两个点所能获得的最长线段,对应图2中的major axis。
  • 半长轴:长轴的一半,对应椭圆公式中的a。
  • 短轴:垂直平分长轴的直线所得弦, 对应图2中的minor axis。
  • 半短轴:短轴的一半,对应椭圆公式中的b。
  • 焦点:每个椭圆均有两个焦点,分别上上图中的F1 , F2。
  • 近拱点:指定一个焦点F1 , 距离焦点F1最近点成为近拱点,也就是图中的A。
  • 远拱点:相对于F1焦点而言,距离F1最远点成为远拱点,也就是图中的B。
  • 离心率:用来描述轨道的形状,椭圆的离心率大小区间(0, 1) ,当离心率为0时表示圆。

四、 离心率的计算公式:

  • 根据近拱点和远拱点距离计算:


其中dp表示近拱点的距离,da表示远拱点的距离。

  • 根据半焦距和半长轴计算:


其中c表示半焦距,a表示半长轴。

  • 根据半长轴和半短轴计算:


其中,a表示半长轴,b表示半短轴。

 

五、 已知椭圆其中一个焦点、近拱点、离心率以及表示椭圆在三维空间的倾向法向量,可以得到此椭圆的公式。

1. 根据焦点和近拱点,得到近拱点到焦点的距离dp。

2. 根据离心率、近拱点距离可以得到远拱点到焦点的距离da。

3. 根据近拱点、焦点以及远拱点距离,可以得到远拱点的坐标值。

4. 根据近拱点、远拱点即可得到椭圆的中心点坐标center。

5. 根据公式:半短轴 = 近拱点距离 * 远拱点距离,即可得到半长轴b的长度。

6. 根据代表椭圆倾向的法向量n和长轴方向的单位向量,两者叉乘即可得到短轴方向上的单位向量,并且根据半短轴的长度,即可得到半短轴向量。

7. 已知半短轴向量、半长轴向量,根据椭圆的参数方程,即可得到椭圆上任一点的坐标值。

 

具体代码如下:

1 //mDirection代表点在椭圆上的运动方向是逆时针还是顺时针,angle用于计算椭圆参数方程的角度
 2 double angle = (mDirection == OrbitDirection.CLOCKWISE ? -1 : 1) * mAngle * time * MathUtil.PRE_PI_DIV_180;
 3 
 4 //计算近拱点到焦点的距离
 5 double periapsisRadius = mPeriapsis.distanceTo(mFocalPoint);
 6 //根据离心率、近拱点到焦点的距离、远拱点到焦点三者的公式即可得到远拱点距离(近拱点到远拱点的距离是椭圆的长轴)
 7 double apoapsisRadius = periapsisRadius * (1 + mEccentricity) / (1 - mEccentricity);
 8 
 9 // 计算近拱点到焦点的单位向量,注意此处乘以1e8 和除以1e8的目的是 去掉第8个小数点后最不重要的数字,以降低计算误差。
10 double uAx = (Math.round(mFocalPoint.x * 1e8) - Math.round(mPeriapsis.x * 1e8)) / 1e8;
11 double uAy = (Math.round(mFocalPoint.y * 1e8) - Math.round(mPeriapsis.y * 1e8)) / 1e8;
12 double uAz = (Math.round(mFocalPoint.z * 1e8) - Math.round(mPeriapsis.z * 1e8)) / 1e8;
13 double mod = Math.sqrt(uAx * uAx + uAy * uAy + uAz * uAz);//计算近拱点到焦点的距离
14 if (mod != 0 && mod != 1) {//单位化
15     mod = 1 / mod;
16     uAx *= mod;
17     uAy *= mod;
18     uAz *= mod;
19 }
20 
21 double apoapsisDir_x = Math.round(uAx * apoapsisRadius * 1e8) / 1e8;
22 double apoapsisDir_y = Math.round(uAy * apoapsisRadius * 1e8) / 1e8;
23 double apoapsisDir_z = Math.round(uAz * apoapsisRadius * 1e8) / 1e8;
24 //计算远拱点坐标
25 double apoapsisPos_x = Math.round((apoapsisDir_x + mFocalPoint.x) * 1e8) / 1e8;
26 double apoapsisPos_y = Math.round((apoapsisDir_y + mFocalPoint.y) * 1e8) / 1e8;
27 double apoapsisPos_z = Math.round((apoapsisDir_z + mFocalPoint.z) * 1e8) / 1e8;
28 
29 //近拱点和远拱点的中心即椭圆的中心
30 double center_x = Math.round(((mPeriapsis.x + apoapsisPos_x) / 2) * 1e8) / 1e8;
31 double center_y = Math.round(((mPeriapsis.y + apoapsisPos_y) / 2) * 1e8) / 1e8;
32 double center_z = Math.round(((mPeriapsis.z + apoapsisPos_z) / 2) * 1e8) / 1e8;
33 
34 //计算半短轴的长度
35 double b = Math.sqrt(periapsisRadius * apoapsisRadius);
36 
37 //从中心点到近拱点的向量
38 double semimajorAxis_x = Math.round((mPeriapsis.x - center_x) * 1e8) / 1e8;
39 double semimajorAxis_y = Math.round((mPeriapsis.y - center_y) * 1e8) / 1e8;
40 double semimajorAxis_z = Math.round((mPeriapsis.z - center_z) * 1e8) / 1e8;
41 
42 //单位化半长轴向量
43 double unitSemiMajorAxis_x = semimajorAxis_x;
44 double unitSemiMajorAxis_y = semimajorAxis_y;
45 double unitSemiMajorAxis_z = semimajorAxis_z;
46 mod = Math.sqrt(semimajorAxis_x * semimajorAxis_x + semimajorAxis_y * semimajorAxis_y + semimajorAxis_z
47         * semimajorAxis_z);
48 if (mod != 0 && mod != 1) {
49     mod = 1 / mod;
50     unitSemiMajorAxis_x *= mod;
51     unitSemiMajorAxis_y *= mod;
52     unitSemiMajorAxis_z *= mod;
53 }
54 
55 //将中心点沿法向量方向平移
56 Vector3 unitNormal = mNormal.clone();
57 unitNormal.normalize();
58 double uNx = Math.round(unitNormal.x * 1e8) / 1e8;
59 double uNy = Math.round(unitNormal.y * 1e8) / 1e8;
60 double uNz = Math.round(unitNormal.z * 1e8) / 1e8;
61 double normalCenter_x = center_x + uNx;
62 double normalCenter_y = center_y + uNy;
63 double normalCenter_z = center_z + uNz;
64 mod = Math.sqrt(normalCenter_x * normalCenter_x + normalCenter_y * normalCenter_y + normalCenter_z
65         * normalCenter_z);
66 if (mod != 0 && mod != 1) {
67     mod = 1 / mod;
68     normalCenter_x *= mod;
69     normalCenter_y *= mod;
70     normalCenter_z *= mod;
71 }
72 
73 mScratch1.setAll(unitSemiMajorAxis_x, unitSemiMajorAxis_y, unitSemiMajorAxis_z);
74 mScratch2.setAll(normalCenter_x, normalCenter_y, normalCenter_z);
75 //叉乘计算半短轴的单位向量
76 Vector3 semiminorAxis = mScratch3.crossAndSet(mScratch1, mScratch2);
77 //得到半短轴向量
78 semiminorAxis.multiply(b);
79 
80 //3D空间椭圆的参数方程
81 double x = center_x + (Math.cos(angle) * semimajorAxis_x) + (Math.sin(angle) * semiminorAxis.x);
82 double y = center_y + (Math.cos(angle) * semimajorAxis_y) + (Math.sin(angle) * semiminorAxis.y);
83 double z = center_z + (Math.cos(angle) * semimajorAxis_z) + (Math.sin(angle) * semiminorAxis.z);