我正在尝试绘制由3D中两个平面的交点形成的线,但我无法理解数学,已经解释了here和here。
我试图弄清楚自己,但是我得到解决方案的最接近的是一个指向与交叉线相同方向的矢量,通过使用平面法线的叉积。我不知道如何在交叉线上找到一个点,任何一点都可以。我认为这种方法是死路一条。以下是此尝试的屏幕截图:
我尝试使用this问题中提到的解决方案,但它与原始解释有一个死链接,并且该等式对我不起作用(它有不平衡的括号,我试图在下面纠正)。
var planeA = new THREE.Plane((new THREE.Vector3(0, 0, 1)).normalize(), 100);
var planeB = new THREE.Plane((new THREE.Vector3(1, 1, 1)).normalize(), -100);
var x1 = planeA.normal.x,
y1 = planeA.normal.y,
z1 = planeA.normal.z,
d1 = planeA.constant;
var x2 = planeB.normal.x,
y2 = planeB.normal.y,
z2 = planeB.normal.z,
d2 = planeB.constant;
var point1 = new THREE.Vector3();
point1.x = 0;
point1.z = (y2 / y1) * (d1 - d2) / (z2 - z1 * y2 / y1);
point1.y = (-z1 * point1.z - d1) / y1;
var point2 = new THREE.Vector3();
point2.x = 1;
point2.z = (y2 / y1) * (x1 * point2.x + d1) - (x2 * point2.x - d2) / (z2 - z1 * y2 / y1);
point2.y = (-z1 * point2.z - x1 * point2.x - d1) / y1;
console.log(point1, point2);
输出:
THREE.Vector3 {x: -1, y: NaN, z: NaN, …}
THREE.Vector3 {x: 1, y: Infinity, z: -Infinity, …}
预期产出:
如果有人能够指出我应该如何工作的一个很好的解释,或者一个平面交叉算法的例子,我将不胜感激。
答案 0 :(得分:5)
以下是http://geomalgorithms.com/a05-_intersect-1.html所述的平面交叉点解决方案的实现。基本上,您首先使用平面法线的叉积来找到两个平面中线的方向。其次,你在平面的隐式方程上使用一些代数(P.n + d = 0,其中P是平面上的某个点,n是法线,d是平面常数)来求解一个点在平面的交叉点以及x = 0,y = 0或z = 0平面之一。然后解决方案是由点和矢量描述的线。我使用的是three.js版本79
/*
Algorithm taken from http://geomalgorithms.com/a05-_intersect-1.html. See the
section 'Intersection of 2 Planes' and specifically the subsection
(A) Direct Linear Equation
*/
function intersectPlanes(p1, p2) {
// the cross product gives us the direction of the line at the intersection
// of the two planes, and gives us an easy way to check if the two planes
// are parallel - the cross product will have zero magnitude
var direction = new THREE.Vector3().crossVectors(p1.normal, p2.normal)
var magnitude = direction.distanceTo(new THREE.Vector3(0, 0, 0))
if (magnitude === 0) {
return null
}
// now find a point on the intersection. We use the 'Direct Linear Equation'
// method described in the linked page, and we choose which coordinate
// to set as zero by seeing which has the largest absolute value in the
// directional vector
var X = Math.abs(direction.x)
var Y = Math.abs(direction.y)
var Z = Math.abs(direction.z)
var point
if (Z >= X && Z >= Y) {
point = solveIntersectingPoint('z', 'x', 'y', p1, p2)
} else if (Y >= Z && Y >= X){
point = solveIntersectingPoint('y', 'z', 'x', p1, p2)
} else {
point = solveIntersectingPoint('x', 'y', 'z', p1, p2)
}
return [point, direction]
}
/*
This method helps finding a point on the intersection between two planes.
Depending on the orientation of the planes, the problem could solve for the
zero point on either the x, y or z axis
*/
function solveIntersectingPoint(zeroCoord, A, B, p1, p2){
var a1 = p1.normal[A]
var b1 = p1.normal[B]
var d1 = p1.constant
var a2 = p2.normal[A]
var b2 = p2.normal[B]
var d2 = p2.constant
var A0 = ((b2 * d1) - (b1 * d2)) / ((a1 * b2 - a2 * b1))
var B0 = ((a1 * d2) - (a2 * d1)) / ((a1 * b2 - a2 * b1))
var point = new THREE.Vector3()
point[zeroCoord] = 0
point[A] = A0
point[B] = B0
return point
}
var planeA = new THREE.Plane((new THREE.Vector3(0, 0, 1)).normalize(), 100)
var planeB = new THREE.Plane((new THREE.Vector3(1, 1, 1)).normalize(), -100)
var [point, direction] = intersectPlanes(planeA, planeB)
答案 1 :(得分:4)
当我遇到这样的问题时,我通常会让符号代数包(在本例中为Mathematica)处理它。输入后
In[1]:= n1={x1,y1,z1};n2={x2,y2,z2};p={x,y,z};
In[2]:= Solve[n1.p==d1&&n2.p==d2,p]
并简化并替换x = 0和x = 1,我得
d2 z1 - d1 z2 d2 y1 - d1 y2
Out[5]= {{{y -> -------------, z -> ----------------}},
y2 z1 - y1 z2 -(y2 z1) + y1 z2
d2 z1 - x2 z1 - d1 z2 + x1 z2
> {{y -> -----------------------------,
y2 z1 - y1 z2
d2 y1 - x2 y1 + (-d1 + x1) y2
> z -> -----------------------------}}}
-(y2 z1) + y1 z2
答案 2 :(得分:3)
回想一下,为了表示一条线,我们需要一个向量来描述它的方向以及该线所经过的点。这称为参数化形式:
line_point(t) = t * (point_2 - point_1) + point_1
其中point_1
和point_2
是该行所经过的任意点,t
是参数化我们的行的标量。现在,如果我们将任意line_point(t)
放入上面的等式中,我们就可以在该行找到任何点t
。
注意:术语(point_2 - point_1)
不算什么,但是一个描述我们行方向的向量,而point_1
这个词什么都不是,而是我们行的一个点去(当然point_2
)也可以使用。
通过拍摄找到交叉线的方向direction
平面法线的交叉乘积,即direction = cross(normal_1,
normal_2)
。
选择任何一架飞机,例如第一个,找到任意两个不同点
在这架飞机上:point_1
和point_2
。如果我们假设平面方程
是a1 * x + b1 * y + c1 * z + d1 = 0
的形式,然后找到2
我们可以做以下几点:
y1 = 1
z1 = 0
x1 = -(b1 + d1) / a1
y2 = 0
z2 = 1
x2 = -(c1 + d1) / a1
其中point_1 = (x1, y1, z1)
和point_2 = (x2, y2, z2)
。
现在我们有2分,我们可以构建参数化
在第一个平面上躺着的代码:
line_point(t) = t * (point_2 - point_1) + point_1
,其中line_point(t)
描述了这一行上的任何一点,而t
只是一个输入标量
(通常称为参数)。
找到该行的交点intersection_point
使用line_point(t)
和第二平面a2 * x + b2 * y + c2 * z + d2 = 0
标准line-plane intersection algorithm(注意
代数形式部分,因为这是实现线平面所需的全部内容
交集,如果你还没有这样做的话。)
现在可以找到我们的交叉线,并且可以构建它
像往常一样参数化表格:intersection_line_point(s) = s *
direction + intersection_point
,其中intersection_line_point(s)
描述此交叉线上的任何点,s
为参数。
注意:我没有在任何地方阅读此算法,我刚刚根据我的线性代数知识从头脑中设计了它。这并不意味着它不起作用,但可能会进一步优化此算法。
当2个法线向量normal_1
和normal_2
几乎共线时,此问题会变得非常ill-conditioned。几何上它意味着2个平面彼此几乎平行,并且有限的 - 确定不可能的交叉线不可能精确算术,在这种情况下是浮点算术。
答案 3 :(得分:2)
很容易让three.js为你解决这个问题。
如果你用矩阵表示法来表达你的问题
m * x = v
然后x的解决方案是
x = inverse( m ) * v
我们将为m使用4x4矩阵,因为three.js对inverse()
类有Matrix4
方法。
var x1 = 0,
y1 = 0,
z1 = 1,
d1 = 100;
var x2 = 1,
y2 = 1,
z2 = 1,
d2 = -100;
var c = 0; // the desired value for the x-coordinate
var v = new THREE.Vector4( d1, d2, c, 1 );
var m = new THREE.Matrix4( x1, y1, z1, 0,
x2, y2, z2, 0,
1, 0, 0, 0,
0, 0, 0, 1
);
var minv = new THREE.Matrix4().getInverse( m );
v.applyMatrix4( minv );
console.log( v );
v的x分量将根据需要等于c,y分量和z分量将包含您要查找的值。 w分量是无关紧要的。
现在,重复c的下一个值,c = 1.
three.js r.58