Пересечение между двумя прямоугольниками в 3D - PullRequest
11 голосов
/ 14 августа 2011

Чтобы получить линию пересечения между двумя прямоугольниками в 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
    /// <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);



    }

, и этозначения теста С наилучшими пожеланиями

Ответы [ 2 ]

12 голосов
/ 22 августа 2011

Сначала нужно указать одну вещь:

  • под трехмерным прямоугольником вы подразумеваете прямоугольник плоскости на трехмерной плоскости. (не прямоугольная призма).

Скажем, ваши прямоугольники не копланарны и не параллельны, и поэтому существует одна уникальная линия D1, которая представляет пересечение плоскости, описываемой каждым прямоугольником.

С учетом этого предположения существуют 4 возможных ситуации пересечения двух прямоугольников R1 и R2:

enter image description here

(примечание: иногда D1 не пересекается ни с R1, ни с R2, ни с R1, R2 можно немного повернуть, поэтому D1 не всегда пересекается на параллельных сторонах, но на последовательных сторонах)

Когда есть пересечение между 2 прямоугольниками, D1 всегда пересекают R1 и R2 на одном и том же пересечении (ср. 1-е и 2-е изображение)

Ваша модель не подходит, потому что ваша линия не может быть параллельна 3 сегментам одного и того же прямоугольника ...

Как вы задали в этом вопросе: Алгоритм пересечения трехмерных линий после того, как у вас есть D1 ( Получить конечные точки отрезка, определенного пересечением двух прямоугольников ), просто определите пересечение с каждый сегмент прямоугольника. (4 сегмента каждого прямоугольника необходимо проверить)

Затем проверьте общее пересечение ... если вы найдете такое, ваши прямоугольники пересекаются.

Извините, что очень трудно напрямую проверить код, но я думаю, что с помощью этой информации вы сможете найти ошибку.

Надеюсь, это поможет.


EDIT:

определить прямоугольник точкой и 2 векторами:

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

определяют плоскости, описанные R1 и R2: P1 и P2

Один ортогональный вектор к P1 (соответственно P2) равен n1 (соответственно n2). Пусть n1 = u ^ v и n2 = u' ^ v 'с:

enter image description here

тогда

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 по замкнутой формуле.)

Теперь давайте посмотрим на пересечения:

4 точки в R1:

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

как описано в Алгоритм пересечения трехмерных линий do:

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'! = Ноль, то это точка пересечения

если вы сделали это правильно шаг за шагом, вы должны найти правильное решение; если я не до конца понял вопрос ...

3 голосов
/ 26 августа 2011

Программа вычисляет линию пересечения плоскостей, проходящих через два прямоугольника.Затем программа ищет пересечения между этой линией и краями одного из прямоугольников.Возвращает две точки пересечения, такие две найденные точки найдены.Я не собираюсь спорить, разумно ли это делать, поскольку я не знаю контекста программы.

Давайте рассмотрим код и поищем вещи, которые могут быть неправильными.

Программа вычисляет линию, проходящую через две плоскости, следующим образом:

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.И, вероятно, по мере исправления более ранних ошибок будет отображаться больше ошибок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...