3D中两个矩形之间的交点

为了获得3D中两个矩形之间的交线,我将它们转换为平面,然后使用它们法线的叉积得到交线,然后我尝试使线与矩形的每个线段相交。

问题是线条平行于三个线段,并且只与NAN,NAN,NAN中的一个相交,这是完全错误的。 你能告诉我我的代码有什么问题吗?

我使用此链接中的vector3 http://www.koders.com/csharp/fidCA8558A72AF7D3E654FDAFA402A168B8BC23C22A.aspx

并创建了我的飞机课程如下

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 ///  /// 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. ///  ///  ///  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 points = new List(); points.Add(r1); points.Add(r2); points.Add(r3); points.Add(r4); points.RemoveAll( t => ((tx == -1) && (ty == -1) && (tz == -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); } 

这是测试值最好的问候

您需要先指定一件事:

  • 通过3D矩形,您指的是3D平面上的平面矩形。 (不是矩形棱镜)。

假设您的矩形不是共面的,也不是平行的,因此有一条唯一的线D1代表每个矩形描述的平面的交点。

鉴于这种假设,它们是2个矩形R1和R2交叉的4种可能情况:

在此处输入图像描述

(注意:有时D1不会与R1或R2和R1相交,R2可以稍微旋转,因此D1并不总是在平行边相交,而是连续的边)

当两个矩形之间存在交叉时,D1总是在同一交叉点上与R1和R2相交(参见第1和第2张图片)

你的模型不好,因为你的线不能与同一个矩形的3个段平行…

正如你在这个问题中提到的: 3D线交叉算法一旦你得到D1( 获得由两个矩形的交点定义的线段的端点 ),就确定与矩形的每个段的交点。(每个矩形的4个段需要被检查)

然后检查公共交叉点…如果找到一个,那么你的矩形相交。

对不起,直接检查代码非常困难,但我想有了这些和平的信息,你应该能够找到错误。

希望能帮助到你。


编辑:

用点和2个向量定义一个矩形:

 R2 {A ,u ,v} R1 {B, u',v'} 

定义R1和R2描述的平面:P1和P2

P1(相应的P2)的一个正交矢量是n1(相应的n2)。让n1 = u ^ vn2 = 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可以为null)

 same for D2 you get I1' I2' I3' I4' 

如果Ij’= Ik’!= null那么它就是一个交叉点

如果你一步一步正确地做到了这一点,你应该找到正确的解决方案; 除非我没有完全理解这个问题……

程序计算通过两个矩形的平面的交线。 然后程序查找该线与其中一个矩形的边缘之间的交点。 它返回找到这两个点的两个交点。 我不打算辩论这是否是明智之举,因为我不知道该计划的背景。

让我们浏览代码并寻找可能出错的事情。

程序计算通过这两个平面的线,如下所示:

 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的计算是错误的。 但我会假装我们有一个有效的观点和方向,并继续执行该计划的其余部分。

程序调用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) ,所以我假设分支解决了2D中的交集并将这些发现提升到3D。 它计算决定因素以检查每个2D投影的平行线。 我不应该做这种逆向工程。

无论如何,这是产生NaN值的代码。 三个分支都没有被触发,所以最后d = 0 ,这得到除以零。 不要依赖AreLinesParallel()来避免除零,最好检查实际重要的值,即d

当然,代码仍然需要更多工作,因为我们还不知道线条是否也在3D中交叉。 只有当0 <= t && t <= 1该点才在边缘上。 并且可能会出现更多的错误,因为早期的错误正在修复中。