How to check if a point lies on a line between 2 other points

Viewed 43428

How would I write this function? Any examples appreciated

function isPointBetweenPoints(currPoint, point1, point2):Boolean {

    var currX = currPoint.x;
    var currY = currPoint.y;

    var p1X = point1.x;
    var p1y = point1.y;

    var p2X = point2.x;
    var p2y = point2.y;

    //here I'm stuck
}
8 Answers

I'll use Triangle approach: Triangle approach

First, I'll check the Area, if the Area is close to 0, then the Point lies on the Line.

But think about the case where the length of AC is so great, then the Area increases far from 0, but visually, we still see that B is on AC: that when we need to check the height of the triangle.

To do this, we need to remember the formula we learn from first grade: Area = Base * Height / 2

Here is the code:

    bool Is3PointOn1Line(IList<Vector2> arrVert, int idx1, int idx2, int idx3)
    {
        //check if the area of the ABC triangle is 0:
        float fArea = arrVert[idx1].x * (arrVert[idx2].y - arrVert[idx3].y) +
            arrVert[idx2].x * (arrVert[idx3].y - arrVert[idx1].y) +
            arrVert[idx3].x * (arrVert[idx1].y - arrVert[idx2].y);
        fArea = Mathf.Abs(fArea);
        if (fArea < SS.EPSILON)
        {
            //Area is zero then it's the line
            return true;
        }
        else
        {
            //Check the height, in case the triangle has long base
            float fBase = Vector2.Distance(arrVert[idx1], arrVert[idx3]);
            float height = 2.0f * fArea / fBase;
            return height < SS.EPSILON;
        }
    }

Usage:

Vector2[] arrVert = new Vector2[3];

arrVert[0] = //...
arrVert[1] = //...
arrVert[2] = //...

if(Is3PointOn1Line(arrVert, 0, 1, 2))
{
    //Ta-da, they're on same line
}

PS: SS.EPSILON = 0.01f and I use some function of Unity (for ex: Vector2.Distance), but you got the idea.

Ready for what seems to be infinitely simpler than some of these other solutions?

You pass it three points (three objects with an x and y property). Points 1 and 2 define your line, and point 3 is the point you are testing.

function pointOnLine(pt1, pt2, pt3) {
    const dx = (pt3.x - pt1.x) / (pt2.x - pt1.x);
    const dy = (pt3.y - pt1.y) / (pt2.y - pt1.y);
    const onLine = dx === dy

    // Check on or within x and y bounds
    const betweenX = 0 <= dx && dx <= 1;
    const betweenY = 0 <= dy && dy <= 1;

    return onLine && betweenX && betweenY;
}

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

Edit: Simplified further according to RBarryYoung's observation.

This approach is similar to Steve's approach, just shorter and improved to use as little memory and process power as possible. But first the mathematical idea:

Let a, b be the ends of the line, ab the difference between them and p the point to check. Then p is exactly then on the line, if

a + i * ab = p

with i being a number in the interval [0;1] representing the index on the line. We can write that as two separate equations (for 2D):

a.x + i * ab.x = p.x
a.y + i * ab.y = p.y
⇔
i = (p.x - a.x) / ab.x
i = (p.y - a.y) / ab.y

Which gives us to requirements for p to be on the line from a to b:

(p.x - a.x) / ab.x = (p.y - a.y) / ab.y
and
0 ≤ i ≤ 1

In code:

function onLine(a, b, p) {
    var i1 = (p.x - a.x) / (b.x - a.x), i2 = (p.y - a.y) / (b.y - a.y);
    return i1 == i2 && i1 <= 0 && i1 >= 1;
}

Technically you could even inline i2 but that makes it even harder to read.

  private static boolean pointInLine(int2 lineBegin, int2 lineEnd, int2 point)
  { boolean result = false;
    
    int2 b = int2(min(lineBegin.x, lineEnd.x), min(lineBegin.y, lineEnd.y));
    int2 e = int2(max(lineBegin.x, lineEnd.x), max(lineBegin.y, lineEnd.y));
    if (point.x >= b.x && point.x <= e.x &&
        point.y >= b.y && point.y <= e.y)
    { int2 normal = lineEnd.sub(lineBegin).perp();
      int2 d0     = lineBegin.sub(point);
      if (d0.dot(normal) == 0x0)
      { result = true;
      }
    }
    return result;
  }

This works for any slope, even if lineBegin == lineEnd == point.

  • First, create two points b and e, which ensures b <= e. (This is two support lines that have a negative slope) Check if point lands on the box (inclusive) created by those two points.
  • Then, get the normal of the line, which is a vector perpendicular to it. You can do this by negating the x and transposing, x,y --> y, -x.
  • Then you can simply create a vector that points to the line from the point or to the point from the line, it doesn't matter and doesn't have to be from the center of the line. Once you do that check if the vector is perpendicular to the normal by getting the dot product of the normal and the point vector.

It also makes it a bit easier on you if you've got some sort of math lib or at least a struct with x,y components. But you can do this with scalar components aswell. But getting the dot product and the normal are very simple calculations.

Here's the dot product: .dot(int2 v) = (x * v.x + y * v.y)

Here's the perpendicular: .perp() = new int2(y, -x)

.sub() .add() Do what you'd expect and params are in the same order.

Related