线段相交

记两线段为ABCD

一、方法一

1. 先判断线段是否共线

依据共线向量叉乘为零来判断
ABCD叉乘是否为0,如果为0则共线

2. 在不共线情况下判断是否相交

依据0t10u1来判断,其中tu的推导过程如下:

假设交点为P,则有P=A+ABt,t[0,1]P=C+CDu,u[0,1]

A+ABt=C+CDu==>ABt=AC+CDu

由于向量自身的叉乘为0,所以上式两边同时叉乘CD可得:

CD×ABt=CD×AC

变形可得:

t=CD×ACCD×ABt[0,1]

同理,两边同时叉乘AB可得:AB×CDu=AB×AC,所以:

u=AB×ACCD×ABu[0,1]

3. 在相交的情况下计算交点

依据第2步计算的tu,代入:P=A+ABt,t[0,1]P=C+CDu,u[0,1]即可求出交点P的坐标

代码实现

struct Point {
  float x, y;
  Point(const float& px = 0, const float& py = 0) : x(px), y(py) {}
  Point operator+(const Point& p) const {
    return Point(x + p.x, y + p.y);
  }
  Point& operator+=(const Point& p) {
    x += p.x;
    y += p.y;
    return *this;
  }
  Point operator-(const Point& p) const {
    return Point(x - p.x, y - p.y);
  }
  Point operator*(const float coeff) const {
    return Point(x * coeff, y * coeff);
  }
};

float Dot2d(const Point & A, const Point & B) {
  return A.x * B.x + A.y * B.y;
}
 
float Cross2d(const Point & A, const Point & B) {
  return A.x * B.y - B.x * A.y;
}

bool IsInsert(const Point& A, const Point& B, const Point& C, const Point&D)
{
     Point AB = B - A;
     Point CD = D - C;
     float det = Cross2d(CD , AB);

      // This takes care of parallel lines
      if (std::fabs(det) <= 1e-14) {
        return false;
      }

      Point AC= C - A;

      double t = Cross2d(CD, AC) / det;
      double u = Cross2d(AB, AC) / det;

      if (t > -EPS && t < 1.0f + EPS && u > -EPS && u < 1.0f + EPS) {
         return true;
         // intersections P = A + AB * t;
      }
}

二、方法二

1. 先判断线段是否共线

同方法一

2. 在不共线情况下判断是否相交

依据线段相交则一条线段两端点位于另一线段两侧来判断
相交情况下有如下两种情形:

判断是否在两侧可以依据两个向量的叉乘(右手法则),如下:

OA×OB>0,则OBOA的逆时针方向
OB×OA<0,则OAOB的顺时针方向

所以在相交的两种情况下有:

  • 情况1
    CDAB两侧,则有:

(AC×AB)(AD×AB)<0

ABCD两侧,则有:

(CA×CD)(CB×CD)<0

  • 情况2
    由于端点在线段上,所以有一个叉乘为0,因此:

(AC×AB)(AD×AB)=0(CA×CD)(CB×CD)=0

代码实现

bool IsInsert(const Point& A, const Point& B, const Point& C, const Point&D)
{
     Point AB = B - A;
     Point CD = D - C;
     Point AC = C - A; 
     Point AD = D - A;
     Point CB = B - C;
     Point CA = -AC;  
     float det = Cross2d(CD , AB);

     // This takes care of parallel lines
     if (std::fabs(det) <= 1e-14) {
        return false;
     }

     if (Cross2D(AC, AB) * Cross2D(AD, AB) <= EPS  &&  Cross2D(CA, CD) * Cross2D(CB, CD)< EPS) {
         return true; 
     }
}

参考链接

line_intersection
Two_line_segments

posted @   半夜打老虎  阅读(515)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示