我正在尝试让一个2D精灵以"弧线"(半椭圆)移动,而不是直线。我有起点和终点的X、Y位置,以及所需半径。
最佳实现方法是什么?
我认为这个问题最好通过一系列坐标变换来解决。为了符号简便,假设你有的两个点是u和v。
假设你正在处理一个非常简单的情况——点u和v分别位于(1,0)和(-1,0),椭圆的长轴长度为1。那么你只需要跟踪半圆。假设你想以恒定的速度在这些点之间插值,你可以使用以下公式:
x(t) = cos(pi * t)
y(t) = sin(pi * t)
当然,你可能没有那么幸运处于这种设置中,因此我们可以进行一系列坐标变换来将你带入这个配置。首先,让我们定义点w为u=(x0,y0)和v=(x1,y1)之间的中点。也就是说:
w = (x2, y2) = ((x0 + x1) / 2, (y0 + y1) / 2)
现在,假设您翻译u和v,使得w位于原点。这意味着u和v沿相反的向量与原点等距离。如果我们使用矩阵和齐次坐标,则可以表示为:
| 1 0 -x2 |
T = | 0 1 -y2 |
| 0 0 1 |
在这个翻译之后,u和v的位置分别由Tu
和Tv
给出。我们称这些点为u'和v'。它们由以下公式给出:
u' = (x0 - x2, x1 - y2) = (x0 / 2 - x1 / 2, y0 / 2 - y1 / 2)
v' = (x1 - x2, y1 - y2) = (x1 / 2 - x0 / 2, y1 / 2 - y0 / 2)
我们现在更接近解决原始问题了,但是我们有一个问题,即u'和v'与x轴不太对齐,就像在原始问题中一样。为了解决这个问题,我们将应用旋转变换,使得u'最终位于(1,0),v'最终位于(0,1)。为此,我们将建立一个坐标系,在该坐标系中,一个基向量沿着u'的方向,另一个基向量垂直于它。为了做到这一点,我们将选择以下单位向量:
e0 = u' / ||u||
e1 = perp(e0)
其中perp
是垂直于e0
的某个单位向量。一种获取该向量的方法是,如果e0 = (x3, y3)
,则e1 = perp(e0) = (-y3, x3)
。您可以验证这个向量与(x3, y3)
垂直,因为它们的点积为零。
给定这些向量,我们可以定义一个变换,将(1, 0)映射到e0
,将(0, 1)映射到e1
,如下:
|x3 -y3 0|
|y3 x3 0|
| 0 0 1|
(最后一列是齐次坐标系)
当然,这与我们想要的相反 - 我们试图将e0
映射到(1, 0),将e1
映射到(0, 1)。为了得到这个矩阵,我们只需反转上面的矩阵。幸运的是,由于我们选择的e0
和e1
是正交的,上面的矩阵是正交的,因此它的逆矩阵就是它的转置:
| x3 y3 0|
R = |-y3 x3 0|
| 0 0 1|
R
应用于u'
和v'
,我们最终得到向量(1,0)和(-1,0),这正是我们想要它们的位置。现在的问题是,我们想要跟踪的椭圆不一定具有单位高度。例如,如果我们称其高度为h
,那么我们将跟踪一个半长轴为h
,半短轴为1
的椭圆路径。但是,这可以通过另一个坐标变换轻松纠正,这次通过因子1/h
缩放坐标系的高度,使我们想要跟踪的椭圆的高度为1。这可以使用以下缩放矩阵完成: | 1 0 0 |
S = | 0 1/h 0 |
| 0 0 1 |
这种设置之所以有用,是因为我们知道如果我们取所需椭圆上介于u
和v
之间的任意点,然后将矩阵SRT
应用于它,那么我们最终会将其转换为使用单位圆上对应点的路径,该路径从(1,0)到(-1,0)。更重要的是,反过来也是一样的。如果我们将单位圆上任意点应用SRT
的逆,我们最终会得到原始椭圆路径上介于u
和v
之间的对应点!为了达成协议,我们知道如何找到从(1,0)到(-1,0)路径上的点,因此我们有一个解决此问题的算法:
| 1 0 -x2 | | 1 0 x2 |
T = | 0 1 -y2 | T^-1 = | 0 1 y2 |
| 0 0 1 | | 0 0 1 |
| x3 y3 0| | x3 -y3 0 |
R = |-y3 x3 0| R^-1 = | y3 x3 0 |
| 0 0 1| | 0 0 1 |
| 1 0 0 | | 1 0 0 |
S = | 0 1/h 0 | S^-1 = | 0 h 0 |
| 0 0 1 | | 0 0 1 |
简而言之,最终算法如下:
如果这里有很多数学内容,抱歉了,但是你的答案(希望如此!)应该由上述过程给出。
你可能想使用椭圆的参数方程,公式如下所示
http://en.wikipedia.org/wiki/Ellipse#General_parametric_form
既然你有一个起始点和结束点,你需要在两端解决t的问题,
然后以相对较小的增量从起点到终点步进。
我相信你正在寻找贝塞尔曲线,请查看http://www.math.ucla.edu/~baker/java/hoefer/Bezier.htm。源代码也可以在同一链接中找到。
如果你正在使用SWT,你可以查看http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html#drawArc(int, int, int, int, int, int)