为任何网格的形式凹/凸排序网格顶点?

问题描述

enter image description here

我有一个 3D 网格,我想要做的是绘制顶部 2D 面的轮廓,所以在这里我可以访问顶部网格顶点和三角形索引,我想要的是对顶点进行排序,以便我可以画出我的轮廓。

 List<Vector3> GetSortedVerticesTest(Dictionary<CreateMesh.Faces,MeshData> mesh,Mesh mainMesh)
{
    List<Vector3> borderVertices = mesh[CreateMesh.Faces.Top].vertices.Where(p => mesh[CreateMesh.Faces.other].vertices.Any(e => e == p)).ToList();
    borderVertices = borderVertices.distinct().ToList();
    List<Vector3> sortedVertices = new List<Vector3>();
    List<PointAngle> candidateAngles = new List<PointAngle>();
    Vector3 startingPoint = borderVertices[0];
    sortedVertices.Add(startingPoint);
    borderVertices.Remove(startingPoint);
    while (borderVertices.Count > 0)
    {
        List<Vector3> candidates = new List<Vector3>();
        for (int i = 0; i < mesh[CreateMesh.Faces.Top].triangles.Count(); i += 3)
        {
            bool commonTriangle = false;
            // verifie que le vertice courant est compris dans le triangle
            for (int k = 0; k < 3; k++)
            {
                int triIndex = mesh[CreateMesh.Faces.Top].triangles[i + k];
                Vector3 posToCompare = mainMesh.vertices[triIndex];

                if (startingPoint == posToCompare)
                {
                    commonTriangle = true;
                }
            }

            if (commonTriangle)
            {
                for (int k = 0; k < 3; k++)
                {
                    int triIndex = mesh[CreateMesh.Faces.Top].triangles[i + k];
                    Vector3 posToCompare = mainMesh.vertices[triIndex];
                    if (borderVertices.Contains(posToCompare))//epsilon
                        candidates.Add(posToCompare);

                }
                candidates.Remove(startingPoint);
                if (candidates.Count != 0)
                {
                    candidates.ForEach(c => candidateAngles.Add(new PointAngle { angle = Vector3.SignedAngle(c - startingPoint,transform.forward,Vector3.up),point = c }));
                    double min = candidateAngles.Min(kvp => kvp.angle);
                    var a = candidateAngles[0].point;
                    PointAngle pointsToKeep = new PointAngle();
                    pointsToKeep = candidateAngles.Where(s => s.angle == min).First();
                    sortedVertices.Add(pointsToKeep.point);
                    borderVertices.Remove(pointsToKeep.point);
                    print("vert coun" + borderVertices.Count);
                    startingPoint = pointsToKeep.point;
                    candidateAngles.Clear();
                }
            }
        }
    }
    return sortedVertices;
}

在这里,我试图让所有候选人获得下一个顶点

解决方法

我没有方便地访问 unity3D,以下示例将使用 MeshGeometry3D。但我希望这些课程足够相似,以便于翻译。

这应该为通过一些任意标准的三角形生成边界边缘列表。我假设这是您要实现的实际目标。边被表示为网格顶点列表的一对索引。

应该可以简单地绘制每条边。还应该可以通过一些额外的处理将边缘连接到线带。

            public static IEnumerable<(int e1,int e2)> GetBorderEdges(MeshGeometry3D mesh,Func<Vector3D,bool > triangleNormalFilter )
    {
        var numTriangles = mesh.TriangleIndices.Count / 3;
        
        // use a value tuple to represent edges,// value tuples have a default equal/getHashCode,so can be used in HashSet
        // Since (1,2) and (2,1) are considered the same edge,// all edges should be ordered smallest index first.
        var allEdges = new HashSet<(int e1,int e2)>();
        var nonBorderEdges = new HashSet<(int e1,int e2)>();
        for (int i = 0; i < numTriangles; i++)
        {
            var normal = mesh.Normals[i];

            // Only process triangles that pass some test,// like normals pointing up (assuming normals exist)
            if (triangleNormalFilter(normal))
            {
                // Multiply by three to get the index of the first value in the TriangleIndices list
                var triangleIndex = i * 3;
                foreach (var edge in GetEdges(mesh,triangleIndex))
                {
                    // If the edge is already in allEdges,Add will return false
                    if (!allEdges.Add(edge))
                    {
                        // Since the edge must be part of two triangles,it is a non border edge
                        nonBorderEdges.Add(edge);
                    }
                }
            }
        }

        // Remove non border edges and return the result
        allEdges.ExceptWith(nonBorderEdges);

        return allEdges;
    }

    // return all edges for a triangle
    private static (int e1,int e2)[] GetEdges(MeshGeometry3D mesh,int triangleIndex)
    {
        var t = triangleIndex;
        var ti = mesh.TriangleIndices;
        return new[]
        {
            CreateEdge(ti[triangleIndex],ti[t + 1]),CreateEdge(ti[triangleIndex + 1],ti[t + 2]),CreateEdge(ti[triangleIndex + 2],ti[t]),};
    }

    // ensures edges are always created smaller index first
    private static (int e1,int e2) CreateEdge(int e1,int e2)
    {
        if (e2 < e1)
        {
            return (e2,e1);
        }
        return (e1,e2);
    }