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

时间:2009-05-25 16:54:56

标签: c# .net algorithm gdi+ line

如何确定一个点是否属于某一行?

如果可能的话,我们很感激。

11 个答案:

答案 0 :(得分:28)

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

假设:

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-Intersect(已经是X1 / Y1 ......)

您必须检查x1 - x2是否不是0。如果是,则检查点是否在线是一个简单的问题,即检查点中的Y值是否等于x1x2。另外,检查点的X不是'x1'或'x2'。

答案 1 :(得分:23)

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

  • 模糊 - 必须有一些错误的余地,因为该功能用于通过点击选择线条。
  • 这条线有一个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;            
}

答案 2 :(得分:19)

确定点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.此解决方案与其他方案相比具有几个相关优势:首先,它不需要特殊情况对于垂直线,第二,它不分(通常是慢速操作),第三,当线几乎但不完全垂直时,它不会触发不良的浮点行为。

答案 3 :(得分:6)

在行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)

答案 4 :(得分: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,但我发现这是最好的答案。

感谢evryone。

答案 5 :(得分:4)

y = m * x + c

这是一条线的等式。 x&amp; y是坐标。每条线的特征在于其斜率(m)和与y轴(c)相交的位置。

所以给予m&amp; c对于一条线,您可以通过检查方程是否适用于x = x1和y = y1

来确定点(x1,y1)是否在线上

答案 6 :(得分:3)

如果您的端点定义了一行

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
}

你需要检查除以零。

答案 7 :(得分:2)

2D线通常使用两个变量x和y中的等式来表示,这里是一个众所周知的等式

y-y1 = (y1-y2)/(x1-x2) (x-x1)

现在想象你的GDI +线是从(0,0)到(100,100),然后是m =(0-100)/(0-100)= 1的值,因此你的线的等式是y- 0 = 1 *(x-0)=&gt; Y = X

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

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

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

希望这有帮助。

答案 8 :(得分:1)

该行的等式是:

y = mx + c

如果满足这个等式,即b = ma + c

,那么点(a,b)就在这一行上

答案 9 :(得分:0)

你能更具体一点吗?

你在谈论什么编程语言?

你在说什么环境?

你在谈论什么“线条”?文本?什么点?屏幕上的XY?

答案 10 :(得分:0)

作为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

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

简单并处理水平,垂直和其他所有内容。