[英]Check is a point (x,y) is between two points drawn on a straight line
我在两点 A(x,y)---B(x,y) 之间画了一条线 现在我有了第三个点 C(x,y)。 我想知道C是否位于A和B之间绘制的线上。我想用java语言来做。 我找到了几个与此类似的答案。 但是,每个人都有一些问题,没有人是完美的。
if (distance(A, C) + distance(B, C) == distance(A, B))
return true; // C is on the line.
return false; // C is not on the line.
或者只是:
return distance(A, C) + distance(B, C) == distance(A, B);
其工作方式相当简单。 如果 C 位于AB
线上,则会出现以下情况:
A-C------B
并且,无论它在那条线上的哪个位置, dist(AC) + dist(CB) == dist(AB)
。 对于任何其他情况,您有一个具有某种描述和“dist(AC) + dist(CB) > dist(AB)”的三角形:
A-----B
\ /
\ /
C
事实上,如果 C 位于外推线上,这甚至有效:
C---A-------B
前提是距离保持无符号。 距离dist(AB)
可以计算为:
___________________________
/ 2 2
V (A.x - B.x) + (A.y - B.y)
请记住浮点运算的固有限制(有限的精度)。 您可能需要选择“足够接近”的测试(例如,误差小于百万分之一)以确保等式正确运行。
注意! 纯数学!
你可以试试这个公式。 把你的A(x1, y1)
和B(x2, y2)
坐标放到公式中,然后你会得到类似的东西
y = k*x + b; // k and b - numbers
然后,任何满足这个方程的点都将位于你的线上。 要检查C(x, y)
是否在A(x1, y1)
和B(x2, y2)
,请检查: (x1<x<x2 && y1<y<y2) || (x1>x>x2 && y1>y>y2)
(x1<x<x2 && y1<y<y2) || (x1>x>x2 && y1>y>y2)
。
例子
A(2,3) B(6,5)
直线方程:
(y - 3)/(5 - 3) = (x - 2)/(6 - 2)
(y - 3)/2 = (x - 2)/4
4*(y - 3) = 2*(x - 2)
4y - 12 = 2x - 4
4y = 2x + 8
y = 1/2 * x + 2; // equation of line. k = 1/2, b = 2;
让我们检查C(4,4)
在这条线上。
2<4<6 & 3<4<5 // C between A and B
现在将 C 坐标放入方程:
4 = 1/2 * 4 + 2
4 = 2 + 2 // equal, C is on line AB
PS:正如@paxdiablo 所写,您需要在计算之前检查线是水平还是垂直。 只需检查
y1 == y2 || x1 == x2
我相信最简单的是
// is BC inline with AC or visa-versa
public static boolean inLine(Point A, Point B, Point C) {
// if AC is vertical
if (A.x == C.x) return B.x == C.x;
// if AC is horizontal
if (A.y == C.y) return B.y == C.y;
// match the gradients
return (A.x - C.x)*(A.y - C.y) == (C.x - B.x)*(C.y - B.y);
}
您可以通过将 x 值的差异除以 y 值的差异来计算梯度。
注意:如果在屏幕上绘制 C,则有一个不同的测试来查看 C 是否出现在 A 和 B 之间的线上。 数学假设 A、B、C 是无限小的点。 实际上很小到内表示误差。
以上答案不必要地复杂。 最简单的如下。
如果 (x-x1)/(x2-x1) = (y-y1)/(y2-y1) = alpha(常数),那么点 C(x,y) 将位于点 1 和 2 之间的线上.
如果 alpha < 0.0,则 C 在点 1 的外部。
希望这个答案有帮助。
我认为这里的所有方法都有一个缺陷,因为它们没有尽可能严格地处理舍入错误。 基本上,所描述的方法将使用一些简单的算法告诉您您的点是否足够接近该线,并且它或多或少会精确。
为什么精度很重要? 因为这正是 op 提出的问题。 对于计算机程序,没有线上的点,只有线的 epsilon 内的点,并且需要记录该 epsilon 是什么。
让我们来说明这个问题。 使用距离比较算法:
假设一个段从 (0, 0) 到 (0, 2000),我们在我们的应用程序中使用浮点数(它有大约 7 个小数位的精度),我们测试 (1E-6, 1000) 上的一个点是否是上线与否。
从线段的任一端到点的距离是 1000.0000000005 或 1000 + 5E-10,因此,加上到点和到点的距离的差值约为 1E-9。 但是这些值都不能以足够的精度存储在浮点数上,并且该方法将返回true
。
如果我们使用更精确的方法,比如计算到直线中最近点的距离,它会返回一个浮点数有足够精度来存储的值,我们可以根据可接受的 epsilon 返回 false。
我在示例中使用了浮点数,但同样适用于任何浮点类型,例如 double。
一种解决方案是使用 BigDecimal 和任何您想要的方法,如果性能和内存命中不是问题。
一种比比较浮点距离更精确的方法,更重要的是,虽然计算成本更高,但始终精确,是计算到直线中最近点的距离。
看起来我正在分裂头发,但我之前不得不处理这个问题。 这是链接几何运算时的问题。 如果你不能控制你正在处理的精度损失类型,最终你会遇到困难的错误,这将迫使你对代码进行严格的推理以修复它们。
我相信一个简单的方法是检查由 3 个点形成的角度。 如果角度 ACB 为 180 度(或接近它,取决于您想要的准确度),则点 C 位于 A 和 B 之间。
def DistBetwPoints(p1, p2):
return math.sqrt( (p2[0] - p1[0])**2 + (p2[1] - p1[1])**2 )
# "Check if point C is between line endpoints A and B"
def PointBetwPoints(A, B, C):
dist_line_endp = DistBetwPoints(A,B)
if DistBetwPoints(A,C)>dist_line_endp: return 1
elif DistBetwPoints(B,C)>dist_line_endp: return 1
else: return 0
这是我的 C# 解决方案。 我相信 Java 等价物将几乎相同。
笔记:
如果点位于线的边界内(它不假设无限线),则方法将仅返回 true。
它将处理垂直或水平线。
它计算要检查的点与线的距离,从而允许将公差传递给该方法。
/// <summary> /// Check if Point C is on the line AB /// </summary> public static bool IsOnLine(Point A, Point B, Point C, double tolerance) { double minX = Math.Min(AX, BX) - tolerance; double maxX = Math.Max(AX, BX) + tolerance; double minY = Math.Min(AY, BY) - tolerance; double maxY = Math.Max(AY, BY) + tolerance; //Check C is within the bounds of the line if (CX >= maxX || CX <= minX || CY <= minY || CY >= maxY) { return false; } // Check for when AB is vertical if (AX == BX) { if (Math.Abs(AX - CX) >= tolerance) { return false; } return true; } // Check for when AB is horizontal if (AY == BY) { if (Math.Abs(AY - CY) >= tolerance) { return false; } return true; } // Check istance of the point form the line double distFromLine = Math.Abs(((BX - AX)*(AY - CY))-((AX - CX)*(BY - AY))) / Math.Sqrt((BX - AX) * (BX - AX) + (BY - AY) * (BY - AY)); if (distFromLine >= tolerance) { return false; } else { return true; } }
这是我制作的 JavaScript 函数。 您向它传递三个点(具有 x 和 y 属性的三个对象)。 点 1 和 2 定义您的线,点 3 是您要测试的点。
您将收到一个带有一些有用信息的对象:
on_projected_line
- 如果pt3
位于线上的任何位置,包括点之外。on_line
-如果pt3
谎言上线之间或上pt1
和pt2
。x_between
- 如果pt3
在 x 边界之间或在 x 边界上。y_between
- 如果pt3
在 y 边界之间或之上。between
- 如果x_between
和y_between
都为真。 /** * @description Check if pt3 is on line defined by pt1 and pt2. * @param {Object} pt1 The first point defining the line. * @param {float} pt1.x * @param {float} pt1.y * @param {Object} pt2 The second point defining the line. * @param {float} pt2.x * @param {float} pt2.y * @param {Object} pt3 The point to test. * @param {float} pt3.x * @param {float} pt3.y */ function pointOnLine(pt1, pt2, pt3) { const result = { on_projected_line: true, on_line: false, between_both: false, between_x: false, between_y: false, }; // Determine if on line interior or exterior const x = (pt3.x - pt1.x) / (pt2.x - pt1.x); const y = (pt3.y - pt1.y) / (pt2.y - pt1.y); // Check if on line equation result.on_projected_line = x === y; // Check within x bounds if ( (pt1.x <= pt3.x && pt3.x <= pt2.x) || (pt2.x <= pt3.x && pt3.x <= pt1.x) ) { result.between_x = true; } // Check within y bounds if ( (pt1.y <= pt3.y && pt3.y <= pt2.y) || (pt2.y <= pt3.y && pt3.y <= pt1.y) ) { result.between_y = true; } result.between_both = result.between_x && result.between_y; result.on_line = result.on_projected_line && result.between_both; return result; } console.log("pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2})") console.log(pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2})) console.log("pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 0.5, y: 0.5})") console.log(pointOnLine({x: 0, y: 0}, {x: 1, y: 1}, {x: 0.5, y: 0.5}))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.