我认为(1)计算线盘交点会更简单,它可以是空的、点或线段(2)测试交点是否与线段相交。
直线上的点是((1-t) x1 + t x2, (1-t) y1 + t y2)
对于所有真实的t
. Let x(t) = (1-t) x1 + t x2 - cx
and y(t) = (1-t) y1 + t y2 - cy
。线盘交点非空当且仅当多项式x(t)^2 + y(t)^2 - r^2 = 0
有真正的根源t1 <= t2
。在这种情况下,线盘交点全部为t
in [t1, t2]
。线段都是t
in [0, 1]
。两者相交当且仅当t1 <= 1
and t2 >= 0
.
计算t1
and t2
需要平方根,我们可以避免。让a, b, c
是这样的x(t)^2 + y(t)^2 - r^2 = a t^2 + b t + c
。我们有t1 + t2 = -b/a
and t1 t2 = c/a
.
-
根t1
and t2
是真实的当且仅当b^2 - 4 a c >= 0
.
-
条件t1 <= 1
为假当且仅当t1 - 1 > 0
and t2 - 1 > 0
,当且仅当(t1 - 1) + (t2 - 1) > 0
and (t1 - 1) (t2 - 1) > 0
,这相当于-b/a - 2 > 0
and c/a + b/a + 1 > 0
. Since a > 0
,这简化为-b > 2 a
and c + b + a > 0
.
-
条件t2 >= 0
为假当且仅当t1 < 0
and t2 < 0
,当且仅当t1 + t2 = -b/a < 0
and t1 t2 = c/a > 0
. Since a > 0
,这简化为b > 0
and c > 0
.
在 JavaScript 中实现。
function lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r) {
let x_linear = x2 - x1;
let x_constant = x1 - cx;
let y_linear = y2 - y1;
let y_constant = y1 - cy;
let a = x_linear * x_linear + y_linear * y_linear;
let half_b = x_linear * x_constant + y_linear * y_constant;
let c = x_constant * x_constant + y_constant * y_constant - r * r;
return (
half_b * half_b >= a * c &&
(-half_b <= a || c + half_b + half_b + a <= 0) &&
(half_b <= 0 || c <= 0)
);
}
function lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) {
let deltaX = x2 - x1;
let deltaY = y2 - y1;
let mag = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
let unitX = deltaX / mag;
let unitY = deltaY / mag;
let d = (cx - x1) * unitY - (cy - y1) * unitX;
if (d < -r || d > r) {
return false;
}
let x1CXDelta = x1 - cx;
let y1CYDelta = y1 - cy;
let x2CXDelta = x2 - cx;
let y2CYDelta = y2 - cy;
let pointOneWithinCircle =
x1CXDelta * x1CXDelta + y1CYDelta * y1CYDelta < r * r;
if (pointOneWithinCircle) {
return true;
}
let pointTwoWithinCircle =
x2CXDelta * x2CXDelta + y2CYDelta * y2CYDelta < r * r;
if (pointTwoWithinCircle) {
return true;
}
let foo = unitX * x1 + unitY * y1;
let bar = unitX * cx + unitY * cy;
let baz = unitX * x2 + unitY * y2;
return (foo < bar && bar < baz) || (baz < bar && bar < foo);
}
function test() {
for (let i = 0; i < 10000000; i++) {
let x1 = Math.random();
let y1 = Math.random();
let x2 = Math.random();
let y2 = Math.random();
let cx = Math.random();
let cy = Math.random();
let r = Math.random();
if (
lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) !=
lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r)
) {
console.log("bad");
break;
}
}
}
test();