在中学或大学时代,我们应该在数学中都学过椭圆方程、双曲线方程等等,当然那个时候学习这些知识的目的就是为了考试,为了能够拿个好成绩,上个好大学等。那么除此之外,这些知识对于我们今后的生活或者工作又带来什么便利呢?
巧合的是作为程序员,尤其是偏向算法方面的人员,经常会有这种需求,例如实现一个物体做椭圆运动的效果,或者做一个圆形轨迹的效果,亦或者做一个看似毫无规律的曲线运动,那么这就用到了椭圆方程、圆形方程及贝塞尔曲线方程等等。
在此着重介绍下椭圆方程的应用。
一、 标准椭圆方程公式:
二、 中心点在 ( 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);