[英]Intersection between two rectangles in 3D
为了得到 3D 中两个矩形之间的相交线,我将它们转换为平面,然后使用它们的法线的叉积得到相交线,然后我尝试得到与矩形的每个线段的线相交。
问题是这条线平行于三个线段,并且只与 NAN,NAN,NAN 中的一个相交,这是完全错误的。 你能告诉我我的代码有什么问题吗?
我使用此链接中的vector3 http://www.koders.com/csharp/fidCA8558A72AF7D3E654FDAFA402A168B8BC23C22A.aspx
并创建了我的飞机 class 如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace referenceLineAlgorithm
{
struct Line
{
public Vector3 direction;
public Vector3 point;
}
struct lineSegment
{
public Vector3 firstPoint;
public Vector3 secondPoint;
}
class plane_test
{
public enum Line3DResult
{
Line3DResult_Parallel = 0,
Line3DResult_SkewNoCross = 1,
Line3DResult_SkewCross = 2
};
#region Fields
public Vector3 Normal;
public float D;
public Vector3[] cornersArray;
public Vector3 FirstPoint;
public Vector3 SecondPoint;
public Vector3 temp;
public Vector3 normalBeforeNormalization;
#endregion
#region constructors
public plane_test(Vector3 point0, Vector3 point1, Vector3 point2, Vector3 point3)
{
Vector3 edge1 = point1 - point0;
Vector3 edge2 = point2 - point0;
Normal = edge1.Cross(edge2);
normalBeforeNormalization = Normal;
Normal.Normalize();
D = -Normal.Dot(point0);
///// Set the Rectangle corners
cornersArray = new Vector3[] { point0, point1, point2, point3 };
}
#endregion
#region Methods
/// <summary>
/// This is a pseudodistance. The sign of the return value is
/// positive if the point is on the positive side of the plane,
/// negative if the point is on the negative side, and zero if the
/// point is on the plane.
/// The absolute value of the return value is the true distance only
/// when the plane normal is a unit length vector.
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public float GetDistance(Vector3 point)
{
return Normal.Dot(point) + D;
}
public void Intersection(plane_test SecondOne)
{
///////////////////////////// Get the parallel to the line of interrsection (Direction )
Vector3 LineDirection = Normal.Cross(SecondOne.Normal);
float d1 = this.GetDistance(LineDirection);
float d2 = SecondOne.GetDistance(LineDirection);
temp = (LineDirection - (this.Normal * d1) - (SecondOne.Normal * d2));
temp.x = Math.Abs((float)Math.Round((decimal)FirstPoint.x, 2));
temp.y = Math.Abs((float)Math.Round((decimal)FirstPoint.y, 2));
Line line;
line.direction = LineDirection;
line.point = temp;
////////// Line segments
lineSegment AB, BC, CD, DA;
AB.firstPoint = cornersArray[0]; AB.secondPoint = cornersArray[1];
BC.firstPoint = cornersArray[1]; BC.secondPoint = cornersArray[2];
CD.firstPoint = cornersArray[2]; CD.secondPoint = cornersArray[3];
DA.firstPoint = cornersArray[3]; DA.secondPoint = cornersArray[0];
Vector3 r1 = new Vector3(-1, -1, -1);
Vector3 r2 = new Vector3(-1, -1, -1);
Vector3 r3 = new Vector3(-1, -1, -1);
Vector3 r4 = new Vector3(-1, -1, -1);
/*
0,0 |----------------| w,0
| |
| |
0,h |________________| w,h
*/
IntersectionPointBetweenLines(AB, line, ref r1);
IntersectionPointBetweenLines(BC, line, ref r2);
IntersectionPointBetweenLines(CD, line, ref r3);
IntersectionPointBetweenLines(DA, line, ref r4);
List<Vector3> points = new List<Vector3>();
points.Add(r1);
points.Add(r2);
points.Add(r3);
points.Add(r4);
points.RemoveAll(
t => ((t.x == -1) && (t.y == -1) && (t.z == -1))
);
if (points.Count == 2)
{
FirstPoint = points[0];
SecondPoint = points[1];
}
}
public Line3DResult IntersectionPointBetweenLines(lineSegment first, Line aSecondLine, ref Vector3 result)
{
Vector3 p1 = first.firstPoint;
Vector3 n1 = first.secondPoint - first.firstPoint;
Vector3 p2 = aSecondLine.point;
Vector3 n2 = aSecondLine.direction;
bool parallel = AreLinesParallel(first, aSecondLine);
if (parallel)
{
return Line3DResult.Line3DResult_Parallel;
}
else
{
float d = 0, dt = 0, dk = 0;
float t = 0, k = 0;
if (Math.Abs(n1.x * n2.y - n2.x * n1.y) > float.Epsilon)
{
d = n1.x * (-n2.y) - (-n2.x) * n1.y;
dt = (p2.x - p1.x) * (-n2.y) - (p2.y - p1.y) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.z * n2.y - n2.z * n1.y) > float.Epsilon)
{
d = n1.z * (-n2.y) - (-n2.z) * n1.y;
dt = (p2.z - p1.z) * (-n2.y) - (p2.y - p1.y) * (-n2.z);
dk = n1.z * (p2.z - p1.z) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.x * n2.z - n2.x * n1.z) > float.Epsilon)
{
d = n1.x * (-n2.z) - (-n2.x) * n1.z;
dt = (p2.x - p1.x) * (-n2.z) - (p2.z - p1.z) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.z * (p2.z - p1.z);
}
t = dt / d;
k = dk / d;
result = n1 * t + p1;
// Check if the point on the segmaent or not
// if (! isPointOnSegment(first, result))
//{
// result = new Vector3(-1,-1,-1);
// }
return Line3DResult.Line3DResult_SkewCross;
}
}
private bool AreLinesParallel(lineSegment first, Line aSecondLine)
{
Vector3 vector = (first.secondPoint - first.firstPoint);
vector.Normalize();
float kl = 0, km = 0, kn = 0;
if (vector.x != aSecondLine.direction.x)
{
if (vector.x != 0 && aSecondLine.direction.x != 0)
{
kl = vector.x / aSecondLine.direction.x;
}
}
if (vector.y != aSecondLine.direction.y)
{
if (vector.y != 0 && aSecondLine.direction.y != 0)
{
km = vector.y / aSecondLine.direction.y;
}
}
if (vector.z != aSecondLine.direction.z)
{
if (vector.z != 0 && aSecondLine.direction.z != 0)
{
kn = vector.z / aSecondLine.direction.z;
}
}
// both if all are null or all are equal, the lines are parallel
return (kl == km && km == kn);
}
private bool isPointOnSegment(lineSegment segment, Vector3 point)
{
//(x - x1) / (x2 - x1) = (y - y1) / (y2 - y1) = (z - z1) / (z2 - z1)
float component1 = (point.x - segment.firstPoint.x) / (segment.secondPoint.x - segment.firstPoint.x);
float component2 = (point.y - segment.firstPoint.y) / (segment.secondPoint.y - segment.firstPoint.y);
float component3 = (point.z - segment.firstPoint.z) / (segment.secondPoint.z - segment.firstPoint.z);
if ((component1 == component2) && (component2 == component3))
{
return true;
}
else
{
return false;
}
}
#endregion
}
}
static void Main(string[] args)
{
//// create the first plane points
Vector3 point11 =new Vector3(-255.5f, -160.0f,-1.5f) ; //0,0
Vector3 point21 = new Vector3(256.5f, -160.0f, -1.5f); //0,w
Vector3 point31 = new Vector3(256.5f, -160.0f, -513.5f); //h,0
Vector3 point41 = new Vector3(-255.5f, -160.0f, -513.5f); //w,h
plane_test plane1 = new plane_test(point11, point21, point41, point31);
//// create the Second plane points
Vector3 point12 = new Vector3(-201.6289f, -349.6289f, -21.5f);
Vector3 point22 =new Vector3(310.3711f,-349.6289f,-21.5f);
Vector3 point32 = new Vector3(310.3711f, 162.3711f, -21.5f);
Vector3 point42 =new Vector3(-201.6289f,162.3711f,-21.5f);
plane_test plane2 = new plane_test(point12, point22, point42, point32);
plane2.Intersection(plane1);
}
这是测试值最好的问候
您需要先指定一件事:
假设您的矩形既不共面也不平行,因此有一条独特的线 D1 表示每个矩形所描述的平面的交点。
鉴于此假设,它们是 2 个矩形 R1 和 R2 相交的 4 种可能情况:
(注意:有时 D1 既不与 R1 也不与 R2 和 R1 相交,R2 可以稍微旋转一点,因此 D1 并不总是与平行边相交,而是在连续边上相交)
当两个矩形之间有交点时,D1 总是在同一个交点上与 R1 和 R2 相交(参见第 1 和第 2 张图片)
您的 model 不好,因为您的线不能与同一矩形的 3 段平行...
正如您在这个问题中所问的那样: 3D 线相交算法一旦您拥有 D1 ( 获取由两个矩形的交点定义的线段的端点),只需确定与矩形的每个线段的交点。(每个矩形的 4 个线段需要被检查)
然后检查共同的交点......如果你找到一个,那么你的矩形相交。
抱歉,很难直接检查代码,但我想通过这些信息和平,您应该能够找到错误。
希望能帮助到你。
编辑:
通过一个点和 2 个向量定义一个矩形:
R2 {A ,u ,v}
R1 {B, u',v'}
定义由 R1 和 R2 描述的平面:P1 和 P2
P1(resp. P2) 的一个正交向量是 n1 (resp. n2)。让n1 = u ^ v
和n2 = u' ^ v
' 有:
然后
P1: n1.(x-xA,y-yA,z-zA)=0
P2: n2.(x-xB,y-yB,z-zB)=0
然后,如果您只是在寻找 D1,则 D1 的等式是:
D1: P1^2 + P2 ^2 =0 (x,y,z verify P1 =0 an P2 =0 )
D1 : n1.(x-xA,y-yA,z-zA)^2 + n2.(x-xB,y-yB,z-zB)^2 =0
(因此,仅通过矩形的表达式,您就可以得到带有封闭公式的 D1 方程。)
现在让我们看看交叉点:
R1中的4个点是:
{ A, A+u, A+v, A+u+v }
如3D 线相交算法中所述:
D1 inter [A,A+u] = I1
D1 inter [A,A+v] = I2
D1 inter [A+u,A+u+v] = I3
D1 inter [A+v,A+u+v] = I4
(I1,I2,I3,I4 可以为空)
same for D2 you get I1' I2' I3' I4'
如果 Ij'=Ik' != null 那么它是一个交点
如果你一步一步正确地做到了,你应该得到正确的解决方案; 除非我没有完全理解这个问题......
该程序计算通过两个矩形的平面的交线。 然后程序寻找这条线和其中一个矩形的边缘之间的交点。 它返回两个相交点,这两个点都找到了。 我不打算讨论这是否明智,因为我不知道该程序的上下文。
让我们通过代码检查 go 并寻找可能出错的地方。
该程序计算通过这两个平面的线,如下所示:
Vector3 LineDirection = Normal.Cross(SecondOne.Normal);
float d1 = this.GetDistance(LineDirection);
float d2 = SecondOne.GetDistance(LineDirection);
temp = (LineDirection - (this.Normal * d1) - (SecondOne.Normal * d2));
temp.x = Math.Abs((float)Math.Round((decimal)FirstPoint.x, 2));
temp.y = Math.Abs((float)Math.Round((decimal)FirstPoint.y, 2));
Line line;
line.direction = LineDirection;
line.point = temp;
线方向的计算是可以的,但是point
的计算是错误的,你可能知道。 但我会假装我们有一个有效的点和方向,并继续执行程序的 rest。
该程序调用AreLinesParallel()
以消除与通过平面的线平行的边。 代码如下所示:
Vector3 vector = (first.secondPoint - first.firstPoint);
vector.Normalize();
float kl = 0, km = 0, kn = 0;
if (vector.x != aSecondLine.direction.x)
{
if (vector.x != 0 && aSecondLine.direction.x != 0)
{
kl = vector.x / aSecondLine.direction.x;
}
}
if (vector.y != aSecondLine.direction.y)
{
if (vector.y != 0 && aSecondLine.direction.y != 0)
{
km = vector.y / aSecondLine.direction.y;
}
}
if (vector.z != aSecondLine.direction.z)
{
if (vector.z != 0 && aSecondLine.direction.z != 0)
{
kn = vector.z / aSecondLine.direction.z;
}
}
// both if all are null or all are equal, the lines are parallel
return ((kl == km && km == kn));
代码或多或少会检查边缘方向的元素除以线方向的元素是否都彼此相等。 这是一个危险的过程。 由于舍入误差,即使AreLinesParallel()
声称这些线并不真正平行,以后的过程仍可能会被零除。 最好不要使用该程序。
现在是代码的核心,测试边缘和线之间的交点:
float d = 0, dt = 0, dk = 0;
float t = 0, k = 0;
if (Math.Abs(n1.x * n2.y - n2.x * n1.y) > float.Epsilon)
{
d = n1.x * (-n2.y) - (-n2.x) * n1.y;
dt = (p2.x - p1.x) * (-n2.y) - (p2.y - p1.y) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.z * n2.y - n2.z * n1.y) > float.Epsilon)
{
d = n1.z * (-n2.y) - (-n2.z) * n1.y;
dt = (p2.z - p1.z) * (-n2.y) - (p2.y - p1.y) * (-n2.z);
dk = n1.z * (p2.z - p1.z) - n1.y * (p2.y - p1.y);
}
else if (Math.Abs(n1.x * n2.z - n2.x * n1.z) > float.Epsilon)
{
d = n1.x * (-n2.z) - (-n2.x) * n1.z;
dt = (p2.x - p1.x) * (-n2.z) - (p2.z - p1.z) * (-n2.x);
dk = n1.x * (p2.x - p1.x) - n1.z * (p2.z - p1.z);
}
t = dt / d;
k = dk / d;
result = n1 * t + p1;
这段代码的一个错误是缺少解释算法起源的注释。 如果没有记录的算法可参考,则注释可以包含导致公式的推导。 第一个分支处理(x, y)
,第二个处理(y, z)
,第三个处理(z, x)
,所以我假设这些分支解决二维交叉问题并将这些发现提升到 3D。 它计算行列式以检查每个 2D 投影的平行线。 我不应该做这种逆向工程。
无论如何,这是产生NaN
值的代码。 三个分支都没有被触发,所以最后d = 0
,这给出了除以零。 与其依靠AreLinesParallel()
来避免被零除,不如检查实际重要的值,即d
。
当然,代码还需要更多的工作,因为我们还不知道 3D 中的线是否也交叉。 此外,仅当0 <= t && t <= 1
时,该点才位于边缘。 随着早期错误的修复,可能会出现更多错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.