C++检测平面内两个三角形是否相交(逻辑清晰,手撕精简版)

看了很多博客对这个问题都写的逻辑混乱,还漏掉了某些情况,实在看不下去了。所以本人按自己对几何的理解手写了一版,可能并不是最优解法,但是力求简洁,百行之内完成任务。

1 理论部分

1.1 两个三角形a,b存在三种关系:

  • 相交
  • 包含
  • 外离

1.2 相交的判断:

  • 是否有边相交:遍历三角形a和b的3条边,判断是否有相交线段
  • 是否有点在边上:分别遍历三角形a和b的点,判断是否在另一三角的边上
  • 是否有顶点重合:遍历三角形a和b的点,判断是否坐标相同

其中,判断有边相交:

  • 首先利用两外积的方向是否异号判断两线段是否相交dcmp(c1)*dcmp(c2) < 0(图左)
  • 再进一步限定为线段相交避免图右侧的情况(见代码seg部分):在这里插入图片描述

其中,判断点是否在边上:

  • 首先利用点到线上两点外积为0判断点是否在线上(dcmp(cross(p - a1, p - a2)) == 0
  • 再进一步利用内积是否为负限定为在线段上dcmp(dot(p - a1, p - a2)) < 0(见代码os)

为了方便确定符号,限定精度,设计了dcmp函数,并用Vector的别名区分point的意义(见代码)。

1.3 包含的判断:

  • 遍历a中的点,判断是否在三角形b中
  • 遍历b中的点,判断是否在三角形a中
  • 判断点是否在三角形中:
    • 按顺序计算点和三角形顶点和边的外积,三条边同号说明在内部(见下图和代码ist部分) :在这里插入图片描述

2 C++手撕平面内两个三角形是否相交

#include <bits/stdc++.h>
using namespace std;

struct point {
    
    
    double x, y;
    point() {
    
     };
    point(double _x, double _y) {
    
     x = _x; y = _y; }
};
//两点坐标的加减乘:
typedef point Vector;
Vector operator + (point a, point b) {
    
    
    return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (point a, point b) {
    
    
    return Vector(a.x - b.x, a.y-b.y);
}
Vector operator * (point a, point b) {
    
    
    return Vector(a.x * b.x, a.y * b.y);
}
//点乘:
double  dot (Vector a, Vector b) {
    
    
    return a.x * b.y + a.y * b.y;
}
//叉乘:
double cross(Vector a, Vector b) {
    
    
    return a.x * b.y - a.y * b.x;
}
//精度:
const double eps = 1e-6;
//重要的分类:
int dcmp(double x) {
    
    
    if (fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}
//判断两点是否相等:
bool operator == (const point& a, const point& b) {
    
    
    return (dcmp(a.x - b.x) == 0 & dcmp(a.y - b.y) == 0);
}
//判断两线段是否相交(不包括端点):
bool seg(point a1, point a2, point b1, point b2) {
    
    
    double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1);
    double d1 = cross(b2 - b1, a1 - b1), d2 = cross(b2 - b1, a2 - b1);
    return dcmp(c1)*dcmp(c2) < 0 && dcmp(d1)*dcmp(d2) < 0;
}
//判断(三角形)顶点p是否在(另一三角形的)某边a1a2(不包括端点)上:
bool os(point p,point a1,point a2) {
    
    
    return (dcmp(cross(p - a1, p - a2)) == 0 && dcmp(dot(p - a1, p - a2)) < 0 );
}
//判断点p是否在三角形t内部:
bool ist(point p,point * t) {
    
    
    return 
        (dcmp(cross(t[1] - t[0], p - t[0])) > 0
            && dcmp(cross(t[2] - t[1], p - t[1])) > 0
            && dcmp(cross(t[0] - t[2], p - t[2])) > 0)
        ||
        (dcmp(cross(t[1] - t[0], p - t[0])) <0
            && dcmp(cross(t[2] - t[1], p - t[1])) < 0
            && dcmp(cross(t[0] - t[2], p - t[2])) < 0);
}
void main()
{
    
    
    //构造三角形a,b:
    point a[3], b[3];
    cout << "请输入三角形a的三个顶点的坐标x,y:" << endl;
    cin >> a[0].x >> a[0].y >> a[1].x >> a[1].y >> a[2].x >> a[2].y;
    cout << "请输入三角形b的三个顶点的坐标x,y:" << endl;
    cin >> b[0].x >> b[0].y >> b[1].x >> b[1].y >> b[2].x >> b[2].y;
    //判断是否相交:
    bool flag = false;
    for (int i = 0; i < 3; ++i) {
    
    
        for (int j = 0; j < 3; ++j) {
    
    
            if (seg(a[i], a[(i + 1) % 3], b[j], b[(j + 1) % 3])) {
    
     flag = true; break; }
            if (os(a[i], b[j], b[(j + 1) % 3])) {
    
     flag = true; break; }
            if (a[i] == b[j]) {
    
     flag = true; break; }
        }
    }
    if (flag) cout << "三角形a、b的关系为:相交";
    else {
    
    //判断是否包含,否则外离
        if (ist(a[0], b) && ist(a[1], b) && ist(a[2], b)) flag = true;
        else if (ist(b[0], a) && ist(b[1], a) && ist(b[2], a)) flag = true;
        if (flag) cout << "三角形a、b的关系为:包含";
        else cout << "三角形a、b的关系为:外离";
    }
}

3 测试

3.1 外离:

在这里插入图片描述

在这里插入图片描述

3.2 包含

在这里插入图片描述

在这里插入图片描述

3.3 有点在三角形内部的相交:

在这里插入图片描述

在这里插入图片描述

3.4 无点在三角内部的相交:

这种六芒星的情况很多人都漏掉了,点均在三角外不等价于相交:
在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44671418/article/details/125130971