我想计算光线和线段之间的交叉点。为此,我形成线性方程并寻找交叉点。现在我遇到一个例子的数值问题。我的代码的缩写:
public class Test {
public static void main(String[] args) {
double rayAX = 443.19661703858895d;
double rayAY = 666.3485960845833d;
double rayBX = 443.196744279195d;
double rayBY = 103.21654864924565d;
double segAX = 450.0d;
double segAY = 114.42801992127828d;
double segBX = 443.196744279195d;
double segBY = 103.21654864924565d;
double a1 = (rayBY - rayAY) / (rayBX - rayAX);
double t1 = rayAY - rayAX * a1;
double a2 = (segBY - segAY) / (segBX - segAX);
double t2 = segAY - segAX * a2;
double x = (t2 - t1) / (a1 - a2);
double y = a1 * x + t1;
System.out.println(x);
System.out.println(y);
}
}
显然,返回应为(443.196744279195,103.21654864924565),因为此点在光线和线段上都相同。 但实际回报是在我的情况下(443.19674427919506,103.21654844284058)
在第二个数字中,小数点后第六位出现错误。 我猜错误是因为值rayAX和rayBX彼此非常接近。我的问题是:在计算交叉点时,我能获得更精确的结果吗?
答案 0 :(得分:3)
这里是一种更加数字稳定的交叉方式(注意它实际上是两条线的交叉点......看起来你原来的代码并没有检查交叉点是否在细分市场):
double rX = rayBX - rayAX;
double rY = rayBY - rayAY;
double sAX = segAX - rayAX;
double sAY = segAY - rayAY;
double areaA = sAX * rY - sAY * rX;
double sBX = segBX - rayAX;
double sBY = segBY - rayAY;
double areaB = sBX * rY - sBY * rX;
double t = areaA / (areaA - areaB);
// if t is not between 0 and 1, intersection is not in segment
double x = (1 - t) * segAX + t * segBX;
double y = (1 - t) * segAY + t * segBY;
粗略解释:让A
和B
成为光线的端点,让X
和Y
成为细分的端点。让P
成为我们正在寻找的交叉点。然后,PX
与PY
的比率等于ABX
的面积与ABY
的面积之比。您可以使用交叉产品计算面积,这是上面的代码所做的。请注意此过程如何仅使用一个除法,这有助于最小化数值不稳定性。
答案 1 :(得分:0)
据我所知,通过高斯旋转的Gauss或Gauss-Jordan方法可以获得最佳的数值稳定性。
您需要为R
和S
解决此线性2x2系统。
(Brx - Arx).R - (Bsx - Asx).S = Asx - Arx
(Bxy - Ary).R - (Bsx - Asx).S = Asy - Ary
Total pivoting告诉您选择具有最大模块的LHS系数。有四种可能的选择,因此您必须实现四种算法版本。
例如,假设左上系数在系统中占主导地位
A.X + B.Y = C
D.X + E.Y = F
然后
X + (B/A).Y = (C/A)
D.X + E .Y = F
,
(E - D.(B/A)) Y = F - D.(C/A)
和
Y = (F - D.(C/A)) / (E - D.(B/A))
X = (C/A) - (B/A).Y
使用精确算术这确实等同于克莱默的规则,但从数字的角度来看可能更好。
其他案例是对称处理的。