如何检测两个线段相交的位置?

时间:2009-02-18 22:47:18

标签: geometry line-intersection

如何确定两条线是否相交,如果它们相交,在x,y点处是什么?

27 个答案:

答案 0 :(得分:662)

对于使用矢量交叉产品的这个问题,有一个很好的方法。将二维向量叉积 v × w 定义为 v x w <子>ý - <强> v <子>ý瓦特 <子> X

假设两个线段从 p p + r ,从 q q + <强>取值即可。然后第一行上的任何一点都可以表示为 p + t r (对于标量参数 t )和第二行上的任何一点为 q + u s (对于标量参数 u )。

Two line segments intersecting

如果我们能找到 t u 这两条线相交:

  

p + t r = q + u s

Formulae for the point of intersection

s 交叉双方,获取

  

p + t r )× s =( q + s )× s

由于 s × s = 0,这意味着

  

t r × s )=( q - p ) × s

因此,解决 t

  

t =( q - p )× s /( r × s

以同样的方式,我们可以解决 u

  

p + t r )× r =( q + s )× r

     

u s × r )=( p - q ) × r

     

u =( p - q )× r /( s × r

为了减少计算步骤的数量,可以方便地将其重写如下(记住 s × r = - r ×<强>取值):

  

u =( q - p )× r /( r × s

现在有四种情况:

  1. 如果 r × s = 0且( q - p )× r = 0,则两条线共线。

    在这种情况下,根据第一个方程式表示第二个分段( q q + s )的端点线段( p + t r ):

      

    t 0 =( q - p )· r /(的 - [R ·<强> - [R

         

    t 1 =( q + s - p )·< strong> r /( r · r )= t 0 + s < / strong>· r /( r · r

    如果 t 0 t 1 之间的间隔与区间[0,1]相交,那么线段共线且重叠;否则它们是共线的和不相交的。

    请注意,如果 s r 指向相反方向,则 s · r &lt; 0,所以要检查的间隔是[ t 1 t 0 ]而不是[ t 0 t 1 ]。

  2. 如果 r × s = 0且( q - p )× r ≠0,则两条线平行且不相交。

  3. 如果 r × s ≠0且0≤ t ≤1且0≤ u ≤1,两个线段在 p + t r = q + u点相遇 <强>取值

  4. 否则,两个线段不平行但不相交。

  5. 信用:此方法是Ronald Goldman在 Graphics Gems 中发表的文章“三空间两条线的交点”中的3D线交叉算法的二维特化,第304页在三个维度中,通常的情况是线条是倾斜的(既不平行也不相交),在这种情况下,该方法给出了两条线最接近的点。

答案 1 :(得分:230)

FWIW,以下函数(在C中)都检测线交点并确定交点。它基于Andre LeMothe的“Tricks of the Windows Game Programming Gurus”算法。它与其他答案中的某些算法(例如Gareth's)没有什么不同。 LeMothe然后使用Cramer的规则(不要问我)自己解决方程式。

我可以证明它在我的微弱小行星克隆中起作用,并且似乎正确处理Elemental,Dan和Wodzu在其他答案中描述的边缘情况。它也可能比KingNestor发布的代码更快,因为它是所有的乘法和除法,没有平方根!

我想在那里有一些除以零的可能性,尽管在我的情况下这不是一个问题。很容易修改,以避免崩溃。

// Returns 1 if the lines intersect, otherwise 0. In addition, if the lines 
// intersect the intersection point may be stored in the floats i_x and i_y.
char get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s1_x, s1_y, s2_x, s2_y;
    s1_x = p1_x - p0_x;     s1_y = p1_y - p0_y;
    s2_x = p3_x - p2_x;     s2_y = p3_y - p2_y;

    float s, t;
    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
    {
        // Collision detected
        if (i_x != NULL)
            *i_x = p0_x + (t * s1_x);
        if (i_y != NULL)
            *i_y = p0_y + (t * s1_y);
        return 1;
    }

    return 0; // No collision
}
顺便说一句,我必须说,在LeMothe的书中,虽然他显然得到了正确的算法,但他所展示的具体例子插错了数字并且计算错误。例如:

  

(4 *(4 - 1)+ 12 *(7 - 1))/(17 * 4 + 12 * 10)

     

= 844 / 0.88

     

= 0.44

让我感到困惑的是小时。 :(

答案 2 :(得分:64)

问题归结为这个问题:从A到B和从C到D的两条线是否相交?然后你可以问四次(在直线和矩形的四边之间)。

这是执行此操作的矢量数学。我假设从A到B的线是有问题的线,从C到D的线是矩形线之一。我的记法是Ax是“A的x坐标”,Cy是“C的y坐标”。并且“*”表示点积,例如A*B = Ax*Bx + Ay*By

E = B-A = ( Bx-Ax, By-Ay )
F = D-C = ( Dx-Cx, Dy-Cy ) 
P = ( -Ey, Ex )
h = ( (A-C) * P ) / ( F * P )

这个h号码是关键。如果h介于01之间,则相交,否则不会。如果F*P为零,当然你不能进行计算,但在这种情况下,这些线是平行的,因此只在明显的情况下相交。

确切的交点是C + F*h

更有趣:

如果h 完全 01,那么这些线会触及一个终点。您可以认为这是一个“交叉点”或不适合您。

具体来说,h是你需要多少乘以线的长度才能与另一条线完全接触。

因此,如果h<0,则表示矩形线在给定线的“后面”(“方向”为“从A到B”),如果h>1,则矩形线是“在给定线的前面。

<强>推导:

A和C是指向行开头的向量; E和F是形成该线的A和C末端的向量。

对于平面中的任何两条非平行线,必须只有一对标量gh,以便该等式成立:

A + E*g = C + F*h

为什么呢?因为两条非平行线必须相交,这意味着您可以将每条线都按比例缩放两条线并相互接触。

起初这看起来像是一个有两个未知数的方程式!但是当你认为这是一个二维矢量方程时,并不是这意味着这实际上是{{} {1}}和x。)

我们必须消除其中一个变量。一种简单的方法是将y项设为零。要做到这一点,请使用一个向量与零点对齐的向量取等式两边的点积。我在上面调用了E向量,我做了E的明显变换。

你现在有:

P

答案 3 :(得分:46)

我试图实现Jason上面所描述的优雅算法;不幸的是,在通过调试中的数学工作时,我发现很多情况都不起作用。

例如,考虑点A(10,10)B(20,20)C(10,1)D(1,10)给出h = .5但是通过检查可以清楚地看出这些段不是 - 彼此靠近的地方。

图示这清楚地表明0 <0。 h&lt; 1个标准仅表示如果存在,则拦截点将位于CD上,但不会告知该点是否位于AB上。 为确保存在交叉点,必须对变量g进行对称计算,并且拦截要求为: 0&lt; g&lt; 1和0&lt; h&lt; 1

答案 4 :(得分:45)

这是对Gavin答案的改进。 marcp的解决方案也类似,但都没有推迟该部门。

这实际上也是Gareth Rees回答的实际应用,因为2D中的交叉产品是perp-dot-product,这是该代码使用的三个。切换到3D并使用交叉积,在末尾插入s和t,得到3D中线之间的两个最接近的点。 无论如何,2D解决方案:

int get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s02_x, s02_y, s10_x, s10_y, s32_x, s32_y, s_numer, t_numer, denom, t;
    s10_x = p1_x - p0_x;
    s10_y = p1_y - p0_y;
    s32_x = p3_x - p2_x;
    s32_y = p3_y - p2_y;

    denom = s10_x * s32_y - s32_x * s10_y;
    if (denom == 0)
        return 0; // Collinear
    bool denomPositive = denom > 0;

    s02_x = p0_x - p2_x;
    s02_y = p0_y - p2_y;
    s_numer = s10_x * s02_y - s10_y * s02_x;
    if ((s_numer < 0) == denomPositive)
        return 0; // No collision

    t_numer = s32_x * s02_y - s32_y * s02_x;
    if ((t_numer < 0) == denomPositive)
        return 0; // No collision

    if (((s_numer > denom) == denomPositive) || ((t_numer > denom) == denomPositive))
        return 0; // No collision
    // Collision detected
    t = t_numer / denom;
    if (i_x != NULL)
        *i_x = p0_x + (t * s10_x);
    if (i_y != NULL)
        *i_y = p0_y + (t * s10_y);

    return 1;
}

基本上它将分组推迟到最后一刻,并将大部分测试移动到某些计算完成之前,从而增加早期出局。最后,它还避免了在线条平行时发生零分割的情况。

您也可以考虑使用epsilon测试而不是与零进行比较。非常接近平行的线可以产生稍微偏离的结果。这不是一个错误,它是浮点数学的一个限制。

答案 5 :(得分:40)

问题C:如何检测两个线段是否相交?

我搜索了相同的主题,我对答案不满意。所以我写了一篇文章,用很多图片解释了非常详细的how to check if two line segments intersect。有完整的(和经过测试的)Java代码。

这篇文章被裁剪为最重要的部分:

检查线段a是否与线段b相交的算法如下所示:

Enter image description here

什么是边界框?这是两个线段的两个边界框:

enter image description here

如果两个边界框都有一个交点,则移动线段a使得一个点位于(0 | 0)。现在你有一条线穿过a定义的原点。现在以相同的方式移动线段b并检查线段b的新点是否在线a的不同侧。如果是这种情况,请反过来检查。如果是这种情况,则线段相交。如果没有,它们就不相交了。

问题A:两个线段在哪里相交?

你知道两个线段a和b相交。如果您不知道,请使用我在“问题C”中给您的工具进行检查。

现在你可以通过一些案例来获得7年级数学的解决方案(见code and interactive example)。

问题B:如何检测两条线是否相交?

我们说你的观点A = (x1, y1),点B = (x2, y2)C = (x_3, y_3)D = (x_4, y_4)。 你的第一行由AB(A!= B)定义,第二行由CD定义(C!= D)。

function doLinesIntersect(AB, CD) {
    if (x1 == x2) {
        return !(x3 == x4 && x1 != x3);
    } else if (x3 == x4) {
        return true;
    } else {
        // Both lines are not parallel to the y-axis
        m1 = (y1-y2)/(x1-x2);
        m2 = (y3-y4)/(x3-x4);
        return m1 != m2;
    }
}

问题D:两条线在哪里相交?

检查问题B是否相交。

线a和b由每条线的两个点定义。 您基本上可以应用问题A中使用的相同逻辑。

答案 6 :(得分:21)

这里接受的答案是不正确的(从那时起它就不被接受了,所以万岁!)。它没有正确消除所有非交叉点。通常情况下它可能会起作用,但它可能会失败,尤其是在0和1被认为对h有效的情况下。

考虑以下情况:

(4,1) - (5,1)和(0,0) - (0,2)

处的线

这些是垂直线,显然不重叠。

A =(4,1)
B =(5,1)
C =(0,0)
d =(0,2)
E =(5,1) - (4,1)=( - 1,0)
F =(0,2) - (0,0)=(0,-2)
P =(0,1)
h =((4,1) - (0,0))点(0,1)/((0,-2)点(0,1))= 0

根据上述答案,这两个线段在端点处相交(值为0和1)。该端点将是:

(0,0)+(0,-2)* 0 =(0,0)

所以,显然两条线段在(0,0)处相遇,这是在线CD上,但不在线AB上。出了什么问题?答案是0和1的值无效,只有HAPPEN才能正确预测端点交叉。当一条线(但不是另一条线)的延伸线符合线段时,算法会预测线段的交叉点,但这不正确。我想通过测试从AB对CD开始,然后再用CD对AB进行测试,这个问题就会被消除。只有当两者都介于0和1之间时才能说它们相交。

如果您必须预测终点,我建议使用矢量叉积法。

-Dan

答案 7 :(得分:14)

iMalc答案的Python版本:

def find_intersection( p0, p1, p2, p3 ) :

    s10_x = p1[0] - p0[0]
    s10_y = p1[1] - p0[1]
    s32_x = p3[0] - p2[0]
    s32_y = p3[1] - p2[1]

    denom = s10_x * s32_y - s32_x * s10_y

    if denom == 0 : return None # collinear

    denom_is_positive = denom > 0

    s02_x = p0[0] - p2[0]
    s02_y = p0[1] - p2[1]

    s_numer = s10_x * s02_y - s10_y * s02_x

    if (s_numer < 0) == denom_is_positive : return None # no collision

    t_numer = s32_x * s02_y - s32_y * s02_x

    if (t_numer < 0) == denom_is_positive : return None # no collision

    if (s_numer > denom) == denom_is_positive or (t_numer > denom) == denom_is_positive : return None # no collision


    # collision detected

    t = t_numer / denom

    intersection_point = [ p0[0] + (t * s10_x), p0[1] + (t * s10_y) ]


    return intersection_point

答案 8 :(得分:11)

找到两个线段的正确交集是一项非常重要的任务,有很多边缘情况。这是一个用Java编写的经过充分记录,工作和测试的解决方案。

实质上,找到两个线段的交集时会发生三件事:

  1. 段不相交

  2. 有一个独特的交叉点

  3. 交叉点是另一个细分

  4. 注意:在代码中,我假设x1 = x2和y1 = y2的线段(x1,y1),(x2,y2)是有效的线段。从数学上讲,线段由不同的点组成,但我允许段在此实现中是完整性的点。

    代码来自我的github repo

    /**
     * This snippet finds the intersection of two line segments.
     * The intersection may either be empty, a single point or the
     * intersection is a subsegment there's an overlap.
     */
    
    import static java.lang.Math.abs;
    import static java.lang.Math.max;
    import static java.lang.Math.min;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class LineSegmentLineSegmentIntersection {
    
      // Small epsilon used for double value comparison.
      private static final double EPS = 1e-5;
    
      // 2D Point class.
      public static class Pt {
        double x, y;
        public Pt(double x, double y) {
          this.x = x; 
          this.y = y;
        }
        public boolean equals(Pt pt) {
          return abs(x - pt.x) < EPS && abs(y - pt.y) < EPS;
        }
      }
    
      // Finds the orientation of point 'c' relative to the line segment (a, b)
      // Returns  0 if all three points are collinear.
      // Returns -1 if 'c' is clockwise to segment (a, b), i.e right of line formed by the segment.
      // Returns +1 if 'c' is counter clockwise to segment (a, b), i.e left of line
      // formed by the segment.
      public static int orientation(Pt a, Pt b, Pt c) {
        double value = (b.y - a.y) * (c.x - b.x) - 
                       (b.x - a.x) * (c.y - b.y);
        if (abs(value) < EPS) return 0;
        return (value > 0) ? -1 : +1;
      }
    
      // Tests whether point 'c' is on the line segment (a, b).
      // Ensure first that point c is collinear to segment (a, b) and
      // then check whether c is within the rectangle formed by (a, b)
      public static boolean pointOnLine(Pt a, Pt b, Pt c) {
        return orientation(a, b, c) == 0 && 
               min(a.x, b.x) <= c.x && c.x <= max(a.x, b.x) && 
               min(a.y, b.y) <= c.y && c.y <= max(a.y, b.y);
      }
    
      // Determines whether two segments intersect.
      public static boolean segmentsIntersect(Pt p1, Pt p2, Pt p3, Pt p4) {
    
        // Get the orientation of points p3 and p4 in relation
        // to the line segment (p1, p2)
        int o1 = orientation(p1, p2, p3);
        int o2 = orientation(p1, p2, p4);
        int o3 = orientation(p3, p4, p1);
        int o4 = orientation(p3, p4, p2);
    
        // If the points p1, p2 are on opposite sides of the infinite
        // line formed by (p3, p4) and conversly p3, p4 are on opposite
        // sides of the infinite line formed by (p1, p2) then there is
        // an intersection.
        if (o1 != o2 && o3 != o4) return true;
    
        // Collinear special cases (perhaps these if checks can be simplified?)
        if (o1 == 0 && pointOnLine(p1, p2, p3)) return true;
        if (o2 == 0 && pointOnLine(p1, p2, p4)) return true;
        if (o3 == 0 && pointOnLine(p3, p4, p1)) return true;
        if (o4 == 0 && pointOnLine(p3, p4, p2)) return true;
    
        return false;
      }
    
      public static List<Pt> getCommonEndpoints(Pt p1, Pt p2, Pt p3, Pt p4) {
    
        List<Pt> points = new ArrayList<>();
    
        if (p1.equals(p3)) {
          points.add(p1);
          if (p2.equals(p4)) points.add(p2);
    
        } else if (p1.equals(p4)) {
          points.add(p1);
          if (p2.equals(p3)) points.add(p2);
    
        } else if (p2.equals(p3)) {
          points.add(p2);
          if (p1.equals(p4)) points.add(p1);
    
        } else if (p2.equals(p4)) {
          points.add(p2);
          if (p1.equals(p3)) points.add(p1);
        }
    
        return points;
      }
    
      // Finds the intersection point(s) of two line segments. Unlike regular line 
      // segments, segments which are points (x1 = x2 and y1 = y2) are allowed.
      public static Pt[] lineSegmentLineSegmentIntersection(Pt p1, Pt p2, Pt p3, Pt p4) {
    
        // No intersection.
        if (!segmentsIntersect(p1, p2, p3, p4)) return new Pt[]{};
    
        // Both segments are a single point.
        if (p1.equals(p2) && p2.equals(p3) && p3.equals(p4))
          return new Pt[]{p1};
    
        List<Pt> endpoints = getCommonEndpoints(p1, p2, p3, p4);
        int n = endpoints.size();
    
        // One of the line segments is an intersecting single point.
        // NOTE: checking only n == 1 is insufficient to return early
        // because the solution might be a sub segment.
        boolean singleton = p1.equals(p2) || p3.equals(p4);
        if (n == 1 && singleton) return new Pt[]{endpoints.get(0)};
    
        // Segments are equal.
        if (n == 2) return new Pt[]{endpoints.get(0), endpoints.get(1)};
    
        boolean collinearSegments = (orientation(p1, p2, p3) == 0) && 
                                    (orientation(p1, p2, p4) == 0);
    
        // The intersection will be a sub-segment of the two
        // segments since they overlap each other.
        if (collinearSegments) {
    
          // Segment #2 is enclosed in segment #1
          if (pointOnLine(p1, p2, p3) && pointOnLine(p1, p2, p4))
            return new Pt[]{p3, p4};
    
          // Segment #1 is enclosed in segment #2
          if (pointOnLine(p3, p4, p1) && pointOnLine(p3, p4, p2))
            return new Pt[]{p1, p2};
    
          // The subsegment is part of segment #1 and part of segment #2.
          // Find the middle points which correspond to this segment.
          Pt midPoint1 = pointOnLine(p1, p2, p3) ? p3 : p4;
          Pt midPoint2 = pointOnLine(p3, p4, p1) ? p1 : p2;
    
          // There is actually only one middle point!
          if (midPoint1.equals(midPoint2)) return new Pt[]{midPoint1};
    
          return new Pt[]{midPoint1, midPoint2};
        }
    
        /* Beyond this point there is a unique intersection point. */
    
        // Segment #1 is a vertical line.
        if (abs(p1.x - p2.x) < EPS) {
          double m = (p4.y - p3.y) / (p4.x - p3.x);
          double b = p3.y - m * p3.x;
          return new Pt[]{new Pt(p1.x, m * p1.x + b)};
        }
    
        // Segment #2 is a vertical line.
        if (abs(p3.x - p4.x) < EPS) {
          double m = (p2.y - p1.y) / (p2.x - p1.x);
          double b = p1.y - m * p1.x;
          return new Pt[]{new Pt(p3.x, m * p3.x + b)};
        }
    
        double m1 = (p2.y - p1.y) / (p2.x - p1.x);
        double m2 = (p4.y - p3.y) / (p4.x - p3.x);
        double b1 = p1.y - m1 * p1.x;
        double b2 = p3.y - m2 * p3.x;
        double x = (b2 - b1) / (m1 - m2);
        double y = (m1 * b2 - m2 * b1) / (m1 - m2);
    
        return new Pt[]{new Pt(x, y)};
      }
    
    }
    

    这是一个简单的用法示例:

      public static void main(String[] args) {
    
        // Segment #1 is (p1, p2), segment #2 is (p3, p4)
        Pt p1, p2, p3, p4;
    
        p1 = new Pt(-2, 4); p2 = new Pt(3, 3);
        p3 = new Pt(0, 0);  p4 = new Pt(2, 4);
        Pt[] points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
        Pt point = points[0];
    
        // Prints: (1.636, 3.273)
        System.out.printf("(%.3f, %.3f)\n", point.x, point.y);
    
        p1 = new Pt(-10, 0); p2 = new Pt(+10, 0);
        p3 = new Pt(-5, 0);  p4 = new Pt(+5, 0);
        points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
        Pt point1 = points[0], point2 = points[1];
    
        // Prints: (-5.000, 0.000) (5.000, 0.000)
        System.out.printf("(%.3f, %.3f) (%.3f, %.3f)\n", point1.x, point1.y, point2.x, point2.y);
      }
    

答案 9 :(得分:8)

只是想提一下,在Numeric Recipes系列中可以找到一个好的解释和明确的解决方案。我已经获得了第3版,答案在第1117页,第21.4节。另一种具有不同命名的解决方案可以在Marina Gavrilova Reliable Line Section Intersection Testing的论文中找到。在我看来,她的解决方案有点简单。

我的实施如下:

bool NuGeometry::IsBetween(const double& x0, const double& x, const double& x1){
   return (x >= x0) && (x <= x1);
}

bool NuGeometry::FindIntersection(const double& x0, const double& y0, 
     const double& x1, const double& y1,
     const double& a0, const double& b0, 
     const double& a1, const double& b1, 
     double& xy, double& ab) {
   // four endpoints are x0, y0 & x1,y1 & a0,b0 & a1,b1
   // returned values xy and ab are the fractional distance along xy and ab
   // and are only defined when the result is true

   bool partial = false;
   double denom = (b0 - b1) * (x0 - x1) - (y0 - y1) * (a0 - a1);
   if (denom == 0) {
      xy = -1;
      ab = -1;
   } else {
      xy = (a0 * (y1 - b1) + a1 * (b0 - y1) + x1 * (b1 - b0)) / denom;
      partial = NuGeometry::IsBetween(0, xy, 1);
      if (partial) {
         // no point calculating this unless xy is between 0 & 1
         ab = (y1 * (x0 - a1) + b1 * (x1 - x0) + y0 * (a1 - x1)) / denom; 
      }
   }
   if ( partial && NuGeometry::IsBetween(0, ab, 1)) {
      ab = 1-ab;
      xy = 1-xy;
      return true;
   }  else return false;
}

答案 10 :(得分:8)

C和Objective-C

基于Gareth Rees的回答

const AGKLine AGKLineZero = (AGKLine){(CGPoint){0.0, 0.0}, (CGPoint){0.0, 0.0}};

AGKLine AGKLineMake(CGPoint start, CGPoint end)
{
    return (AGKLine){start, end};
}

double AGKLineLength(AGKLine l)
{
    return CGPointLengthBetween_AGK(l.start, l.end);
}

BOOL AGKLineIntersection(AGKLine l1, AGKLine l2, CGPoint *out_pointOfIntersection)
{
    // http://stackoverflow.com/a/565282/202451

    CGPoint p = l1.start;
    CGPoint q = l2.start;
    CGPoint r = CGPointSubtract_AGK(l1.end, l1.start);
    CGPoint s = CGPointSubtract_AGK(l2.end, l2.start);

    double s_r_crossProduct = CGPointCrossProductZComponent_AGK(r, s);
    double t = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), s) / s_r_crossProduct;
    double u = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), r) / s_r_crossProduct;

    if(t < 0 || t > 1.0 || u < 0 || u > 1.0)
    {
        if(out_pointOfIntersection != NULL)
        {
            *out_pointOfIntersection = CGPointZero;
        }
        return NO;
    }
    else
    {
        if(out_pointOfIntersection != NULL)
        {
            CGPoint i = CGPointAdd_AGK(p, CGPointMultiply_AGK(r, t));
            *out_pointOfIntersection = i;
        }
        return YES;
    }
}

CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
    return v1.x * v2.y - v1.y * v2.x;
}

CGPoint CGPointSubtract_AGK(CGPoint p1, CGPoint p2)
{
    return (CGPoint){p1.x - p2.x, p1.y - p2.y};
}

CGPoint CGPointAdd_AGK(CGPoint p1, CGPoint p2)
{
    return (CGPoint){p1.x + p2.x, p1.y + p2.y};
}

CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
    return v1.x * v2.y - v1.y * v2.x;
}

CGPoint CGPointMultiply_AGK(CGPoint p1, CGFloat factor)
{
    return (CGPoint){p1.x * factor, p1.y * factor};
}

许多函数和结构都是私有的,但你应该很容易知道发生了什么。 这是回购https://github.com/hfossli/AGGeometryKit/

的公开内容

答案 11 :(得分:8)

上面提供了大量的解决方案,但我认为以下解决方案非常简单易懂。

当且仅当

时,两个段Vector AB和Vector CD相交
  1. 端点a和b位于段CD的两侧。
  2. 端点c和d位于段AB的相对侧。
  3. 更具体地说,a和b位于段CD的相对侧,当且仅当两个三元组a,c,d和b,c,d中的一个是逆时针顺序时。

    Intersect(a, b, c, d)
     if CCW(a, c, d) == CCW(b, c, d)
        return false;
     else if CCW(a, b, c) == CCW(a, b, d)
        return false;
     else
        return true;
    

    此处CCW表示逆时针方向,它根据点的方向返回true / false。

    来源:http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf 第2页

答案 12 :(得分:6)

我尝试了其中一些答案,但他们没有为我工作(对不起家伙);经过一些网络搜索,我找到了this

稍微修改一下他的代码,我现在有了这个函数,它将返回交点,或者如果没有找到交集,它将返回-1,-1。

    Public Function intercetion(ByVal ax As Integer, ByVal ay As Integer, ByVal bx As Integer, ByVal by As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal dx As Integer, ByVal dy As Integer) As Point
    '//  Determines the intersection point of the line segment defined by points A and B
    '//  with the line segment defined by points C and D.
    '//
    '//  Returns YES if the intersection point was found, and stores that point in X,Y.
    '//  Returns NO if there is no determinable intersection point, in which case X,Y will
    '//  be unmodified.

    Dim distAB, theCos, theSin, newX, ABpos As Double

    '//  Fail if either line segment is zero-length.
    If ax = bx And ay = by Or cx = dx And cy = dy Then Return New Point(-1, -1)

    '//  Fail if the segments share an end-point.
    If ax = cx And ay = cy Or bx = cx And by = cy Or ax = dx And ay = dy Or bx = dx And by = dy Then Return New Point(-1, -1)

    '//  (1) Translate the system so that point A is on the origin.
    bx -= ax
    by -= ay
    cx -= ax
    cy -= ay
    dx -= ax
    dy -= ay

    '//  Discover the length of segment A-B.
    distAB = Math.Sqrt(bx * bx + by * by)

    '//  (2) Rotate the system so that point B is on the positive X axis.
    theCos = bx / distAB
    theSin = by / distAB
    newX = cx * theCos + cy * theSin
    cy = cy * theCos - cx * theSin
    cx = newX
    newX = dx * theCos + dy * theSin
    dy = dy * theCos - dx * theSin
    dx = newX

    '//  Fail if segment C-D doesn't cross line A-B.
    If cy < 0 And dy < 0 Or cy >= 0 And dy >= 0 Then Return New Point(-1, -1)

    '//  (3) Discover the position of the intersection point along line A-B.
    ABpos = dx + (cx - dx) * dy / (dy - cy)

    '//  Fail if segment C-D crosses line A-B outside of segment A-B.
    If ABpos < 0 Or ABpos > distAB Then Return New Point(-1, -1)

    '//  (4) Apply the discovered position to line A-B in the original coordinate system.
    '*X=Ax+ABpos*theCos
    '*Y=Ay+ABpos*theSin

    '//  Success.
    Return New Point(ax + ABpos * theCos, ay + ABpos * theSin)
End Function

答案 13 :(得分:6)

似乎对Gavin's answer感兴趣,其中cortijon在comments中提出了javascript版本,而iMalc提供的版本略有fewer computations。有些人指出了各种代码提案的缺点,其他人则对一些代码提案的效率进行了评论。

iMalc通过Gavin的答案提供的算法是我目前在javascript项目中使用的算法,我只想提供一个清理版本,如果它可以帮助任何人。

// Some variables for reuse, others may do this differently
var p0x, p1x, p2x, p3x, ix,
    p0y, p1y, p2y, p3y, iy,
    collisionDetected;

// do stuff, call other functions, set endpoints...

// note: for my purpose I use |t| < |d| as opposed to
// |t| <= |d| which is equivalent to 0 <= t < 1 rather than
// 0 <= t <= 1 as in Gavin's answer - results may vary

var lineSegmentIntersection = function(){
    var d, dx1, dx2, dx3, dy1, dy2, dy3, s, t;

    dx1 = p1x - p0x;      dy1 = p1y - p0y;
    dx2 = p3x - p2x;      dy2 = p3y - p2y;
    dx3 = p0x - p2x;      dy3 = p0y - p2y;

    collisionDetected = 0;

    d = dx1 * dy2 - dx2 * dy1;

    if(d !== 0){
        s = dx1 * dy3 - dx3 * dy1;
        if((s <= 0 && d < 0 && s >= d) || (s >= 0 && d > 0 && s <= d)){
            t = dx2 * dy3 - dx3 * dy2;
            if((t <= 0 && d < 0 && t > d) || (t >= 0 && d > 0 && t < d)){
                t = t / d;
                collisionDetected = 1;
                ix = p0x + t * dx1;
                iy = p0y + t * dy1;
            }
        }
    }
};

答案 14 :(得分:6)

这对我来说效果很好。取自here

 // calculates intersection and checks for parallel lines.  
 // also checks that the intersection point is actually on  
 // the line segment p1-p2  
 Point findIntersection(Point p1,Point p2,  
   Point p3,Point p4) {  
   float xD1,yD1,xD2,yD2,xD3,yD3;  
   float dot,deg,len1,len2;  
   float segmentLen1,segmentLen2;  
   float ua,ub,div;  

   // calculate differences  
   xD1=p2.x-p1.x;  
   xD2=p4.x-p3.x;  
   yD1=p2.y-p1.y;  
   yD2=p4.y-p3.y;  
   xD3=p1.x-p3.x;  
   yD3=p1.y-p3.y;    

   // calculate the lengths of the two lines  
   len1=sqrt(xD1*xD1+yD1*yD1);  
   len2=sqrt(xD2*xD2+yD2*yD2);  

   // calculate angle between the two lines.  
   dot=(xD1*xD2+yD1*yD2); // dot product  
   deg=dot/(len1*len2);  

   // if abs(angle)==1 then the lines are parallell,  
   // so no intersection is possible  
   if(abs(deg)==1) return null;  

   // find intersection Pt between two lines  
   Point pt=new Point(0,0);  
   div=yD2*xD1-xD2*yD1;  
   ua=(xD2*yD3-yD2*xD3)/div;  
   ub=(xD1*yD3-yD1*xD3)/div;  
   pt.x=p1.x+ua*xD1;  
   pt.y=p1.y+ua*yD1;  

   // calculate the combined length of the two segments  
   // between Pt-p1 and Pt-p2  
   xD1=pt.x-p1.x;  
   xD2=pt.x-p2.x;  
   yD1=pt.y-p1.y;  
   yD2=pt.y-p2.y;  
   segmentLen1=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  

   // calculate the combined length of the two segments  
   // between Pt-p3 and Pt-p4  
   xD1=pt.x-p3.x;  
   xD2=pt.x-p4.x;  
   yD1=pt.y-p3.y;  
   yD2=pt.y-p4.y;  
   segmentLen2=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  

   // if the lengths of both sets of segments are the same as  
   // the lenghts of the two lines the point is actually  
   // on the line segment.  

   // if the point isn’t on the line, return null  
   if(abs(len1-segmentLen1)>0.01 || abs(len2-segmentLen2)>0.01)  
     return null;  

   // return the valid intersection  
   return pt;  
 }  

 class Point{  
   float x,y;  
   Point(float x, float y){  
     this.x = x;  
     this.y = y;  
   }  

   void set(float x, float y){  
     this.x = x;  
     this.y = y;  
   }  
 }  

答案 15 :(得分:5)

我认为这个问题有一个更简单的解决方案。我今天提出了另一个想法,似乎工作得很好(至少现在是2D)。您所要做的就是计算两条线之间的交点,然后检查计算的交点是否在两个线段的边界框内。如果是,则线段相交。那就是它。

编辑:

这就是我计算交集的方式(我不知道在哪里找到这个代码片段)

Point3D

来自

System.Windows.Media.Media3D

public static Point3D? Intersection(Point3D start1, Point3D end1, Point3D start2, Point3D end2) {

        double a1 = end1.Y - start1.Y;
        double b1 = start1.X - end1.X;
        double c1 = a1 * start1.X + b1 * start1.Y;

        double a2 = end2.Y - start2.Y;
        double b2 = start2.X - end2.X;
        double c2 = a2 * start2.X + b2 * start2.Y;

        double det = a1 * b2 - a2 * b1;
        if (det == 0) { // lines are parallel
            return null;
        }

        double x = (b2 * c1 - b1 * c2) / det;
        double y = (a1 * c2 - a2 * c1) / det;

        return new Point3D(x, y, 0.0);
    }

这是我的(为了答案而简化)BoundingBox类:

public class BoundingBox {
    private Point3D min = new Point3D();
    private Point3D max = new Point3D();

    public BoundingBox(Point3D point) {
        min = point;
        max = point;
    }

    public Point3D Min {
        get { return min; }
        set { min = value; }
    }

    public Point3D Max {
        get { return max; }
        set { max = value; }
    }

    public bool Contains(BoundingBox box) {
        bool contains =
            min.X <= box.min.X && max.X >= box.max.X &&
            min.Y <= box.min.Y && max.Y >= box.max.Y &&
            min.Z <= box.min.Z && max.Z >= box.max.Z;
        return contains;
    }

    public bool Contains(Point3D point) {
        return Contains(new BoundingBox(point));
    }

}

答案 16 :(得分:3)

此解决方案可能有所帮助

public static float GetLineYIntesept(PointF p, float slope)
    {
        return p.Y - slope * p.X;
    }

    public static PointF FindIntersection(PointF line1Start, PointF line1End, PointF line2Start, PointF line2End)
    {

        float slope1 = (line1End.Y - line1Start.Y) / (line1End.X - line1Start.X);
        float slope2 = (line2End.Y - line2Start.Y) / (line2End.X - line2Start.X);

        float yinter1 = GetLineYIntesept(line1Start, slope1);
        float yinter2 = GetLineYIntesept(line2Start, slope2);

        if (slope1 == slope2 && yinter1 != yinter2)
            return PointF.Empty;

        float x = (yinter2 - yinter1) / (slope1 - slope2);

        float y = slope1 * x + yinter1;

        return new PointF(x, y);
    }

答案 17 :(得分:3)

我将Kris的上述答案移植到了JavaScript上。在尝试了许多不同的答案之后,他提供了正确的观点。我以为我疯了,因为我没有得到我需要的分数。

function getLineLineCollision(p0, p1, p2, p3) {
    var s1, s2;
    s1 = {x: p1.x - p0.x, y: p1.y - p0.y};
    s2 = {x: p3.x - p2.x, y: p3.y - p2.y};

    var s10_x = p1.x - p0.x;
    var s10_y = p1.y - p0.y;
    var s32_x = p3.x - p2.x;
    var s32_y = p3.y - p2.y;

    var denom = s10_x * s32_y - s32_x * s10_y;

    if(denom == 0) {
        return false;
    }

    var denom_positive = denom > 0;

    var s02_x = p0.x - p2.x;
    var s02_y = p0.y - p2.y;

    var s_numer = s10_x * s02_y - s10_y * s02_x;

    if((s_numer < 0) == denom_positive) {
        return false;
    }

    var t_numer = s32_x * s02_y - s32_y * s02_x;

    if((t_numer < 0) == denom_positive) {
        return false;
    }

    if((s_numer > denom) == denom_positive || (t_numer > denom) == denom_positive) {
        return false;
    }

    var t = t_numer / denom;

    var p = {x: p0.x + (t * s10_x), y: p0.y + (t * s10_y)};
    return p;
}

答案 18 :(得分:2)

这是基于Gareth Ree的回答。如果它们也返回线段的重叠。用C ++编写,V是一个简单的向量类。其中2D中两个向量的叉积返回单个标量。它由我的学校自动测试系统测试并通过。

//Required input point must be colinear with the line
bool on_segment(const V& p, const LineSegment& l)
{
    //If a point is on the line, the sum of the vectors formed by the point to the line endpoints must be equal
    V va = p - l.pa;
    V vb = p - l.pb;
    R ma = va.magnitude();
    R mb = vb.magnitude();
    R ml = (l.pb - l.pa).magnitude();
    R s = ma + mb;
    bool r = s <= ml + epsilon;
    return r;
}

//Compute using vector math
// Returns 0 points if the lines do not intersect or overlap
// Returns 1 point if the lines intersect
//  Returns 2 points if the lines overlap, contain the points where overlapping start starts and stop
std::vector<V> intersect(const LineSegment& la, const LineSegment& lb)
{
    std::vector<V> r;

    //http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
    V oa, ob, da, db; //Origin and direction vectors
    R sa, sb; //Scalar values
    oa = la.pa;
    da = la.pb - la.pa;
    ob = lb.pa;
    db = lb.pb - lb.pa;

    if (da.cross(db) == 0 && (ob - oa).cross(da) == 0) //If colinear
    {
        if (on_segment(lb.pa, la) && on_segment(lb.pb, la))
        {
            r.push_back(lb.pa);
            r.push_back(lb.pb);
            dprintf("colinear, overlapping\n");
            return r;
        }

        if (on_segment(la.pa, lb) && on_segment(la.pb, lb))
        {
            r.push_back(la.pa);
            r.push_back(la.pb);
            dprintf("colinear, overlapping\n");
            return r;
        }

        if (on_segment(la.pa, lb))
            r.push_back(la.pa);

        if (on_segment(la.pb, lb))
            r.push_back(la.pb);

        if (on_segment(lb.pa, la))
            r.push_back(lb.pa);

        if (on_segment(lb.pb, la))
            r.push_back(lb.pb);

        if (r.size() == 0)
            dprintf("colinear, non-overlapping\n");
        else
            dprintf("colinear, overlapping\n");

        return r;
    }

    if (da.cross(db) == 0 && (ob - oa).cross(da) != 0)
    {
        dprintf("parallel non-intersecting\n");
        return r;
    }

    //Math trick db cross db == 0, which is a single scalar in 2D.
    //Crossing both sides with vector db gives:
    sa = (ob - oa).cross(db) / da.cross(db);

    //Crossing both sides with vector da gives
    sb = (oa - ob).cross(da) / db.cross(da);

    if (0 <= sa && sa <= 1 && 0 <= sb && sb <= 1)
    {
        dprintf("intersecting\n");
        r.push_back(oa + da * sa);
        return r;
    }

    dprintf("non-intersecting, non-parallel, non-colinear, non-overlapping\n");
    return r;
}

答案 19 :(得分:2)

我尝试了很多方法然后决定自己编写。所以这就是:

bool IsBetween (float x, float b1, float b2)
{
   return ( ((x >= (b1 - 0.1f)) && 
        (x <= (b2 + 0.1f))) || 
        ((x >= (b2 - 0.1f)) &&
        (x <= (b1 + 0.1f))));
}

bool IsSegmentsColliding(   POINTFLOAT lineA,
                POINTFLOAT lineB,
                POINTFLOAT line2A,
                POINTFLOAT line2B)
{
    float deltaX1 = lineB.x - lineA.x;
    float deltaX2 = line2B.x - line2A.x;
    float deltaY1 = lineB.y - lineA.y;
    float deltaY2 = line2B.y - line2A.y;

    if (abs(deltaX1) < 0.01f && 
        abs(deltaX2) < 0.01f) // Both are vertical lines
        return false;
    if (abs((deltaY1 / deltaX1) -
        (deltaY2 / deltaX2)) < 0.001f) // Two parallel line
        return false;

    float xCol = (  (   (deltaX1 * deltaX2) * 
                        (line2A.y - lineA.y)) - 
                    (line2A.x * deltaY2 * deltaX1) + 
                    (lineA.x * deltaY1 * deltaX2)) / 
                 ((deltaY1 * deltaX2) - (deltaY2 * deltaX1));
    float yCol = 0;
    if (deltaX1 < 0.01f) // L1 is a vertical line
        yCol = ((xCol * deltaY2) + 
                (line2A.y * deltaX2) - 
                (line2A.x * deltaY2)) / deltaX2;
    else // L1 is acceptable
        yCol = ((xCol * deltaY1) +
                (lineA.y * deltaX1) -
                (lineA.x * deltaY1)) / deltaX1;

    bool isCol =    IsBetween(xCol, lineA.x, lineB.x) &&
            IsBetween(yCol, lineA.y, lineB.y) &&
            IsBetween(xCol, line2A.x, line2B.x) &&
            IsBetween(yCol, line2A.y, line2B.y);
    return isCol;
}

基于这两个公式:(我从线和其他公式中简化了它们)

formula for x

formula for y

答案 20 :(得分:2)

这是C#中线段的基本实现,具有相应的交叉点检测代码。它需要一个名为Vector2f的2D矢量/点结构,但您可以将其替换为具有X / Y属性的任何其他类型。如果符合您的需求,您也可以将float替换为double

此代码用于我的.NET物理库Boing

public struct LineSegment2f
{
    public Vector2f From { get; }
    public Vector2f To { get; }

    public LineSegment2f(Vector2f @from, Vector2f to)
    {
        From = @from;
        To = to;
    }

    public Vector2f Delta => new Vector2f(To.X - From.X, To.Y - From.Y);

    /// <summary>
    /// Attempt to intersect two line segments.
    /// </summary>
    /// <remarks>
    /// Even if the line segments do not intersect, <paramref name="t"/> and <paramref name="u"/> will be set.
    /// If the lines are parallel, <paramref name="t"/> and <paramref name="u"/> are set to <see cref="float.NaN"/>.
    /// </remarks>
    /// <param name="other">The line to attempt intersection of this line with.</param>
    /// <param name="intersectionPoint">The point of intersection if within the line segments, or empty..</param>
    /// <param name="t">The distance along this line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
    /// <param name="u">The distance along the other line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
    /// <returns><c>true</c> if the line segments intersect, otherwise <c>false</c>.</returns>
    public bool TryIntersect(LineSegment2f other, out Vector2f intersectionPoint, out float t, out float u)
    {
        var p = From;
        var q = other.From;
        var r = Delta;
        var s = other.Delta;

        // t = (q − p) × s / (r × s)
        // u = (q − p) × r / (r × s)

        var denom = Fake2DCross(r, s);

        if (denom == 0)
        {
            // lines are collinear or parallel
            t = float.NaN;
            u = float.NaN;
            intersectionPoint = default(Vector2f);
            return false;
        }

        var tNumer = Fake2DCross(q - p, s);
        var uNumer = Fake2DCross(q - p, r);

        t = tNumer / denom;
        u = uNumer / denom;

        if (t < 0 || t > 1 || u < 0 || u > 1)
        {
            // line segments do not intersect within their ranges
            intersectionPoint = default(Vector2f);
            return false;
        }

        intersectionPoint = p + r * t;
        return true;
    }

    private static float Fake2DCross(Vector2f a, Vector2f b)
    {
        return a.X * b.Y - a.Y * b.X;
    }
}

答案 21 :(得分:1)

用于检查两个给定线段是否相交的C ++程序

#include <iostream>
using namespace std;

struct Point
{
    int x;
    int y;
};

// Given three colinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
bool onSegment(Point p, Point q, Point r)
{
    if (q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) &&
        q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y))
       return true;

    return false;
}

// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Point p, Point q, Point r)
{
    // See 10th slides from following link for derivation of the formula
    // http://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf
    int val = (q.y - p.y) * (r.x - q.x) -
              (q.x - p.x) * (r.y - q.y);

    if (val == 0) return 0;  // colinear

    return (val > 0)? 1: 2; // clock or counterclock wise
}

// The main function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
bool doIntersect(Point p1, Point q1, Point p2, Point q2)
{
    // Find the four orientations needed for general and
    // special cases
    int o1 = orientation(p1, q1, p2);
    int o2 = orientation(p1, q1, q2);
    int o3 = orientation(p2, q2, p1);
    int o4 = orientation(p2, q2, q1);

    // General case
    if (o1 != o2 && o3 != o4)
        return true;

    // Special Cases
    // p1, q1 and p2 are colinear and p2 lies on segment p1q1
    if (o1 == 0 && onSegment(p1, p2, q1)) return true;

    // p1, q1 and p2 are colinear and q2 lies on segment p1q1
    if (o2 == 0 && onSegment(p1, q2, q1)) return true;

    // p2, q2 and p1 are colinear and p1 lies on segment p2q2
    if (o3 == 0 && onSegment(p2, p1, q2)) return true;

     // p2, q2 and q1 are colinear and q1 lies on segment p2q2
    if (o4 == 0 && onSegment(p2, q1, q2)) return true;

    return false; // Doesn't fall in any of the above cases
}

// Driver program to test above functions
int main()
{
    struct Point p1 = {1, 1}, q1 = {10, 1};
    struct Point p2 = {1, 2}, q2 = {10, 2};

    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    p1 = {10, 0}, q1 = {0, 10};
    p2 = {0, 0}, q2 = {10, 10};
    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    p1 = {-5, -5}, q1 = {0, 0};
    p2 = {1, 1}, q2 = {10, 10};
    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    return 0;
}

答案 22 :(得分:1)

基于@Gareth Rees的答案,Python的版本:

import numpy as np

def np_perp( a ) :
    b = np.empty_like(a)
    b[0] = a[1]
    b[1] = -a[0]
    return b

def np_cross_product(a, b):
    return np.dot(a, np_perp(b))

def np_seg_intersect(a, b, considerCollinearOverlapAsIntersect = False):
    # https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
    # http://www.codeproject.com/Tips/862988/Find-the-intersection-point-of-two-line-segments
    r = a[1] - a[0]
    s = b[1] - b[0]
    v = b[0] - a[0]
    num = np_cross_product(v, r)
    denom = np_cross_product(r, s)
    # If r x s = 0 and (q - p) x r = 0, then the two lines are collinear.
    if np.isclose(denom, 0) and np.isclose(num, 0):
        # 1. If either  0 <= (q - p) * r <= r * r or 0 <= (p - q) * s <= * s
        # then the two lines are overlapping,
        if(considerCollinearOverlapAsIntersect):
            vDotR = np.dot(v, r)
            aDotS = np.dot(-v, s)
            if (0 <= vDotR  and vDotR <= np.dot(r,r)) or (0 <= aDotS  and aDotS <= np.dot(s,s)):
                return True
        # 2. If neither 0 <= (q - p) * r = r * r nor 0 <= (p - q) * s <= s * s
        # then the two lines are collinear but disjoint.
        # No need to implement this expression, as it follows from the expression above.
        return None
    if np.isclose(denom, 0) and not np.isclose(num, 0):
        # Parallel and non intersecting
        return None
    u = num / denom
    t = np_cross_product(v, s) / denom
    if u >= 0 and u <= 1 and t >= 0 and t <= 1:
        res = b[0] + (s*u)
        return res
    # Otherwise, the two line segments are not parallel but do not intersect.
    return None

答案 23 :(得分:0)

如果矩形的每一边都是线段,并且用户绘制的部分是线段,那么您需要检查用户绘制的线段以与四条边线段相交。考虑到每个细分的起点和终点,这应该是一个相当简单的练习。

答案 24 :(得分:0)

基于t3chb0t的答案:

int intersezione_linee(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
   //L1: estremi (x1,y1)(x2,y2) L2: estremi (x3,y3)(x3,y3)
   int d;
   d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
   if(!d)
       return 0;
   p_x = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4))/d;
   p_y = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4))/d;
   return 1;
}

int in_bounding_box(int x1, int y1, int x2, int y2, int p_x, int p_y)
{
    return p_x>=x1 && p_x<=x2 && p_y>=y1 && p_y<=y2;

}

int intersezione_segmenti(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
    if (!intersezione_linee(x1,y1,x2,y2,x3,y3,x4,y4,p_x,p_y))
        return 0;

    return in_bounding_box(x1,y1,x2,y2,p_x,p_y) && in_bounding_box(x3,y3,x4,y4,p_x,p_y);
}

答案 25 :(得分:0)

我从“多视图几何”一书中读到了这些算法

使用

后的文字

'作为转置标志

*作为点积

x作为交叉乘积,当用作运算符

1。行定义

点x_vec =(x,y)'位于线上ax + by + c = 0

我们表示L =(a,b,c)',点为(x,y,1)'为齐次坐标

线方程可以写成

(x,y,1)(a,b,c)'= 0或x'* L = 0

2。线的交叉点

我们有两条线L1 =(a1,b1,c1)',L2 =(a2,b2,c2)'

假设x是点,矢量,并且x = L1×L2(L1叉积L2)。

小心,x总是一个2D点,​​如果你感到困惑,请阅读齐次坐标(L1xL2)是一个三元素矢量,而x是一个二维坐标。

根据三重产品,我们知道

L1 *(L1 x L2)= 0,L2 *(L1 x L2)= 0,因为L1,L2共面

我们用向量x代替(L1xL2),然后我们得到L1 * x = 0,L2 * x = 0,这意味着x位于L1和L2上,x是交点。

小心,这里x是齐次坐标,如果x的最后一个元素为零,则表示L1和L2是平行的。

答案 26 :(得分:0)

许多答案将所有计算都包含在一个函数中。如果您需要计算代码中其他地方使用的线斜率,y截距或x截距,那么您将冗余地进行这些计算。我已经分离出各自的函数,使用了明显的变量名,并对我的代码进行了评论,以便更容易理解。我需要知道线条是否无限地超出它们的端点,所以在JavaScript中:

http://jsfiddle.net/skibulk/evmqq00u/

var point_a = {x:0, y:10},
    point_b = {x:12, y:12},
    point_c = {x:10, y:0},
    point_d = {x:0, y:0},
    slope_ab = slope(point_a, point_b),
    slope_bc = slope(point_b, point_c),
    slope_cd = slope(point_c, point_d),
    slope_da = slope(point_d, point_a),
    yint_ab = y_intercept(point_a, slope_ab),
    yint_bc = y_intercept(point_b, slope_bc),
    yint_cd = y_intercept(point_c, slope_cd),
    yint_da = y_intercept(point_d, slope_da),
    xint_ab = x_intercept(point_a, slope_ab, yint_ab),
    xint_bc = x_intercept(point_b, slope_bc, yint_bc),
    xint_cd = x_intercept(point_c, slope_cd, yint_cd),
    xint_da = x_intercept(point_d, slope_da, yint_da),
    point_aa = intersect(slope_da, yint_da, xint_da, slope_ab, yint_ab, xint_ab),
    point_bb = intersect(slope_ab, yint_ab, xint_ab, slope_bc, yint_bc, xint_bc),
    point_cc = intersect(slope_bc, yint_bc, xint_bc, slope_cd, yint_cd, xint_cd),
    point_dd = intersect(slope_cd, yint_cd, xint_cd, slope_da, yint_da, xint_da);

console.log(point_a, point_b, point_c, point_d);
console.log(slope_ab, slope_bc, slope_cd, slope_da);
console.log(yint_ab, yint_bc, yint_cd, yint_da);
console.log(xint_ab, xint_bc, xint_cd, xint_da);
console.log(point_aa, point_bb, point_cc, point_dd);

function slope(point_a, point_b) {
  var i = (point_b.y - point_a.y) / (point_b.x - point_a.x);
  if (i === -Infinity) return Infinity;
  if (i === -0) return 0;
  return i;
}

function y_intercept(point, slope) {
    // Horizontal Line
    if (slope == 0) return point.y;
  // Vertical Line
    if (slope == Infinity)
  {
    // THE Y-Axis
    if (point.x == 0) return Infinity;
    // No Intercept
    return null;
  }
  // Angled Line
  return point.y - (slope * point.x);
}

function x_intercept(point, slope, yint) {
    // Vertical Line
    if (slope == Infinity) return point.x;
  // Horizontal Line
    if (slope == 0)
  {
    // THE X-Axis
    if (point.y == 0) return Infinity;
    // No Intercept
    return null;
  }
  // Angled Line
  return -yint / slope;
}

// Intersection of two infinite lines
function intersect(slope_a, yint_a, xint_a, slope_b, yint_b, xint_b) {
  if (slope_a == slope_b)
  {
    // Equal Lines
    if (yint_a == yint_b && xint_a == xint_b) return Infinity;
    // Parallel Lines
    return null;
  }
  // First Line Vertical
    if (slope_a == Infinity)
  {
    return {
        x: xint_a,
      y: (slope_b * xint_a) + yint_b
    };
  }
  // Second Line Vertical
    if (slope_b == Infinity)
  {
    return {
        x: xint_b,
      y: (slope_a * xint_b) + yint_a
    };
  }
  // Not Equal, Not Parallel, Not Vertical
  var i = (yint_b - yint_a) / (slope_a - slope_b);
  return {
    x: i,
    y: (slope_a * i) + yint_a
  };
}