计算几何——判断直线相交

计算几何——判断直线相交

判断直线相交问题,通常有多种手段解决,大部分都需要涉及浮点数运算。但是通过计算几何向量叉乘的方法可以避免使用浮点数来判断直线是否相交。

用向量叉乘判断向量的相对位置

假设 b ⃗ \vec{b} b a ⃗ \vec{a} a 的左边,如图(1),根据右手定则, a ⃗ × b ⃗ \vec{a} \times \vec{b} a ×b z z z坐标的值应该为正值。如果 b ⃗ \vec{b} b a ⃗ \vec{a} a 的右边,如图(2),根据右手定则, a ⃗ × b ⃗ \vec{a} \times \vec{b} a ×b z z z坐标的值应该为负值。

故两个向量叉乘得到的向量的 z z z坐标的正负决定了两个向量的相对位置。

向量相对位置

线段的跨越

取线段 S 1 S_1 S1所在的直线,这条直线将平面分成了两个部分,若 S 2 S_2 S2的两个端点分别处在这两个部分,那么我们称线段 S 2 S_2 S2跨越了 S 1 S_1 S1

跨越
我们可以通过向量叉乘判断 S 2 S_2 S2是否跨越 S 1 S_1 S1。我们将线段的端点进行标记,称向量 a b ⃗ \vec{ab} ab 为轴向量, a c ⃗ \vec{ac} ac a d ⃗ \vec{ad} ad 为侧向量,我们分别计算 a b ⃗ × a c ⃗ \vec{ab} \times \vec{ac} ab ×ac a b ⃗ × a d ⃗ \vec{ab} \times \vec{ad} ab ×ad 由图可以知, S 2 S_2 S2跨越 S 1 S_1 S1的充要条件为两个侧向量和轴向量的相对位置不同,一个在轴向量的左侧,另一个在右侧。
向量叉乘
我们可以得出线段相交的一个充分条件,即两个线段互相跨越,那么这两个线段必然相交。

相互跨越

边界条件

令外,线段相交还存在两个边界条件,其一为一个线段的端点在另外一个线段上,即下图。

在线段上

另外一种情况即为共线,共线的情况其实为上述的一个特例,两种情况其实是一种情况。

共线

在下述代码中,我们针对这两个情况进行处理。

代码实现

首先是向量类实现。

// 向量类
struct Vector
{
    int x, y, z;

    Vector(int xi, int yi, int zi) : x(xi), y(yi), z(zi) {}
    Vector(int xi, int yi) : Vector(xi, yi, 0) {}
    Vector operator+(const Vector &o) const
    {
        return Vector(x + o.x, y + o.y, z + o.z);
    }

    Vector operator-(const Vector &o) const
    {
        return Vector(x - o.x, y - o.y, z - o.z);
    }
    // 向量叉乘
    Vector operator*(const Vector &o) const
    {
        return Vector(y * o.z - z * o.y, z * o.x - x * o.z, x * o.y - y * o.x);
    }
};

其次是计算方向的辅助函数,和判断点是否在线段上的辅助函数。

// abc分别为三点,计算以a为公共顶点,ab与ac向量的叉乘的z坐标
int direction(Vector a, Vector b, Vector c)
{
    return ((b - a) * (c - a)).z;
}

// 判断点c是否在线段ab上
bool on_segment(Vector a, Vector b, Vector c)
{
    return c.x >= min(a.x, b.x) && c.x <= max(a.x, b.x) && c.y >= min(a.y, b.y) && c.y <= max(a.y, b.y);
}

最后是判断相交的代码。

// 判断两个线段是否相交, ab 为 S1 的两个端点, cd 为 S2 的两个端点
bool is_intersect(Vector a, Vector b, Vector c, Vector d)
{
    // 计算轴向量和侧向量的叉乘值
    int d1 = direction(a, b, c);
    int d2 = direction(a, b, d);
    int d3 = direction(c, d, a);
    int d4 = direction(c, d, b);
    if (d1 * d2 < 0 && d3 * d4 < 0) // 情况一:互相跨越
        return true;
    else if (d1 == 0 && on_segment(a, b, c)) // 情况二:存在一端点在线段上
        return true;
    else if (d2 == 0 && on_segment(a, b, d))
        return true;
    else if (d3 == 0 && on_segment(c, d, a))
        return true;
    else if (d4 == 0 && on_segment(c, d, b))
        return true;
    else
        return false;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值