我有两个3D点,即(x1,y1,z1)和(x2,y2,z2)和一个3D点(x,y,z)。 我想知道 (x,y,z) 是否位于连接 (x1,y1,z1) 和 (x2,y2,z2) 的线上。
我尝试了以下算法:
if ((((x - x1) / x2-x1) == ((y - y1) / y2-y1)) && (((x - x1) / x2 - x1)
== ((z - z1) / z2- z1)) --> then,the 3D Line intersects (x,y,z)
但是,如果我的 x1 = x2 (或) y1 = y2 (或) z1=z2 怎么办?然后我会得到一个错误,说"除以零"是不可能的。
如果有人能提出一些替代方法,我会很高兴。
提前谢谢。
我会使用点线距离测量,如果距离小于接近零的误差阈值,则返回 true。
为了详细说明,我们使用一条由两点组成的线,p1
和p2
.我们想知道p3
是否在线。首先,我们使用点到线距离公式找到d
。
d = ((p0 - p1).cross(p0 - p2)).length() / (p2 - p1).length()
也就是说,假设您可以使用+
、-
、cross
、length
操作。出于性能原因,您可能更愿意查找d
平方。
d2 = ((p0 - p1).cross(p0 - p2)).lengthSquared() / (p2 - p1).lengthSquared()
现在,如果d
或d2
正好为零,那么您必须在线上。但这是浮点运算,所以我会根据您的应用程序留出一点余地。所以从本质上讲,d < 1e6
或其他东西应该可以解决问题。
简单的点积可以轻松做到这一点......因此,让我们考虑一下我们得到了由两点定义的线p0,p1
.该线上的任何点p
到任何端点的斜率相同或负,因此
|dot(p1-p0,p-p0)|/(|p1-p0|*|p-p0|) = 1.0
为了使浮点更健壮,如下所示:
|dot(p1-p0,p-p0)|/(|p1-p0|*|p-p0|) >= 1.0-1e-10;
1e-10
足够小的地方 epsilon ...重写为代码:
dx=x1-x0;
dy=y1-y0;
dz=z1-z0;
ex=x-x0;
ey=y-y0;
ez=z-z0;
q =dx*ex;
q+=dy*ey;
q+=dz*zy;
q*=q;
q/=(dx*dx+dy*dy+dz*dz);
q/=(ex*ex+ey*ey+ez*ez);
if (q>=1.0-1e-10) point p(x,y) is on the line
else p(x,y) is not on line
正如您所看到的不需要 sqrt,我们可以比较功率......
但是,您应该在p==p0
时处理边缘情况,然后使用p1
或立即返回 true。
如果您只想要线段内(而不是边缘点外)的点,那么您需要对代码进行轻微更改
0.0 <= dot(p1-p0,p-p0)/|p-p0| <= 1.0
所以:
dx=x1-x0;
dy=y1-y0;
dz=z1-z0;
ex=x-x0;
ey=y-y0;
ez=z-z0;
q =dx*ex;
q+=dy*ey;
q+=dz*zy;
if (q<0.0) p(x,y) is not on line
q*=q;
q/=(ex*ex+ey*ey+ez*ez);
if (q<=1.0) point p(x,y) is on the line
else p(x,y) is not on line
顺便说一句,点积的结果为您提供了一个垂直投影到另一个向量的比率或它们之间角度的 cos(如果它们被归一化),因此对于平行向量,结果是长度或1.0
的100%
。如果使用测角p-p0
法调整1e-10
值,则可以将其转换为检测与线垂直距离的点(这对于粗线和/或鼠标选择可能很方便)。
要解决上述问题,您需要检查三角形的面积,将它们视为 3-D 空间中的 3 个点。要找到该区域,请通过此链接。如果面积 = 0,则给定的点是共线的。
如果您不关心性能问题,则可以使用空间中段的参数方程。
P(t) = P0 + t(P1 - P0)
其中 P0 和 P1 是 3d 点,t 是介于 0 到 1 之间的参数。
这导致 3 个方程
x(t) = x0 + t(x1 - x0)
y(t) = y0 + t(y1 - y0)
z(t) = z0 + t(z1 - z0)
因此,要检查您的 (x,y,z) 点是否位于直线中,您可以获得 t 的初始值,例如t = (x - x0)/(x1-x0)
然后检查它是否满足其他两个方程
t = (x - x0)/(x1-x0)
if ( (y0 + t(y1-y0) == y) and (z0 + t(z1-z0) == z) ) then
---> we are in the line
就像@Jay指出的那样,这是浮点数学,你必须处理一些值的容忍度。例如,测试 y 可以是 y0+ t(y1-y0) - y <0.001
正如您自己指出的那样,以稳健的方式执行此操作并非易事。因此,我建议你不要重新发明轮子,而是使用几何库。我在 geometrictools.com 使用野生魔法方面有很好的经验。
在您的情况下,要使用的谓词将gte::DCPQuery
获取点和线之间的距离,然后测试它是否足够接近于零,以实现您的"在线"目的。
使用示例可能如下所示:
using namespace gte;
Line3<double> line({x1, y1, z1}, {x2 - x1, y2 - y1, z2 - z1});
Vector3<double> point{x, y, z};
DCPPoint3Line3 query;
auto result = query(point, line);
bool pointIsOnLine = (result.distance < some_epsilon);
(编译器触及的代码注释,旨在显示方法,而不是"分号完美")。