基本上圆的方程是(x - cx)2 + (y - cy)2 = r2。因此,您可以通过将 y 替换为 0 轻松找到圆和 X 轴之间的交点。之后,您只需解决一个简单的quadratic equation:x2 - 2cxx + cx2 + cy2 - r2 = 0 。为此,您有 3 种可能的结果:
- 没有交集 - 行列式将是无理数(JavaScript 中的 NaN),忽略此结果;
- 一个交集 - 两个解决方案都匹配,使用 [value, value];
- 两个交点 - 两种解决方案不同,请使用 [value1, value2]。
对新计算的交叉区间进行排序,然后尝试在可能的地方合并它们。但是请记住,在每种程序语言中都有近似值,因此您需要为点近似值定义增量值,并在合并区间时将其考虑在内。
当间隔合并时,您可以通过在每个间隔的开始/结束处减去/添加相同的增量值来生成 x 坐标。最后,从所有方面来看,最接近零的就是你的答案。
这里是一个 O(n log n) 复杂度的例子,它以可读性为导向。我用 1*10-10 表示 delta :
var circles = [
{x:0, y:0, r:1},
{x:2.5, y:0, r:1},
{x:-1, y:0.5, r:1},
{x:2, y:-0.5, r:1},
{x:-2, y:0, r:1},
{x:10, y:10, r:1}
];
console.log(getClosestPoint(circles, 1e-10));
function getClosestPoint(circles, delta)
{
var intervals = [],
len = circles.length,
i, result;
for (i = 0; i < len; i++)
{
result = getXIntersection(circles[i])
if (result)
{
intervals.push(result);
}
}
intervals = intervals.sort(function(a, b){
return a.from - b.from;
});
if (intervals.length <= 0) return 0;
intervals = mergeIntervals(intervals, delta);
var points = getClosestPoints(intervals, delta);
points = points.sort(function(a, b){
return Math.abs(a) - Math.abs(b);
});
return points[0];
}
function getXIntersection(circle)
{
var d = Math.sqrt(circle.r * circle.r - circle.y * circle.y);
return isNaN(d) ? null : {from: circle.x - d, to: circle.x + d};
}
function mergeIntervals(intervals, delta)
{
var curr = intervals[0],
result = [],
len = intervals.length, i;
for (i = 1 ; i < len ; i++)
{
if (intervals[i].from <= curr.to + delta)
{
curr.to = Math.max(curr.to, intervals[i].to);
} else {
result.push(curr);
curr = intervals[i];
}
}
result.push(curr);
return result;
}
function getClosestPoints(intervals, delta)
{
var result = [],
len = intervals.length, i;
for (i = 0 ; i < len ; i++)
{
result.push( intervals[i].from - delta );
result.push( intervals[i].to + delta );
}
return result;
}