给出一个在3D空间中有8个顶点的立方体。如何确定myPoint
是在多维数据集的内部还是外部?
cube[0] = (x0, y0, z0);
cube[1] = (x1, y1, z1);
cube[2] = (x2, y2, z2);
cube[3] = (x3, y3, z3);
cube[4] = (x4, y4, z4);
cube[5] = (x5, y5, z5);
cube[6] = (x6, y6, z6);
cube[7] = (x7, y7, z7);
myPoint = (x, y, z);
我正在尝试在3D中实现此data filter technique
答案 0 :(得分:5)
可能最简单的方法是计算绑定立方体的6个平面中的每个平面的平面方程,将点插入每个平面并确保结果符号为正(或者为负,取决于您是否计算你的飞机面向内或向外)。平面方程是p * normal + k = 0,通过取两个边之间的叉积来计算法线,然后将其中一个点插入平面方程中得到k。
更高级的方法是想象定义X,Y和Z轴的立方体和偏移(由立方体[0]定义)并将它们插入矩阵以转换两个空间之间的点。通过该矩阵的逆转换您的点将其置于“立方体空间”中,其中立方体与X / Y / Z轴对齐,因此您可以仅对边进行幅度比较。
答案 1 :(得分:5)
如果你想从链接的帖子中实现想法,那么考虑轴对齐的立方体(实际上是平行六面体)是有意义的。在这种情况下,支票为xmin<=x<=xmax && ymin<=y<=ymax && zmin<=z<=zmax
。
答案 2 :(得分:3)
特殊情况#1(轴对齐立方体):
如maxim1000's answer所示,您可以简单地检查所考虑点的X,Y,Z坐标是否位于多维数据集的X,Y,Z坐标的最小值和最大值中。
X_min <= X <= X_max and Y_min <= Y <= Y_max and Z_min <= Z <= Z_max
如果满足上述条件,则该点位于立方体内部,否则就不在立方体内。
常规案例(定向多维数据集):
有两种方法可以解决此问题。首先,将点引入立方体的局部坐标系并应用上述特殊情况。第二种情况是关于向量投影的概念。第一种情况比第二种情况稍微复杂一点,因为您需要计算旋转矩阵,该矩阵将点从世界坐标系转换为多维数据集的局部坐标系。考虑下图所示的多维数据集。
对于这两种方法,我们都需要从多维数据集表示中得出一些基本信息。让我们将原点固定在立方体左下角的立方体的局部坐标系中;在这种情况下,它是点D。现在,在三个维度上计算单位方向矢量,并计算这些方向上的立方体范围。可以按照以下步骤进行。
X local ,Y local 和Z local 在图中以蓝色,红色,绿色表示。和X length ,Y length 和Z length 是沿轴的范围。
现在让我们重新解决问题。
方法#1 :在Cube的局部坐标系中考虑这一点。为此,我们需要估算旋转矩阵。在这种情况下,旋转矩阵是3 x 3矩阵,其中X local ,Y local 和Z local 作为列。
使用旋转矩阵R,可以将点带入局部坐标系,然后应用轴对齐立方体的特殊情况。
方法2 :
构造从立方体中心到所考虑点的方向向量,并将其投影到每个局部轴上,并检查投影是否沿该轴超出了立方体的范围。如果投影在沿每个轴的范围内,则点在内部,否则在立方体的外部。
立方体的中心是I,如图所示。从立方体中心到点P的方向向量为V。向量V在X local ,Y local 和Z local 的计算公式如下。
现在,仅当满足以下所有条件时,点P才在立方体内。
这是方法2中的python快速实现。
import numpy as np
def inside_test(points , cube3d):
"""
cube3d = numpy array of the shape (8,3) with coordinates in the clockwise order. first the bottom plane is considered then the top one.
points = array of points with shape (N, 3).
Returns the indices of the points array which are outside the cube3d
"""
b1,b2,b3,b4,t1,t2,t3,t4 = cube3d
dir1 = (t1-b1)
size1 = np.linalg.norm(dir1)
dir1 = dir1 / size1
dir2 = (b2-b1)
size2 = np.linalg.norm(dir2)
dir2 = dir2 / size2
dir3 = (b4-b1)
size3 = np.linalg.norm(dir3)
dir3 = dir3 / size3
cube3d_center = (b1 + t3)/2.0
dir_vec = points - cube3d_center
res1 = np.where( (np.absolute(np.dot(dir_vec, dir1)) * 2) > size1 )[0]
res2 = np.where( (np.absolute(np.dot(dir_vec, dir2)) * 2) > size2 )[0]
res3 = np.where( (np.absolute(np.dot(dir_vec, dir3)) * 2) > size3 )[0]
return list( set().union(res1, res2, res3) )
答案 3 :(得分:0)
例子。你选择了具有开始(0,9,1)和结束(10,3,-1)的对角线
So for P1(5,6,0)
P.x (5) > Start.x(0) & P.x(5) < End.x(10)
P.y (6) < Start.x(9) & P.y(6) > End.x(3)
P.z (0) < Start.x(1) & P.z(0) > End.x(-1)
P1 is inside
for P2(5,6,6)
P.x (5) > Start.x(0) & P.x(5) < End.x(10)
P.y (6) < Start.x(9) & P.y(6) > End.x(3)
P.z (6) > Start.x(1) & P.z(6) > End.x(-1) <-- Not Within Range
P2 is out side
答案 4 :(得分:0)
这是我用来解决类似问题的算法,其中我的立方体未与3d空间的轴对齐。它可能不是最快的选择,但它有效。
使用以下公式计算多维数据集的 edgeLength (例如,从 a 到 b 的距离):
distance = sqrt(sqr(a.X - b.X) + (a.Y - b.Y) + (a.Z - b.Z))
确定点是否在多维数据集内
double latA = a DistanceTo(myPoint);
double latB = b.DistanceTo(myPoint);
if (Math.Abs(latA + latB - edgeLength) < 0.0001)
{
return true;
}
double angleA = CalculateAngleA(latA, latB, edgeLength);
if (angleA > 90.0001) return false;
double angleB = CalculateAngleA(latB, latA, edgeLength);
if (angleB > 90.0001) return false;
latB = e.DistanceTo(myPoint);
if (Math.Abs(latA + latB - edgeLength) < 0.0001)
{
return true;
}
angleA = CalculateAngleA(latA, latB, edgeLength);
if (angleA > 90.0001) return false;
angleB = CalculateAngleA(latB, latA, edgeLength);
if (angleB > 90.0001) return false;
latB = d.DistanceTo(myPoint);
if (Math.Abs(latA + latB - edgeLength) < 0.001)
{
return true;
}
angleA = CalculateAngleA(latA, latB, edgeLength);
if (angleA > 90.0001) return false;
angleB = CalculateAngleA(latB, latA, edgeLength);
if (angleB > 90.0001) return false;
//if all validations pass
return true;
计算angless(以度为单位)的公式:
double CalculateAngleA(double latA, double latB, double latC)
{
return Math.Acos((latA*latA + latC*latC - latB*latB)/(2*latA*latC))*(180.0/Math.PI);
}
答案 5 :(得分:0)
1)确定每个面(F1,F2,...)的平面方程(ax + by + cz + d = 0)
1.1计算F1的法线向量(将此向量指向对象外部!):
n1 =(n1x,n1y,n1z)
1.2在F1平面方程的系数上设置n1
n1x Px + n1y Py + n1z * Pz-d = val
在Java中看起来像这样:
private Vector3f normal;
private float d;
public PlaneEquation(Vector3f normal, Vector v1){
this.normal = normal;
this.d = normal.x*v1x + normal.y*v1y+ normal.z*v1z;
}
public float relativePosition(Vector3f p){
return normal.x*p.x + normal.y*p.y+ normal.z*p.z - d;
}
此等式是函数F1(Px,Py,Pz),因此P是要测试的点。
1.3通过在等式上设置F1的共面点来计算d的值。 。我选择了v1,因此:
d = n1x v1x + n1y v1y + n1z * v1z
2)使用平面函数方法relativePosition(Vector3f p),以测试点坐标F1(Px,Py,Pz),F2(Px,Py, Pz)等...
2.2)如果所有结果F1(P),F2(P),...均为负,则该点为对象的INSIDE,否则为OUT或ON。或者,如果某些结果是肯定的,则说明是OUT。
2.3)如果没有结果为肯定,但至少一个为零,则该点位于对象上。
boolean OUT = false;
boolean IN = false;
boolean ON = false;
for(PlaneEquation plane : planeList){
if(plane.relativePosition(point) > 0){
OUT = true;
ON = false;
break;
}
if(plane.relativePosition(point) == 0){
ON = true;
}
}
if(OUT == false && ON == false){
IN = true;
}
方法2-重心坐标
import org.joml.Vector3f;
public class HexaedronEquation {
// a is origin point
private Vector3f a,b,c,d;//vertices of parallelepiped
private Vector3f u,v,w; //direction vectors
private Vector3f vXu; //v cross u
private Vector3f wXu; //w croos u
public HexaedronEquation(Vector3f a,Vector3f b,Vector3f c,Vector3f d ) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.u = this.b.sub(a,new Vector3f()); // b - a
this.v = this.c.sub(a, new Vector3f()); // c - a
this.w = this.d.sub(a, new Vector3f()); // d - a
this.vXu = this.v.cross(u, new Vector3f());
this.wXu = this.w.cross(u, new Vector3f());
}
public float[] getParams(Vector3f p) {
/* baricentric coordinates
* solve the system
* px = ax +t1*ux +t2*vx + t3*wx
* py = ay +t1*uy +t2*vy + t3*wy
* pz = az +t1*uz +t2*vz + t3*wz
*/
float t1 = 0;
float t2 = 0;
float t3 = 0;
Vector3f s = p.sub(a, new Vector3f());
Vector3f sXu = s.cross(u, new Vector3f());
if(vXu.length() != 0 && w.length()!=0) {
t3 = s.dot(vXu)/w.dot(vXu);
}
if(vXu.x != 0) {
t2 = (sXu.x - t3*wXu.x)/vXu.x;
}
if(vXu.y != 0) {
t2 = (sXu.y - t3*wXu.y)/vXu.y;
}
if(vXu.z != 0) {
t2 = (sXu.z - t3*wXu.z)/vXu.z;
}
if(u.x != 0) {
t1 = (s.x - v.x*t2 -w.x*t3)/u.x;
}
if(u.y != 0) {
t1 = (s.y - v.y*t2 -w.y*t3)/u.y;
}
if(u.z != 0) {
t1 = (s.z - v.z*t2 -w.z*t3)/u.z;
}
return new float[] {t1, t2, t3};
}
public boolean isInside(Vector3f p) {
float[] params = getParams(p);
if(params[0]< 0 || params[0] > 1) return false;
if(params[1]< 0 || params[1] > 1) return false;
if(params[2]< 0 || params[2] > 1) return false;
if(params[0]>0 || params[0] <1) return true;
if(params[1]>0 || params[1] <1) return true;
if(params[2]>0 || params[2] <1) return true;
return false;
}
public boolean isOn(Vector3f p) {
float[] params = getParams(p);
if(params[0]< 0 || params[0] > 1) return false;
if(params[1]< 0 || params[1] > 1) return false;
if(params[2]< 0 || params[2] > 1) return false;
if(params[0]==0 || params[0] ==1) return true;
if(params[1]==0 || params[1] ==1) return true;
if(params[2]==0 || params[2] ==1) return true;
return false;
}
public boolean isOnInside(Vector3f p) {
float[] params = getParams(p);
System.out.println("params: "+ getParamsString(params));
if(params[0]< 0 || params[0] > 1) return false;
if(params[1]< 0 || params[1] > 1) return false;
if(params[2]< 0 || params[2] > 1) return false;
return true;
}
public boolean isOut(Vector3f p) {
float[] params = getParams(p);
if(params[0]<0 || params[0] >1) return true;
if(params[1]<0 || params[1] >1) return true;
if(params[2]<0 || params[2] >1) return true;
return false;
}