简体   繁体   English

检查点 (x,y) 是否在直线上绘制的两点之间

[英]Check is a point (x,y) is between two points drawn on a straight line

I have drawn a line between two points A(x,y)---B(x,y) Now I have a third point C(x,y).我在两点 A(x,y)---B(x,y) 之间画了一条线 现在我有了第三个点 C(x,y)。 I want to know that if C lies on the line which is drawn between A and B. I want to do it in java language.我想知道C是否位于A和B之间绘制的线上。我想用java语言来做。 I have found couple of answers similar to this.我找到了几个与此类似的答案。 But, all have some problems and no one is perfect.但是,每个人都有一些问题,没有人是完美的。

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.

or just:或者只是:

return distance(A, C) + distance(B, C) == distance(A, B);

The way this works is rather simple.其工作方式相当简单。 If C lies on the AB line, you'll get the following scenario:如果 C 位于AB线上,则会出现以下情况:

A-C------B

and, regardless of where it lies on that line, dist(AC) + dist(CB) == dist(AB) .并且,无论它在那条线上的哪个位置, dist(AC) + dist(CB) == dist(AB) For any other case, you have a triangle of some description and 'dist(AC) + dist(CB) > dist(AB)':对于任何其他情况,您有一个具有某种描述和“dist(AC) + dist(CB) > dist(AB)”的三角形:

A-----B
 \   /
  \ /
   C

In fact, this even works if C lies on the extrapolated line:事实上,如果 C 位于外推线上,这甚至有效:

C---A-------B

provided that the distances are kept unsigned.前提是距离保持无符号。 The distance dist(AB) can be calculated as:距离dist(AB)可以计算为:

  ___________________________
 /           2              2
V (A.x - B.x)  + (A.y - B.y)

Keep in mind the inherent limitations (limited precision) of floating point operations.请记住浮点运算的固有限制(有限的精度)。 It's possible that you may need to opt for a "close enough" test (say, less than one part per million error) to ensure correct functioning of the equality.您可能需要选择“足够接近”的测试(例如,误差小于百万分之一)以确保等式正确运行。

ATTENTION!注意! Math-only!纯数学!

试试这个!

You can try this formula.你可以试试这个公式。 Put your A(x1, y1) and B(x2, y2) coordinates to formula, then you'll get something like把你的A(x1, y1)B(x2, y2)坐标放到公式中,然后你会得到类似的东西

y = k*x + b; // k and b - numbers

Then, any point which will satisfy this equation, will lie on your line.然后,任何满足这个方程的点都将位于你的线上。 To check that C(x, y) is between A(x1, y1) and B(x2, y2) , check this: (x1<x<x2 && y1<y<y2) || (x1>x>x2 && y1>y>y2)要检查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) . (x1<x<x2 && y1<y<y2) || (x1>x>x2 && y1>y>y2)

Example例子

A(2,3) B(6,5)

The equation of line:直线方程:

(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;

Let's check if C(4,4) lies on this line.让我们检查C(4,4)在这条线上。

2<4<6 & 3<4<5 // C between A and B

Now put C coordinates to equation:现在将 C 坐标放入方程:

4 = 1/2 * 4 + 2
4 = 2 + 2 // equal, C is on line AB

PS: as @paxdiablo wrote, you need to check if line is horizontal or vertical before calculating. PS:正如@paxdiablo 所写,您需要在计算之前检查线是水平还是垂直。 Just check只需检查

y1 == y2 || x1 == x2

I believe the simplest is我相信最简单的是

// 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);
}

You can calculate the gradient by taking the difference in the x values divided by the difference in the y values.您可以通过将 x 值的差异除以 y 值的差异来计算梯度。

Note: there is a different test to see if C appears on the line between A and B if you draw it on a screen.注意:如果在屏幕上绘制 C,则有一个不同的测试来查看 C 是否出现在 A 和 B 之间的线上。 Maths assumes that A, B, C are infinitely small points.数学假设 A、B、C 是无限小的点。 Actually very small to within representation error.实际上很小到内表示误差。

The above answers are unnecessarily complicated.以上答案不必要地复杂。 The simplest is as follows.最简单的如下。

  1. if (x-x1)/(x2-x1) = (y-y1)/(y2-y1) = alpha (a constant), then the point C(x,y) will lie on the line between pts 1 & 2.如果 (x-x1)/(x2-x1) = (y-y1)/(y2-y1) = alpha(常数),那么点 C(x,y) 将位于点 1 和 2 之间的线上.

  2. If alpha < 0.0, then C is exterior to point 1.如果 alpha < 0.0,则 C 在点 1 的外部。

  3. If alpha > 1.0, then C is exterior to point 2.如果 alpha > 1.0,则 C 在点 2 的外部。
  4. Finally if alpha = [0,1.0], then C is interior to 1 & 2.最后,如果 alpha = [0,1.0],则 C 是 1 & 2 的内部。

Hope this answer helps.希望这个答案有帮助。

I think all the methods here have a pitfall, in that they are not dealing with rounding errors as rigorously as they could.我认为这里的所有方法都有一个缺陷,因为它们没有尽可能严格地处理舍入错误。 Basically the methods described will tell you if your point is close enough to the line using some straightforward algorithm and that it will be more or less precise.基本上,所描述的方法将使用一些简单的算法告诉您您的点是否足够接近该线,并且它或多或少会精确。

Why precision is important?为什么精度很重要? Because it's the very problem presented by op.因为这正是 op 提出的问题。 For a computer program there is no such thing as a point on a line, there is only point within an epsilon of a line and what that epsilon is needs to be documented.对于计算机程序,没有线上的点,只有线的 epsilon 内的点,并且需要记录该 epsilon 是什么。

Let's illustrate the problem.让我们来说明这个问题。 Using the distance comparison algorithm:使用距离比较算法:

Let's say a segment goes from (0, 0) to (0, 2000), we are using floats in our application (which have around 7 decimal places of precision) and we test whether a point on (1E-6, 1000) is on the line or not.假设一个段从 (0, 0) 到 (0, 2000),我们在我们的应用程序中使用浮点数(它有大约 7 个小数位的精度),我们测试 (1E-6, 1000) 上的一个点是否是上线与否。

The distance from either end of the segment to the point is 1000.0000000005 or 1000 + 5E-10, and, thus, the difference with the addition of the distance to and from the point is around 1E-9.从线段的任一端到点的距离是 1000.0000000005 或 1000 + 5E-10,因此,加上到点和到点的距离的差值约为 1E-9。 But none of those values can be stored on a float with enough precission and the method will return true .但是这些值都不能以足够的精度存储在浮点数上,并且该方法将返回true

If we use a more precise method like calculating the distance to the closest point in the line, it returns a value that a float has enough precision to store and we could return false depending on the acceptable epsilon.如果我们使用更精确的方法,比如计算到直线中最近点的距离,它会返回一个浮点数有足够精度来存储的值,我们可以根据可接受的 epsilon 返回 false。

I used floats in the example but the same applies to any floating point type such as double.我在示例中使用了浮点数,但同样适用于任何浮点类型,例如 double。

One solution is to use BigDecimal and whichever method you want if incurring in performance and memory hit is not an issue.一种解决方案是使用 BigDecimal 和任何您想要的方法,如果性能和内存命中不是问题。

A more precise method than comparing distances for floating points, and, more importantly, consistently precise, although at a higher computational cost, is calculating the distance to the closest point in the line.一种比比较浮点距离更精确的方法,更重要的是,虽然计算成本更高,但始终精确,是计算到直线中最近点的距离。

Shortest distance between a point and a line segment 点到线段的最短距离

It looks like I'm splitting hairs but I had to deal with this problem before.看起来我正在分裂头发,但我之前不得不处理这个问题。 It's an issue when chaining geometric operations.这是链接几何运算时的问题。 If you don't control what kind of precission loss you are dealing with, eventually you will run into difficult bugs that will force you to reason rigorously about the code in order to fix them.如果你不能控制你正在处理的精度损失类型,最终你会遇到困难的错误,这将迫使你对代码进行严格的推理以修复它们。

An easy way to do that I believe would be the check the angle formed by the 3 points.我相信一个简单的方法是检查由 3 个点形成的角度。 If the angle ACB is 180 degrees (or close to it,depending on how accurate you want to be) then the point C is between A and B.如果角度 ACB 为 180 度(或接近它,取决于您想要的准确度),则点 C 位于 A 和 B 之间。

I think this might help我认为这可能会有所帮助

How to check if a point lies on a line between 2 other points 如何检查一个点是否位于其他两个点之间的一条线上

That solution uses only integers given you only provide integers which removes some pitfalls as well该解决方案仅使用整数,因为您只提供整数,这也消除了一些陷阱

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

Here is my C# solution.这是我的 C# 解决方案。 I believe the Java equivalent will be almost identical.我相信 Java 等价物将几乎相同。

Notes:笔记:

  1. Method will only return true if the point is within the bounds of the line (it does not assume an infinite line).如果点位于线的边界内(它不假设无限线),则方法将仅返回 true。

  2. It will handle vertical or horizontal lines.它将处理垂直或水平线。

  3. It calculates the distance of the point being checked from the line so allows a tolerance to be passed to the method.它计算要检查的点与线的距离,从而允许将公差传递给该方法。

     /// <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; } }

Here is a JavaScript function I made.这是我制作的 JavaScript 函数。 You pass it three points (three objects with an x and y property).您向它传递三个点(具有 x 和 y 属性的三个对象)。 Points 1 and 2 define your line, and point 3 is the point you are testing.点 1 和 2 定义您的线,点 3 是您要测试的点。

You will receive an object back with some useful info:您将收到一个带有一些有用信息的对象:

  • on_projected_line - If pt3 lies anywhere on the line including outside the points. on_projected_line - 如果pt3位于线上的任何位置,包括点之外。
  • on_line - If pt3 lies on the line and between or on pt1 and pt2 . on_line -如果pt3谎言上线之间或上pt1pt2
  • x_between - If pt3 is between or on the x bounds. x_between - 如果pt3在 x 边界之间或在 x 边界上。
  • y_between - If pt3 is between or on the y bounds. y_between - 如果pt3在 y 边界之间或之上。
  • between - If x_between and y_between are both true. between - 如果x_betweeny_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM