How can I tell if a point belongs to a certain line?

前端 未结 11 2168
你的背包
你的背包 2020-12-01 08:22

How can I tell if a point belongs to a certain line?

Examples are appreciated, if possible.

相关标签:
11条回答
  • 2020-12-01 08:35

    Given two points on the line L0 and L1 and the point to test P.

                   (L1 - L0) * (P - L0)
    n = (P - L0) - --------------------- (L1 - L0)
                   (L1 - L0) * (L1 - L0)
    

    The norm of the vector n is the distance of the point P from the line through L0 and L1. If this distance is zero or small enough (in the case of rounding errors), the point lies on the line.

    The symbol * represents the dot product.

    Example

    P = (5, 5)
    
    L0 = (0, 10)
    L1 = (20, -10)
    
    L1 - L0 = (20, -20)
    P  - L0 = (5, -5)
    
                  (20, -20) * (5, -5)
    n = (5, -5) - --------------------- (20, -20)
                  (20, -20) * (20, -20)
    
                  200
      = (5, -5) - --- (20, -20)
                  800
    
      = (5, -5) - (5, -5)
    
      = (0, 0)
    
    0 讨论(0)
  • 2020-12-01 08:43

    I just wrote an function which handles a few extra requirements since I use this check in a drawing application:

    • Fuzziness - There must be some room for error since the function is used to select lines by clicking on them.
    • The line got an EndPoint and a StartPoint, no infinite lines.
    • Must handle straight vertical and horizontal lines, (x2 - x1) == 0 causes division by zero in the other answers.
    private const double SELECTION_FUZZINESS = 3;
    
    internal override bool ContainsPoint(Point point)
    {
        LineGeometry lineGeo = geometry as LineGeometry;
        Point leftPoint;
        Point rightPoint;
    
        // Normalize start/end to left right to make the offset calc simpler.
        if (lineGeo.StartPoint.X <= lineGeo.EndPoint.X)
        {
            leftPoint   = lineGeo.StartPoint;
            rightPoint  = lineGeo.EndPoint;
        }
        else
        {
            leftPoint   = lineGeo.EndPoint;
            rightPoint  = lineGeo.StartPoint;
        }
    
        // If point is out of bounds, no need to do further checks.                  
        if (point.X + SELECTION_FUZZINESS < leftPoint.X || rightPoint.X < point.X - SELECTION_FUZZINESS)
            return false;
        else if (point.Y + SELECTION_FUZZINESS < Math.Min(leftPoint.Y, rightPoint.Y) || Math.Max(leftPoint.Y, rightPoint.Y) < point.Y - SELECTION_FUZZINESS)
            return false;
    
        double deltaX = rightPoint.X - leftPoint.X;
        double deltaY = rightPoint.Y - leftPoint.Y;
    
        // If the line is straight, the earlier boundary check is enough to determine that the point is on the line.
        // Also prevents division by zero exceptions.
        if (deltaX == 0 || deltaY == 0) 
            return true;
    
        double slope        = deltaY / deltaX;
        double offset       = leftPoint.Y - leftPoint.X * slope;
        double calculatedY  = point.X * slope + offset;
    
        // Check calculated Y matches the points Y coord with some easing.
        bool lineContains = point.Y - SELECTION_FUZZINESS <= calculatedY && calculatedY <= point.Y + SELECTION_FUZZINESS;
    
        return lineContains;            
    }
    
    0 讨论(0)
  • 2020-12-01 08:43

    If you have a line defined by its endpoints

    PointF pt1, pt2;
    

    and you have a point that you want to check

    PointF checkPoint;
    

    then you could define a function as follows:

    bool IsOnLine(PointF endPoint1, PointF endPoint2, PointF checkPoint) 
    {
        return (checkPoint.Y - endPoint1.Y) / (endPoint2.Y - endPoint1.Y)
            == (checkPoint.X - endPoint1.X) / (endPoint2.X - endPoint1.X);
    }
    

    and call it as follows:

    if (IsOnLine(pt1, pt2, checkPoint) {
        // Is on line
    }
    

    You will need to check for division by zero though.

    0 讨论(0)
  • 2020-12-01 08:44

    As an alternative to the slope/y-intercept method, I chose this approach using Math.Atan2:

    // as an extension method
    public static bool Intersects(this Vector2 v, LineSegment s) {
        //  check from line segment start perspective
        var reference = Math.Atan2(s.Start.Y - s.End.Y, s.Start.X - s.End.X);
        var aTanTest = Math.Atan2(s.Start.Y - v.Y, s.Start.X - v.X);
    
        //  check from line segment end perspective
        if (reference == aTanTest) {
            reference = Math.Atan2(s.End.Y - s.Start.Y, s.End.X - s.Start.X);
            aTanTest = Math.Atan2(s.End.Y - v.Y, s.End.X - v.X);
        }
    
        return reference == aTanTest;
    }
    

    The first check reference determines the arcTan from the start point of the line segment to it's end-point. Then from the start point perspective, we determine the arcTan to the vector v.

    If those values are equal, we check from the perspective of the end-point.

    Simple and handles horizontal, vertical and all else in between.

    0 讨论(0)
  • 2020-12-01 08:47

    The best way to determine if a point R = (rx, ry) lies on the line connecting points P = (px, py) and Q = (qx, qy) is to check whether the determinant of the matrix

    {{qx - px, qy - py}, {rx - px, ry - py}},
    

    namely (qx - px) * (ry - py) - (qy - py) * (rx - px) is close to 0. This solution has several related advantages over the others posted: first, it requires no special case for vertical lines, second, it doesn't divide (usually a slow operation), third, it doesn't trigger bad floating-point behavior when the line is almost, but not quite vertical.

    0 讨论(0)
  • 2020-12-01 08:49

    Could you be more specific?

    What programming language are you talking about?

    What environment are you talking about?

    What "lines" are you talking about? Text? What point? XY on the screen?

    0 讨论(0)
提交回复
热议问题