简体   繁体   English

3D 中两个矩形的交点

[英]Intersection between two rectangles in 3D

To get the line of intersection between two rectangles in 3D, I converted them to planes, then get the line of intersection using cross product of their normals, then I try to get the line intersection with each line segment of the rectangle.为了得到 3D 中两个矩形之间的相交线,我将它们转换为平面,然后使用它们的法线的叉积得到相交线,然后我尝试得到与矩形的每个线段的线相交。

The problem is the line is parallel to three segments, and intersect with only one in NAN,NAN,NAN which is totally wrong.问题是这条线平行于三个线段,并且只与 NAN,NAN,NAN 中的一个相交,这是完全错误的。 Can you advise me what's wrong in my code?你能告诉我我的代码有什么问题吗?

I use vector3 from this link http://www.koders.com/csharp/fidCA8558A72AF7D3E654FDAFA402A168B8BC23C22A.aspx我使用此链接中的vector3 http://www.koders.com/csharp/fidCA8558A72AF7D3E654FDAFA402A168B8BC23C22A.aspx

and created my plane class as following并创建了我的飞机 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);



    }

and this is test values Best regards这是测试值最好的问候

You need to specify one thing first:您需要先指定一件事:

  • by 3D rectangle, you mean plane rectangle on a 3D plane. 3D 矩形是指 3D 平面上的平面矩形。 (not a rectangular prism). (不是矩形棱镜)。

Let's say your rectangles are not coplanar nor parallele, and therefore there is one unique line D1 that represents the intersection of the plane described by each rectangle.假设您的矩形既不共面也不平行,因此有一条独特的线 D1 表示每个矩形所描述的平面的交点。

Given this assumption their are 4 possible situations for the intersection of 2 rectangles R1 and R2:鉴于此假设,它们是 2 个矩形 R1 和 R2 相交的 4 种可能情况:

在此处输入图像描述

(note: sometimes D1 doesn't intersect neither R1 nor R2 and R1, R2 can be rotated a little bit so D1 doesn't always intersect on parallele sides, but consecutive sides) (注意:有时 D1 既不与 R1 也不与 R2 和 R1 相交,R2 可以稍微旋转一点,因此 D1 并不总是与平行边相交,而是在连续边上相交)

When there is an intersection between the 2 rectangles, D1 always intersect R1 and R2 on the same intersection (cf 1st and 2nd picture)当两个矩形之间有交点时,D1 总是在同一个交点上与 R1 和 R2 相交(参见第 1 和第 2 张图片)

Your model is not good because your line cannot be parallele to 3 segments of the same rectangle...您的 model 不好,因为您的线不能与同一矩形的 3 段平行...

As you asked in this question: 3D lines intersection algorithm once you have D1 ( Get endpoints of the line segment defined by the intersection of two rectangles ) just determinate the intersection with each segment of the rectangle.(The 4 segments of each rectangles need to be checked)正如您在这个问题中所问的那样: 3D 线相交算法一旦您拥有 D1 ( 获取由两个矩形的交点定义的线段的端点),只需确定与矩形的每个线段的交点。(每个矩形的 4 个线段需要被检查)

Then check for common intersection... if you find one then your rectangles intersect.然后检查共同的交点......如果你找到一个,那么你的矩形相交。

Sorry it's very hard to directly check the code, but I guess with these peaces of information you should be able to find the error.抱歉,很难直接检查代码,但我想通过这些信息和平,您应该能够找到错误。

Hope it helps.希望能帮助到你。


EDIT:编辑:

define a rectangle by a point and 2 vectors:通过一个点和 2 个向量定义一个矩形:

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

define the planes described by R1 and R2: P1 and P2定义由 R1 和 R2 描述的平面:P1 和 P2

One orthogonal vector to P1(resp. P2) is n1 (resp. n2).Let n1 = u ^ v and n2 = u' ^ v ' with: P1(resp. P2) 的一个正交向量是 n1 (resp. n2)。让n1 = u ^ vn2 = u' ^ v ' 有:

在此处输入图像描述

then然后

P1: n1.(x-xA,y-yA,z-zA)=0
P2: n2.(x-xB,y-yB,z-zB)=0

Then if you're just looking for D1 the equation of D1 is:然后,如果您只是在寻找 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

(so just with the expression of your rectangles you can get the equation of D1 with a closed formula.) (因此,仅通过矩形的表达式,您就可以得到带有封闭公式的 D1 方程。)

Now let's look at the intersections:现在让我们看看交叉点:

the 4 points in R1 are: R1中的4个点是:

{ A, A+u, A+v, A+u+v } { A, A+u, A+v, A+u+v }

as describe in 3D lines intersection algorithm do: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 can be null) (I1,I2,I3,I4 可以为空)

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

if Ij'=Ik' != null then it's an intersection point如果 Ij'=Ik' != null 那么它是一个交点

if you did that correctly step by step you should get to the correct solution;如果你一步一步正确地做到了,你应该得到正确的解决方案; unless I didn't fully understand the question...除非我没有完全理解这个问题......

The program computes the line of intersection of the planes passing through two rectangles.该程序计算通过两个矩形的平面的交线。 The program then looks for intersections between this line and the edges of one of the rectangles.然后程序寻找这条线和其中一个矩形的边缘之间的交点。 It returns two points of intersection of such two points are found.它返回两个相交点,这两个点都找到了。 I'm not going to debate whether this is a sensible thing to do since I don't know the context of the program.我不打算讨论这是否明智,因为我不知道该程序的上下文。

Let's go through the code and look for things that could be wrong.让我们通过代码检查 go 并寻找可能出错的地方。

The program computes the line passing through the two planes like this:该程序计算通过这两个平面的线,如下所示:

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;

The computation of the line direction is OK, but the computation of point is wrong, as you probably know.线方向的计算是可以的,但是point的计算是错误的,你可能知道。 But I'll pretend we have a valid point and direction and carry on with the rest of the program.但我会假装我们有一个有效的点和方向,并继续执行程序的 rest。

The program calls AreLinesParallel() to get rid of edges that a parallel to the line through the planes.该程序调用AreLinesParallel()以消除与通过平面的线平行的边。 The code looks like this:代码如下所示:

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));

The code more or less checks that the elements of the direction of the edge divided by the elements of the direction of the line are all equal to each other.代码或多或少会检查边缘方向的元素除以线方向的元素是否都彼此相等。 It's a dangerous procedure to rely on.这是一个危险的过程。 Because of round-off errors, later procedures may still, say, divide by zero, even if AreLinesParallel() claims that the lines aren't really parallel.由于舍入误差,即使AreLinesParallel()声称这些线并不真正平行,以后的过程仍可能会被零除。 It is better not to use the procedure at all.最好不要使用该程序。

Now comes the meat of the code, a test for intersection between the edge and the line:现在是代码的核心,测试边缘和线之间的交点:

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;

A mistake of this code is the lack of a comment that explains the origin of the algorithm.这段代码的一个错误是缺少解释算法起源的注释。 If there is no documented algorithm to refer to, the comment can contain the derivation leading to the formulas.如果没有记录的算法可参考,则注释可以包含导致公式的推导。 The first branch deals with (x, y) , the second with (y, z) and the third with (z, x) , so I assume that the branches solve for intersection in 2D and lift these findings to 3D.第一个分支处理(x, y) ,第二个处理(y, z) ,第三个处理(z, x) ,所以我假设这些分支解决二维交叉问题并将这些发现提升到 3D。 It computes determinants to check for parallel lines for each 2D projection.它计算行列式以检查每个 2D 投影的平行线。 I shouldn't have to do this kind of reverse engineering.我不应该做这种逆向工程。

Anyway, this is the code that produces the NaN values.无论如何,这是产生NaN值的代码。 None of the three branches are triggered, so d = 0 in the end, which gives a division by zero.三个分支都没有被触发,所以最后d = 0 ,这给出了除以零。 Instead of relying on AreLinesParallel() to avoid division by zero, it's is better to check the value that actually matters, namely d .与其依靠AreLinesParallel()来避免被零除,不如检查实际重要的值,即d

Of course, the code still needs more work, because we don't know yet if the lines are crossing in 3D too.当然,代码还需要更多的工作,因为我们还不知道 3D 中的线是否也交叉。 Also the point is on the edge only if 0 <= t && t <= 1 .此外,仅当0 <= t && t <= 1时,该点才位于边缘。 And probably more bugs will show up as the earlier ones are being fixed.随着早期错误的修复,可能会出现更多错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM