【问题标题】:How to find the area that two triangles are in contact如何找到两个三角形的接触面积
【发布时间】:2021-03-13 14:53:56
【问题描述】:

给定顶点的位置和表面法线。如何计算两个三角形接触的面积(可能为 0)?这些三角形也在 3D 空间中,所以如果它们没有正确排列,只是相互卡住,接触面积应该为 0。 (在 C# 中)

【问题讨论】:

    标签: c# algorithm unity3d geometry


    【解决方案1】:

    这不是一个小问题,所以让我们把它分解成几个步骤。

    1. 检查两个三角形是否为coplanar,否则面积为0。
    2. 将三角形投影到二维表面上
    3. 计算交点多边形
    4. 计算面积

    1。检查共面性

    要使两个三角形共面,一个三角形的所有顶点必须位于另一个三角形所确定的平面内。

    使用here 描述的算法,我们可以检查每个顶点是否是这种情况,但由于浮点数并不完全精确,您需要定义一些误差阈值来确定仍然算作什么共面。

    假设 vavb 分别是三角形 A 和 B 的向量,代码可能看起来像这样。
    (注意:我从未使用过 Unity 并且正在编写所有内存中的代码,如果不是 100% 正确,请原谅)。

    public static bool AreTrianglesCoplanar(Vector3[] va, Vector3[] vb) {
        // make sure these are actually triangles
        Debug.Assert(va.Length == 3);
        Debug.Assert(vb.Length == 3);
    
        // calculate the (scaled) normal of triangle A
        var normal = Vector3.Cross(va[1] - va[0], va[2] - va[0]);
    
        // iterate all vertices of triangle B
        for(var vertex in vb) {
            // calculate the dot product between the normal and the vector va[0] --> vertex
            // the dot product will be 0 (or very small) if the angle between these vectors
            // is a right angle (90°)
            float dot = Vector3.Dot(normal, vertex - va[0]).
    
            // the error threshold
            const float epsilon = 0.001f;
            
            // if the dot product is above the threshold, the vertex lies outside the plane
            // in that case the two triangles are not coplanar
            if(Math.Abs(dot) > epsilon)
                return false;
        }
    
        return true;
    }
    

    2。将三角形投影到二维空间

    我们现在知道所有六个顶点都在嵌入到 3D 空间中的同一个 2D 平面中,但我们所有的顶点坐标仍然是 3D 的。因此,下一步是将我们的点投影到 2D 坐标系中,从而保留它们的相对位置。

    This answer 很好地解释了数学。
    首先,我们需要找到一组三个向量组成一个正交基(它们必须相互正交且长度为 1)。

    1. 其中一个只是平面的法向量,因此我们需要另外两个与第一个正交且彼此正交的向量。
    2. 根据定义,由三角形定义的平面中的所有向量都与法线向量正交,因此我们只需选择一个(例如从va[0]va[1] 的向量)并对其进行归一化。
    3. 第三个向量必须与其他两个向量正交,我们可以通过取前两个向量的叉积来找到这样一个向量。

    我们还需要在平面上选择一个点作为我们的原点,例如va[0]

    使用所有这些参数,并使用链接 amswer 中的公式,我们可以确定我们新的投影 (x, y) 坐标(来自另一个答案的t_1t_2)。请注意——因为我们所有的点都位于定义该法线向量的平面上——第三个坐标(在另一个答案中称为s)将始终(接近)零。

    public static void ProjectTo2DPlane(
            Vector3[] va, Vector3[] vb
            out Vector2[] vaProjected, out Vector2[] vbProjecte
        ) {
        // calculate the three coordinate system axes
        var normal = Vector3.Cross(va[1] - va[0], va[2] - va[0]).normalized;
        var e1 = Vector3.Normalize(va[1] - va[0]);
        var e2 = Vector3.Cross(normal, e1);
    
        // select an origin point
        var origin = va[0];
    
        // projection function we will apply to every vertex
        Vector2 ProjectVertex(Vector3 vertex) {
            float s = Dot(normal, vertex - origin);
            float t1 = Dot(e1, vertex - origin);
            float t2 = Dot(e2, vertex - origin);
            // sanity check: this should be close to zero
            // (otherwise the point is outside the plane)
            Debug.Assert(Math.Abs(s) < 0.001);
            return new Vector2(t1, t2);
        }
    
        // project the vertices using Linq
        vaProjected = va.Select(ProjectVertex).ToArray();
        vbProjected = vb.Select(ProjectVertex).ToArray();
    }
    

    完整性检查:

    • 顶点 va[0] 应投影到 (0, 0)。
    • 顶点 va[1] 应该投影到 (*, 0),所以在新的 x 轴上。

    3。 / 4. 2D计算相交面积

    This answer 这个答案提到了最后一步所需的算法。
    Sutherland-Hodgman algorithm 依次将一个三角形与另一个三角形的每一边剪裁在一起。其结果将是三角形、四边形或空多边形。

    最后,shoelace formula 可用于计算生成的裁剪多边形的面积。

    把所有东西放在一起

    假设你实现了CalculateIntersecionPolygonCalculatePolygonArea这两个函数,最终的相交面积可以这样计算:

    public static float CalculateIntersectionArea(Mesh triangleA, Mesh triangleB) {
        var verticesA = triangleA.GetVertices();
        var verticesB = triangleB.GetVertices();
    
        if(!AreTrianglesCoplanar(verticesA, verticesB))
            return 0f; // the triangles are not coplanar
    
        ProjectTo2DPlane(verticesA, verticesB, out Vector2[] projectedA, out Vector2[] projectedB);
    
        CalculateIntersecionPolygon(projectedA, projectedB, out List<Vector2> intersection);
    
        if(intersection.Count == 0)
            return 0f; // the triangles didn't overlap
    
        return CalculatePolygonArea(intersection);
    }
    

    【讨论】:

    • 抱歉,但我没有,也不知道如何实现 CalculateIntersecionPolygon 和 CalculatePolygonArea 方法。你能帮我做这些吗?我希望能够有一个带有独立逻辑的完成方法供我调用,并在以后理解它。谢谢。
    • @IbrahimDDeveloper 很抱歉,StackOverflow 是来帮助您解决问题的,而不是为您解决问题。如果你对做任何工作都不感兴趣,那么这是错误的地方。到目前为止,您还没有给我任何理由相信您实际上试图自己解决它。请更新问题以显示您尝试了什么以及为什么它不起作用。
    猜你喜欢
    • 2015-07-29
    • 2021-12-21
    • 2014-12-06
    • 1970-01-01
    • 2023-01-30
    • 1970-01-01
    • 2014-02-08
    • 2023-01-31
    • 2014-03-24
    相关资源
    最近更新 更多