检查3D形状是否阻挡了两个3D点可见性

时间:2016-02-16 10:46:36

标签: java geometry

我正在寻找一个方法的更好选项,如果两个3D点可以返回"请参阅"彼此背后的3D"墙" /扁平形状。

我已经用于墙体形状(point1XY,point2XY和mixZ maxZ),效果非常好。

for (ShapeHolder shape : allShapes)
{
    final int denominator = ((ty - y) * (shape.getPoint1X() - shape.getPoint2X())) - ((tx - x) * (shape.getPoint1Y() - shape.getPoint2Y()));
    if (denominator == 0)
    {
        continue;
    }

    final float multiplier1 = (float) (((shape.getPoint2X() - shape.getPoint1X()) * (y - shape.getPoint1Y())) - ((shape.getPoint2Y() - shape.getPoint1Y()) * (x - shape.getPoint1X()))) / denominator;
    final float multiplier2 = (float) (((tx - x) * (y - shape.getPoint1Y())) - ((ty - y) * (x - shape.getPoint1X()))) / denominator;
    if ((multiplier1 >= 0) && (multiplier1 <= 1) && (multiplier2 >= 0) && (multiplier2 <= 1))
    {
        final int intersectZ = Math.round(z + (multiplier1 * (tz - z)));
        if ((intersectZ > shape.getZMin()) && (intersectZ < shape.getZMax()))
        {
            return true;
        }
    }
}
return false;

我的主要问题是如何将此代码用于具有不同Z值的抽象四边形状。 就像输入4面平面3D多边形的坐标和两点的坐标一样,只要两点可见性被形状阻挡,就会返回布尔值。

类似的东西:

public boolean checkIfObjectBetween(int aX, int aY, int aZ, int bX, int bY, int bZ, int p1X, int p1Y, int p1Z, int p2X, int p2Y, int p2Z, int p3X, int p3Y, int p3Z, int p4X, int p4Y, int p4Z)
{
    if (A can see B)
    {
        return true;
    }
    return false;
}

graphic explanation

1 个答案:

答案 0 :(得分:1)

解决方案基本上由

组成
  • 在点之间创建线段
  • 将平面(矩形)划分为两个三角形
  • 检查与每个三角形交叉的线段

对于线段 - 三角形相交测试,可以使用Möller–Trumbore intersection algorithm的自适应。在其原始形式中,这仅指射线。为了将其用于线段,还需要检查线段的两个点是否位于三角形的不同边上。

以下是非常简单直接的解决方案。 (它还包含Möller-Trumbore测试的实现,但未使用 - 仅供参考)。 planeIsBetweenPoints方法通过为两个三角形调用lineSegmentTriangleIntersection来进行相交测试。

请注意,这是一个真正简单的实现,我避免任何外部依赖,使其成为一个独立的示例。在更大的上下文中,人们可以期望像Vector3D这样的类用于基本向量操作,在下面的示例中使用double[3]数组和适当的辅助函数进行模拟。

public class PlaneIsBetweePoints
{
    public static void main(String[] args)
    {
        System.out.println(planeIsBetweenPoints(
            5,5,-5, 
            5,5, 5, 
            0,10,0, 
            10,10,0, 
            10,0,0, 
            0,0,0));
        System.out.println(planeIsBetweenPoints(
            5,5, 5, 
            5,5,-5, 
            0,10,0, 
            10,10,0, 
            10,0,0, 
            0,0,0));
        System.out.println(planeIsBetweenPoints(
            5,5, 5, 
            5,5,10, 
            0,10,0, 
            10,10,0, 
            10,0,0, 
            0,0,0));
        System.out.println(planeIsBetweenPoints(
            5,5,-10, 
            5,5,-5, 
            0,10,0, 
            10,10,0, 
            10,0,0, 
            0,0,0));
    }


    public static boolean planeIsBetweenPoints(
        int p0x, int p0y, int p0z, 
        int p1x, int p1y, int p1z, 
        int v0x, int v0y, int v0z, 
        int v1x, int v1y, int v1z, 
        int v2x, int v2y, int v2z, 
        int v3x, int v3y, int v3z)
    {
        double p0[] = { p0x, p0y, p0z };
        double p1[] = { p1x, p1y, p1z };
        double v0[] = { v0x, v0y, v0z };
        double v1[] = { v1x, v1y, v1z };
        double v2[] = { v2x, v2y, v2z };
        double v3[] = { v3x, v3y, v3z };

        return 
            lineSegmentTriangleIntersection(p0, p1, v0, v1, v2) ||
            lineSegmentTriangleIntersection(p0, p1, v0, v2, v3);
    }

    private static final double EPSILON = 0.000001;
    private static void CROSS(double result[], double v0[], double v1[])
    {
        result[0] = v0[1] * v1[2] - v0[2] * v1[1];
        result[1] = v0[2] * v1[0] - v0[0] * v1[2];
        result[2] = v0[0] * v1[1] - v0[1] * v1[0];
    }

    private static double DOT(double v0[], double v1[])
    {
        return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; 
    }

    private static void SUB(double result[], double v0[], double v1[])
    {
        result[0] = v0[0] - v1[0];
        result[1] = v0[1] - v1[1];
        result[2] = v0[2] - v1[2];
    }

    private static void SCALE(double result[], double v[], double f)
    {
        result[0] = v[0] * f;
        result[1] = v[1] * f;
        result[2] = v[2] * f;
    }

    // Adapted Möller-Trumbore test for line segments
    public static boolean lineSegmentTriangleIntersection(
        double p0[], double p1[],
        double v0[], double v1[], double v2[]) 
    {
        double edge1[] = new double[3];
        double edge2[] = new double[3];
        double tVec[] = new double[3];
        double pVec[] = new double[3];
        double qVec[] = new double[3];
        double dir[] = new double[3];
        double normal[] = new double[3];

        SUB(edge1, v1, v0);
        SUB(edge2, v2, v0);
        SUB(dir, p1, p0);
        SCALE(dir, dir, 1.0 / Math.sqrt(DOT(dir, dir)));
        CROSS(pVec, dir, edge2);
        double det = DOT(edge1, pVec);
        if (det > -EPSILON && det < EPSILON)
        {
            return false;
        }
        double invDet = 1.0 / det;
        SUB(tVec, p0, v0);
        double u = DOT(tVec, pVec) * invDet;
        if (u < 0.0 || u > 1.0)
        {
            return false;
        }
        CROSS(qVec, tVec, edge1);
        double v = DOT(dir, qVec) * invDet;
        if (v < 0.0 || (u + v) > 1.0)
        {
            return false;
        }
        CROSS(normal, edge1, edge2);
        SCALE(normal, normal, 1.0 / Math.sqrt(DOT(normal, normal)));
        double d0 = DOT(normal, p0);
        double d1 = DOT(normal, p1);
        return (d0 > 0) != (d1 > 0);
    }


    //=========================================================================
    // The part below is an overly simple implementation of  
    // https://en.wikipedia.org/wiki/
    //     M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
    // only as a reference

    public static boolean rayTriangleIntersection(
        double origin[], double dir[],
        double v0[], double v1[], double v2[],
        double tuv[]) 
    {
        double edge1[] = new double[3];
        double edge2[] = new double[3];
        double tVec[] = new double[3];
        double pVec[] = new double[3];
        double qVec[] = new double[3];

        SUB(edge1, v1, v0);
        SUB(edge2, v2, v0);
        CROSS(pVec, dir, edge2);
        double det = DOT(edge1, pVec);
        if (det > -EPSILON && det < EPSILON)
        {
            return false;
        }
        double invDet = 1.0 / det;
        SUB(tVec, origin, v0);
        double u = DOT(tVec, pVec) * invDet;
        if (u < 0.0 || u > 1.0)
        {
            return false;
        }
        CROSS(qVec, tVec, edge1);
        double v = DOT(dir, qVec) * invDet;
        if (v < 0.0 || (u + v) > 1.0)
        {
            return false;
        }
        double t = DOT(edge2, qVec) * invDet;
        if (t > EPSILON)
        {
            tuv[0] = t;
            tuv[1] = u;
            tuv[2] = v;
            return true;
        }
        return false;
    }

}