【问题标题】:How can I tell if a point belongs to a certain line?如何判断一个点是否属于某条线?
【发布时间】:2010-10-28 18:54:57
【问题描述】:

如何判断一个点是否属于某条线?

如有可能,欢迎提供示例。

【问题讨论】:

  • 请更具体。您必须从哪些信息开始?你有点和方程的有序对吗?

标签: c# .net algorithm gdi+ line


【解决方案1】:

在最简单的形式中,只需将坐标插入直线方程并检查是否相等。

给定:

Point p (X=4, Y=5)
Line l (Slope=1, YIntersect=1)

插入 X 和 Y:

   Y = Slope * X + YIntersect
=> 5 = 1 * 4 + 1
=> 5 = 5

所以是的,重点就在这条线上。

如果您的线以 (X1,Y1),(X2,Y2) 形式表示,那么您可以使用以下公式计算斜率:

 Slope = (y1 - y2) / (x1-x2)

然后用这个得到 Y-Intersect:

 YIntersect = - Slope * X1 + Y1;

编辑:我修复了 Y 相交(一直是 X1 / Y1 ...)

您必须检查x1 - x2 不是0。如果是,那么检查点是否在线很简单,检查点中的 Y 值是否等于x1x2。另外,检查点的 X 不是“x1”或“x2”。

【讨论】:

  • 这是什么语言库?
  • 我会考虑将 EDIT: 对 Y-Intersect 公式的更正移到原始错误版本之上。仔细阅读后才注意到这一点。
  • 最简单的方法是将片段起点和终点的Math.Atan2结果与主题点进行比较。请参阅下面的答案以获取示例。不用担心slope-intercept 方法提供的水平或垂直问题或在零之前有多接近零
【解决方案2】:

作为slope/y-intercept 方法的替代方法,我选择了使用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;
}

第一次检查reference 确定从线段起点到终点的 arcTan。 然后从起点角度,我们确定arcTan到向量v

如果这些值相等,我们从端点的角度进行检查。

简单并处理水平、垂直和介于两者之间的所有其他内容。

【讨论】:

    【解决方案3】:

    给定线上的两个点L0L1 以及要测试的点P

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

    向量n 的范数是点P 到通过L0L1 的直线的距离。如果此距离为零或足够小(在舍入误差的情况下),则该点位于直线上。

    符号*代表点积。

    示例

    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)
    

    【讨论】:

    • +1 用于提及舍入错误。在浮点算术中使用精确相等会导致其他建议的解决方案在很多情况下失败。我不确定所提出算法的数值稳健性,但数值稳健性足够复杂,如果精度很重要,那么建议查看有关该主题的科学文献。或者至少使用作者可能已经完成研究的图书馆。
    • 我不认为你的例子是正确的,因为经过一些转换n = (p - L0) - (p - L0) 并且在每种情况下你都会得到n = (0, 0)
    【解决方案4】:

    我刚刚编写了一个函数来处理一些额外的要求,因为我在绘图应用程序中使用了这个检查:

    • 模糊性 - 由于该功能用于通过单击选择行来选择行,因此肯定存在错误空间。
    • 这条线有一个 EndPoint 和一个 StartPoint,没有无限的线。
    • 必须处理垂直和水平直线,(x2 - x1) == 0 会导致在其他答案中除以零。
    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;            
    }
    

    【讨论】:

    • 为什么这不是公认的答案?所有其他的只是数学和废话。这是一个真实的世界,久经沙场的功能,应该是首选。我的意思是看在上帝的份上,这是 StackOverflow,而不是 MathOverflow。
    • 这是最好的答案,谢谢它的工作原理。但是 SELECTION_FUZZINESS 的最佳价值是多少??
    • @shakil.k,SELECTION_FUZZINESS 对应于您的线宽。值越小,准确率越高
    【解决方案5】:

    如果你有一条由它的端点定义的线

    PointF pt1, pt2;
    

    你有一点要检查

    PointF checkPoint;
    

    那么你可以定义一个函数如下:

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

    并按如下方式调用它:

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

    不过,您需要检查除以零。

    【讨论】:

    • 这不可能...由于点坐标是整数,因此当检查点靠近 endPoint1 且远离 endPoint2 时,您将有(严重的)精度损失。也许如果你把它改成十进制或双倍,它对双方都很好,但我仍然不相信这个等式的准确性。
    • Fair Point(双关语),将它们更改为 PointF
    • 还有两个问题:你没有检查行尾,所以根据这个函数,(4,6)会在(1,2)和(3,4)之间,并且存在精度问题 - 假设一条线从 (1,100) 到 (2,200)。中间没有一个带有整数坐标的点 - 对于整数坐标,检查总是错误的。
    • java.lang.ArithmeticException: 除以零 - 不行
    • 由于算法只检查斜率,平行线会给出假阳性结果
    【解决方案6】:

    我认为帕特里克·麦克唐纳先生给出了几乎正确的答案,这是对他答案的更正:

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

    当然还有很多其他的正确答案,尤其是 Mr.Josh,但我发现这是最好的答案。

    感谢大家。

    【讨论】:

    • 如果 checkPoint.x == endPoint.x 或端点具有相同的 x 值,则为您提供一个 0 的 div
    【解决方案7】:

    判断一个点 R = (rx, ry) 是否在连接点 P = (px, py) 和 Q = (qx, qy) 的线上的最好方法是检查矩阵的行列式

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

    即 (qx - px) * (ry - py) - (qy - py) * (rx - px) 接近于 0。与其他发布的解决方案相比,此解决方案具有几个相关优势:首先,它不需要特殊情况对于垂直线,第二,它不会分割(通常是一个缓慢的操作),第三,当线几乎垂直但不完全垂直时,它不会触发不良的浮点行为。

    【讨论】:

    • 对于从 0,0 到 10,10 且点为 5.1、5.1 的线,行列式为零。但重点不在这条线上。
    • “接近 0”​​究竟是什么意思?
    • 这是一个比“接受”更好的答案。唯一缺少的是“接近”的定义。这必须在被减去的数字的背景下理解:因为有 5 次减法,所以有 5 次“显着损失精度”的机会,这意味着实际上很难对“接近”做出一个好的规范。
    • @Andy:再想一想——从(0,0)(10,10) 的线可以用方程y = x 来描述,所有解这个方程的点都在这条线上。 (5.1, 5.1) 解方程,因此位于线上。
    【解决方案8】:

    二维线通常使用两个变量 x 和 y 的方程来表示,这里是一个众所周知的方程

    现在想象您的 GDI+ 线是从 (0,0) 到 (100, 100) 绘制的,那么 m=(0-100)/(0-100) = 1 的值因此您的线的等式是 y- 0=1*(x-0) => y=x

    现在我们已经有了一个关于这条线的方程,它很容易测试一个点是否属于这条线。当您替换 x=x3 和 y=y3 时,如果给定点 (x3, y3) 满足直线方程,则它属于该直线。例如点 (10, 10) 属于这条线,因为 10=10 但 (10,12) 不属于这条线,因为 12 != 10。

    注意:对于垂直线,斜率 (m) 的值是无限的,但对于这种特殊情况,您可以直接使用垂直线的方程 x=c,其中 c = x1 = x2。

    虽然我不得不说我不确定这是否是最有效的方法。当我手头有更多时间时,我会尝试找到更有效的方法。

    希望这会有所帮助。

    【讨论】:

      【解决方案9】:

      直线方程为:

      y = mx + c
      

      所以如果一个点(a,b)满足这个方程,即b = ma + c

      ,它就在这条线上

      【讨论】:

        【解决方案10】:
        y = m * x + c
        

        这是一条线的方程。 x & y 是坐标。每条线的特征在于其斜率 (m ) 以及与 y 轴 (c) 相交的位置。

        因此,给定一条线的 m & c,您可以通过检查等式是否适用于 x = x1 和 y = y1 来确定点 (x1, y1) 是否在线上

        【讨论】:

        • 除了这个方程不能描述一条垂直线,而且你没有提到这条线有非零粗细的可能性。
        • "一条线没有粗细" -- 在屏幕上绘制时(即编程题时)会这样:它的粗细至少为一个像素,可能更多。
        • 我认为一条线的厚度为 1 像素(当绘制到屏幕上时),它适用于这个等式。如果你想知道一个点是否在一条 X 粗细的线上,你实际上是在问一个点是否在一个矩形中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-19
        • 1970-01-01
        • 2010-12-06
        相关资源
        最近更新 更多