判断点是否包含在任意姿态长方体内部

1. 背景

    为了能够快速判断一个点是否在3维空间中的任意姿态的长方体中,虽然,对于平行坐标平面的长方体(例如平行于oxy平面)的判断方法非常简单,但是,想要判断一个点是否包含在3维空间中的任意姿态的长方体比较复杂。


2. 判断原理

    如下图所示,我们其实只需要判断一个点是否在3组平行平面的同一侧即可以判断(前/后面,左/右面,上/下面),判断的方法比较简单:即利用如图所示中的与法线的夹角,当一个点(如图中点B)在左右面的同一侧时,与法线的夹角都是锐角或钝角;当一个点(如图中点A)在左右面的不同侧时,与法线的夹角必定一个为锐角一个为钝角;实际上,我们不需要判断角度,我们只需要判断余弦值的符号即可,即可以判断向量的内积的符号即可, 总结如下:

      判断点是否在两个平行面的同一侧 <=> 判断点与法线的夹角的大小 <=> 判断余弦值的符号 <=> 判断向量内积的符号


可以使用向量叉积的方法判断线段与长方体是否相交。 具体思路如下: 1. 将长方体分解为每个面,并计算每个面的法向量。 2. 将线段表示为起和方向向量。 3. 判断线段是否长方体的六个面相交。对于每个面,计算线段起到该面的距离,并计算线段在该面法向量上的投影长度,判断这个投影长度是否在线段的范围内。若线段与任意一个面相交,则判断线段与长方体相交。 4. 若线段与所有面都没有相交,则判断线段是否长方体内部。将线段起代入长方体六个面的方程中,若线段起长方体六个面的同一侧,则线段在长方体内部。 代码实现如下: ```c++ #include <iostream> #include <cmath> using namespace std; const double eps = 1e-8; // 精度 struct Point { double x, y, z; }; struct Vector { double x, y, z; Vector operator+(const Vector& v) const { return { x + v.x, y + v.y, z + v.z }; } Vector operator-(const Vector& v) const { return { x - v.x, y - v.y, z - v.z }; } Vector operator*(double k) const { return { x * k, y * k, z * k }; } double operator*(const Vector& v) const { // 积 return x * v.x + y * v.y + z * v.z; } Vector operator^(const Vector& v) const { // 叉积 return { y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x }; } double len() const { // 向量长度 return sqrt(x * x + y * y + z * z); } void normalize() { // 归一化 double l = len(); x /= l; y /= l; z /= l; } }; struct Line { Point p; Vector v; }; struct Face { Vector n; // 法向量 double d; // 到原的距离 }; struct Cube { Face face[6]; // 长方体的六个面 }; // 计算两个之间的距离 double dist(const Point& a, const Point& b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z)); } // 计算到直线的距离 double dist(const Line& l, const Point& p) { return ((p - l.p) ^ l.v).len() / l.v.len(); } // 计算到面的距离 double dist(const Face& f, const Point& p) { return abs(f.n * p - f.d) / f.n.len(); } // 计算线段与面的交 Point intersect(const Line& l, const Face& f) { double t = (f.d - f.n * l.p) / (f.n * l.v); return { l.p.x + l.v.x * t, l.p.y + l.v.y * t, l.p.z + l.v.z * t }; } // 判断线段是否长方体内部 bool inside(const Cube& c, const Line& l) { int cnt1 = 0, cnt2 = 0; for (int i = 0; i < 6; i++) { double d1 = dist(c.face[i], l.p); double d2 = dist(c.face[i], l.p + l.v); if (d1 < eps && d2 < eps) return true; if (d1 < eps) cnt1++; if (d2 < eps) cnt2++; } if (cnt1 == 6 || cnt2 == 6) return true; return cnt1 > 0 && cnt2 > 0; } // 判断线段是否长方体相交 bool intersect(const Cube& c, const Line& l) { for (int i = 0; i < 6; i++) { double d1 = dist(c.face[i], l.p); double d2 = dist(c.face[i], l.p + l.v); if (d1 * d2 < eps || (d1 < eps && d2 - dist(l, intersect(l, c.face[i])) < eps)) return true; } return false; } int main() { Point p1 = { 0, 0, 0 }; Point p2 = { 1, 1, 1 }; Line l = { { -1, 0.5, 0.5 }, { 1, 0, 0 } }; Cube c = { { { 1, 0, 0 }, p2.x }, // x = p2.x { { -1, 0, 0 }, -p1.x }, // x = p1.x { { 0, 1, 0 }, p2.y }, // y = p2.y { { 0, -1, 0 }, -p1.y }, // y = p1.y { { 0, 0, 1 }, p2.z }, // z = p2.z { { 0, 0, -1 }, -p1.z } // z = p1.z }; if (intersect(c, l)) cout << "intersect" << endl; else if (inside(c, l)) cout << "inside" << endl; else cout << "outside" << endl; return 0; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值