如何确定两个长方体是否相交(包括包含)

时间:2012-03-27 23:15:48

标签: computational-geometry

长方体不会始终与轴对齐。如何确定它们是否相互交叉?

2 个答案:

答案 0 :(得分:2)

分离轴定理(http://en.wikipedia.org/wiki/Separating_axis_theorem)确保在你的情况下(两个长方体) 如果它们不相交,则存在分离平面。因此,众所周知的方法是投影立方体顶点(甚至是Oriented Bounding Box顶点,而不假设它是立方体) 到每个可能的分离轴。立方体的面和成对的交叉乘积都有法线。

设N1,N2,N3为第一个立方体面的法线,M1,M2,M3为第二个立方体面的法线。设A和B. 分别是第一和第二个立方体的中心。

然后你必须将第一个立方体的每个点投影到N1,N2,N3,M1,M2,M3,N1xM1,N1xM2等。

然后检查投影点与另一个立方体中心之间的距离。

C ++中的完整代码(对于vec3类型有一些明显的运算符重载):

bool Intersection_BoxToBox( const Box& ABox, const Box& BBox )
{
    float R, R0, R1;

    eBoxSeparatingAxis SeparatingAxis = S_AXIS_NONE;

    float AxisLen, TmpDepth;

    /// Relative distance
    LVector3 D = BBox.FCenter - ABox.FCenter;

    SeparatingAxis = S_AXIS_NONE;

    /// Test each separating plane with Axes A0..A2, B0..B2 (six cases)

    #define TEST_SEP_PLANE( PARAM_AxisName, PARAM_RelativeVal, PARAM_R0, PARAM_R1, PARAM_Normal ) \
        R  = fabs(PARAM_RelativeVal);       \
        R0 = PARAM_R0;             \
        R1 = PARAM_R1;             \
        /* If (R>R0+R1) Then there is no intersection */\
        TmpDepth = R0 + R1 - R;          \
        if (TmpDepth < 0) return false;         \
                 \
        if (MaxDepth > TmpDepth) {        \
            MaxDepth = TmpDepth;       \
            SeparatingAxis = PARAM_AxisName; \
        }

    float a0 = ABox.FExtent[0];
    float a1 = ABox.FExtent[1];
    float a2 = ABox.FExtent[2];
    float b0 = BBox.FExtent[0];
    float b1 = BBox.FExtent[1];
    float b2 = BBox.FExtent[2];

    /// 1. A0
    float A0D = ABox.FAxis[0].Dot( D );
    float c00 = ABox.FAxis[0].Dot( BBox.FAxis[0] );
    float c01 = ABox.FAxis[0].Dot( BBox.FAxis[1] );
    float c02 = ABox.FAxis[0].Dot( BBox.FAxis[2] );
    TEST_SEP_PLANE( S_AXIS_A0, A0D, a0, b0 * fabs( c00 ) + b1 * fabs( c01 ) + b2 * fabs( c02 ), ABox.FAxis[0] )

    /// 2. A1
    float A1D = D.Dot( ABox.FAxis[1] );
    float c10 = ABox.FAxis[1].Dot( BBox.FAxis[0] );
    float c11 = ABox.FAxis[1].Dot( BBox.FAxis[1] );
    float c12 = ABox.FAxis[1].Dot( BBox.FAxis[2] );
    TEST_SEP_PLANE( S_AXIS_A1, A1D, a1, b0 * fabs( c10 ) + b1 * fabs( c11 ) + b2 * fabs( c12 ), ABox.FAxis[1] )

    /// 3. A2
    float A2D = ABox.FAxis[2].Dot( D );
    float c20 = ABox.FAxis[2].Dot( BBox.FAxis[0] );
    float c21 = ABox.FAxis[2].Dot( BBox.FAxis[1] );
    float c22 = ABox.FAxis[2].Dot( BBox.FAxis[2] );
    TEST_SEP_PLANE( S_AXIS_A2, A2D, a2, b0 * fabs( c20 ) + b1 * fabs( c21 ) + b2 * fabs( c22 ), ABox.FAxis[2] )

    /// 4. B0
    float B0D = BBox.FAxis[0].Dot( D );
    TEST_SEP_PLANE( S_AXIS_B0, B0D, a0 * fabs( c00 ) + a1 * fabs( c01 ) + a2 * fabs( c02 ), b0, BBox.FAxis[0] )

    /// 5. B1
    float B1D = BBox.FAxis[1].Dot( D );
    TEST_SEP_PLANE( S_AXIS_B1, B1D, a0 * fabs( c10 ) + a1 * fabs( c11 ) + a2 * fabs( c12 ), b1, BBox.FAxis[1] )

    /// 6. B2
    float B2D = BBox.FAxis[2].Dot( D );
    TEST_SEP_PLANE( S_AXIS_B2, B2D, a0 * fabs( c20 ) + a1 * fabs( c21 ) + a2 * fabs( c22 ), b2, BBox.FAxis[2] )

    #undef TEST_SEP_PLANE

    /// Now we test the cross-product axes

    #define TEST_SEP_AXIS( PARAM_AxisName, PARAM_DirA, PARAM_DirB, PARAM_RelativeVal, PARAM_R0, PARAM_R1) \
        TempAxis = PARAM_DirA .Cross( PARAM_DirB );     \
        AxisLen  = TempAxis.SqrLength();       \
                    \
        if ( AxisLen > ::Math::EPSILON)           \
        {                    \
            R  = PARAM_RelativeVal;          \
            R0 = PARAM_R0;             \
            R1 = PARAM_R1;             \
                    \
            TmpDepth = R0 + R1 - fabs(R);       \
            if (TmpDepth < 0) return false;         \
                    \
            if (MaxDepth * AxisLen > TmpDepth )     \
            {                 \
                MaxDepth = TmpDepth / AxisLen;      \
                SeparatingAxis = PARAM_AxisName; \
            }                 \
        }

    ///  7.-15.       Name        DirA            DirB                RelVal                      R0                           R1
    TEST_SEP_AXIS( S_AXIS_A0B0, ABox.FAxis[0], BBox.FAxis[0] , c10 * A2D - c20 * A1D,  a1 * fabs( c20 ) + a2 * fabs( c10 ), b1 * fabs( c02 ) + b2 * fabs( c01 ) )
    TEST_SEP_AXIS( S_AXIS_A0B1, ABox.FAxis[0], BBox.FAxis[1] , c11 * A2D - c21 * A1D,  a1 * fabs( c21 ) + a2 * fabs( c11 ), b0 * fabs( c02 ) + b2 * fabs( c00 ) )
    TEST_SEP_AXIS( S_AXIS_A0B2, ABox.FAxis[0], BBox.FAxis[2] , c12 * A2D - c22 * A1D,  a1 * fabs( c22 ) + a2 * fabs( c12 ), b0 * fabs( c01 ) + b1 * fabs( c00 ) )
    TEST_SEP_AXIS( S_AXIS_A1B0, ABox.FAxis[1], BBox.FAxis[0] , c20 * A0D - c00 * A2D,  a0 * fabs( c20 ) + a2 * fabs( c00 ), b1 * fabs( c12 ) + b2 * fabs( c11 ) )
    TEST_SEP_AXIS( S_AXIS_A1B1, ABox.FAxis[1], BBox.FAxis[1] , c21 * A0D - c01 * A2D,  a0 * fabs( c21 ) + a2 * fabs( c01 ), b0 * fabs( c12 ) + b2 * fabs( c10 ) )
    TEST_SEP_AXIS( S_AXIS_A1B2, ABox.FAxis[1], BBox.FAxis[2] , c22 * A0D - c02 * A2D,  a0 * fabs( c22 ) + a2 * fabs( c02 ), b0 * fabs( c11 ) + b1 * fabs( c10 ) )
    TEST_SEP_AXIS( S_AXIS_A2B0, ABox.FAxis[2], BBox.FAxis[0] , c00 * A1D - c10 * A0D,  a0 * fabs( c10 ) + a1 * fabs( c00 ), b1 * fabs( c22 ) + b2 * fabs( c21 ) )
    TEST_SEP_AXIS( S_AXIS_A2B1, ABox.FAxis[2], BBox.FAxis[1] , c01 * A1D - c11 * A0D,  a0 * fabs( c11 ) + a1 * fabs( c01 ), b0 * fabs( c22 ) + b2 * fabs( c20 ) )
    TEST_SEP_AXIS( S_AXIS_A2B2, ABox.FAxis[2], BBox.FAxis[2] , c02 * A1D - c12 * A0D,  a0 * fabs( c12 ) + a1 * fabs( c02 ), b0 * fabs( c21 ) + b1 * fabs( c20 ) )

    if ( SeparatingAxis == S_AXIS_NONE ) { return false; }

    #undef TEST_SEP_AXIS

    return true;
}

答案 1 :(得分:0)

您首先要确定其中一个长方体的方向。然后将其他坐标转换为该坐标系。您还可以使用第一个长方体的尺寸来轻松设置每个轴上长方体左/内/右边的点的条件。

现在很容易检查是否

  • 顶点位于长方体内,即所有三个维度中的坐标都在内部=&gt;交叉或含有
  • 边缘(由两个顶点表示)与长方体的面相交。当两个属性顶点位于长方体的同一侧(左侧和左侧或右侧和右侧)时,您不必检查它。

如果每个顶点对的每个坐标对(在每个维度中)都在(左右和左右),则包含长方体,而不是包含(。)