求该点在线段上

本文关键字:在线 | 更新日期: 2023-09-27 18:05:22

我有两个点定义的线段:A(x1,y1,z1)B(x2,y2,z2)。我有点p(x,y,z)如何判断该点是否在线段上?

求该点在线段上

求点p到直线两端点A、b的距离。若AB = AP + PB,则点p位于线段AB上。

AB = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1));
AP = sqrt((x-x1)*(x-x1)+(y-y1)*(y-y1)+(z-z1)*(z-z1));
PB = sqrt((x2-x)*(x2-x)+(y2-y)*(y2-y)+(z2-z)*(z2-z));
if(AB == AP + PB)
    return true;

如果,则:

(x - x1) / (x2 - x1) = (y - y1) / (y2 - y1) = (z - z1) / (z2 - z1)

计算这三个值,如果它们是相同的(在一定程度上的公差),你的点就对了。

要测试点是否在线段中,而不是在行上,可以检查

x1 < x < x2, assuming x1 < x2, or
y1 < y < y2, assuming y1 < y2, or
z1 < z < z2, assuming z1 < z2

首先求AB与AP的外积,如果AB与AP共线,则外积为0。

在这一点上,它可能仍然在延伸到B之后或A之前的大线上,所以我认为你应该能够检查pz是否在az和bz之间。

这似乎是一个重复,实际上,正如其中一个答案提到的,它是在美丽的代码。

如果有人查找内联版本:

public static bool PointOnLine2D (this Vector2 p, Vector2 a, Vector2 b, float t = 1E-03f)
{
    // ensure points are collinear
    var zero = (b.x - a.x) * (p.y - a.y) - (p.x - a.x) * (b.y - a.y);
    if (zero > t || zero < -t) return false;
    // check if x-coordinates are not equal
    if (a.x - b.x > t || b.x - a.x > t)
        // ensure x is between a.x & b.x (use tolerance)
        return a.x > b.x
            ? p.x + t > b.x && p.x - t < a.x
            : p.x + t > a.x && p.x - t < b.x;
    // ensure y is between a.y & b.y (use tolerance)
    return a.y > b.y
        ? p.y + t > b.y && p.y - t < a.y
        : p.y + t > a.y && p.y - t < b.y;
}

您的段最好由参数方程定义

对于线段上的所有点

,下面的等式成立:X = x1 + (x2 - x1) * pY = y1 + (y2 - y1) * pZ = z1 + (z2 - z1) * p

其中p为[0;1]中的数字

所以,如果存在p点坐标满足这些3个方程,你的点在这条线上。如果p在0和1 -之间它也在线段

下面是一些2D情况下的c#代码:

public static bool PointOnLineSegment(PointD pt1, PointD pt2, PointD pt, double epsilon = 0.001)
{
  if (pt.X - Math.Max(pt1.X, pt2.X) > epsilon || 
      Math.Min(pt1.X, pt2.X) - pt.X > epsilon || 
      pt.Y - Math.Max(pt1.Y, pt2.Y) > epsilon || 
      Math.Min(pt1.Y, pt2.Y) - pt.Y > epsilon)
    return false;
  if (Math.Abs(pt2.X - pt1.X) < epsilon)
    return Math.Abs(pt1.X - pt.X) < epsilon || Math.Abs(pt2.X - pt.X) < epsilon;
  if (Math.Abs(pt2.Y - pt1.Y) < epsilon)
    return Math.Abs(pt1.Y - pt.Y) < epsilon || Math.Abs(pt2.Y - pt.Y) < epsilon;
  double x = pt1.X + (pt.Y - pt1.Y) * (pt2.X - pt1.X) / (pt2.Y - pt1.Y);
  double y = pt1.Y + (pt.X - pt1.X) * (pt2.Y - pt1.Y) / (pt2.X - pt1.X);
  return Math.Abs(pt.X - x) < epsilon || Math.Abs(pt.Y - y) < epsilon;
}

或者如果使用visualstudio使用GraphicsPath

让dotnet为您做繁重的工作

这也将允许您添加公差,如果只是在行外单击。

using (Drawing2D.GraphicsPath gp = new Drawing2D.GraphicsPath())
{
    gp.AddLine(new Point(x1, y1), new Point(x2, y2));
    // Make the line as wide as needed (make this larger to allow clicking slightly outside the line) 
    using (Pen objPen = new Pen(Color.Black, 6))
    {
        gp.Widen(objPen);
    }
    if (gp.IsVisible(Mouse.x, Mouse.y))
    {
        // The line was clicked
    }
}

外积(B - A) &乘以;(p - A)应该比B - A短得多。理想情况下,叉乘是零,但这在有限精度浮点硬件上是不可能的。

我用它来计算点a和点b之间的距离AB。

static void Main(string[] args)
{
        double AB = segment(0, 1, 0, 4);
        Console.WriteLine("Length of segment AB: {0}",AB);
}
static double segment (int ax,int ay, int bx, int by)
{
    Vector a = new Vector(ax,ay);
    Vector b = new Vector(bx,by);
    Vector c = (a & b);
    return Math.Sqrt(c.X + c.Y);
}
struct Vector
{
    public readonly float X;
    public readonly float Y;
    public Vector(float x, float y)
    {
        this.X = x;
        this.Y = y;
    }
    public static Vector operator &(Vector a, Vector b)  
    {
        return new Vector((b.X - a.X) * (b.X - a.X), (b.Y - a.Y) * (b.Y - a.Y));
    }
}
在离a给定距离处沿a - b直线计算一个点

设V1为向量(B-A), V2 = (p- a),对V1和V2进行归一化。

如果V1==(-V2),则点p在直线上,但在A之前,&因此不在片段中。如果V1==V2点p在直线上。获取(p- a)的长度并检查它是否小于或等于(B-A)的长度,如果是,则该点在线段上,否则它在b的后面。

这是我可以在WPF中运行的代码

    public static class Math2DExtensions
    {
        public static bool CheckIsPointOnLineSegment(Point point, Line line, double epsilon = 0.1)
        {
            // Thank you @Rob Agar           
            // (x - x1) / (x2 - x1) = (y - y1) / (y2 - y1)
            // x1 < x < x2, assuming x1 < x2
            // y1 < y < y2, assuming y1 < y2          
            var minX = Math.Min(line.APoint.X, line.BPoint.X);
            var maxX = Math.Max(line.APoint.X, line.BPoint.X);
            var minY = Math.Min(line.APoint.Y, line.BPoint.Y);
            var maxY = Math.Max(line.APoint.Y, line.BPoint.Y);
            if (!(minX <= point.X) || !(point.X <= maxX) || !(minY <= point.Y) || !(point.Y <= maxY))
            {
                return false;
            }
            
            if (Math.Abs(line.APoint.X - line.BPoint.X) < epsilon)
            {
                return Math.Abs(line.APoint.X - point.X) < epsilon || Math.Abs(line.BPoint.X - point.X) < epsilon;
            }
            if (Math.Abs(line.APoint.Y - line.BPoint.Y) < epsilon)
            {
                return Math.Abs(line.APoint.Y - point.Y) < epsilon || Math.Abs(line.BPoint.Y - point.Y) < epsilon;
            }
            if (Math.Abs((point.X - line.APoint.X) / (line.BPoint.X - line.APoint.X) - (point.Y - line.APoint.Y) / (line.BPoint.Y - line.APoint.Y)) < epsilon)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    public record Line
    {
        public Point APoint { get; init; }
        public Point BPoint { get; init; }
    }
我的代码在github

感谢@Rob Agar和@MetaMapper

您可以检查该点是否位于point1和point2定义的两个平面之间以及直线方向:

///  Returns the closest point from @a point to this line on this line.
vector3 <Type>
line3d <Type>::closest_point (const vector3 <Type> & point) const
{
    return this -> point () + direction () * dot (point - this -> point (), direction ());
}
///  Returns true if @a point lies between point1 and point2.
template <class Type>
bool
line_segment3 <Type>::is_between (const vector3 <Type> & point) const
{
    const auto closest = line () .closest_point (point);
    return abs ((closest - point0 ()) + (closest - point1 ())) <= abs (point0 () - point1 ());
}