예제 #1
0
        private static void InsertEdge(TriMesh mesh, TriMesh.HalfEdge inner, TriMesh.HalfEdge outer)
        {
            //TriMesh.Edge left = new TriMesh.Edge();
            //left.HalfEdge0 = outer;
            //outer.Edge = left;
            //inner.Next.Edge = left;

            //TriMesh.Edge right = outer.Edge;
            //right.HalfEdge0 = inner;
            //inner.Edge = right;
            //outer.Opposite.Edge = right;

            //inner.Opposite = outer.Opposite;
            //inner.Next.Opposite = outer;
            //outer.Opposite.Opposite = inner;
            //outer.Opposite = inner.Next;
            inner.Opposite          = outer.Opposite;
            inner.Next.Opposite     = outer;
            outer.Opposite.Opposite = inner;
            outer.Opposite          = inner.Next;
            outer.Edge.HalfEdge0    = outer;

            TriMesh.Edge edge = new TriMesh.Edge();
            edge.HalfEdge0      = inner;
            inner.Edge          = edge;
            inner.Opposite.Edge = edge;
            inner.Next.Edge     = outer.Edge;

            mesh.AppendToEdgeList(edge);
        }
예제 #2
0
        TriMesh.Edge CreateNewEdge(TriMesh.Vertex from, TriMesh.Vertex to)
        {
            TriMesh mesh = (TriMesh)from.Mesh;

            // Create new edge
            TriMesh.Edge edge = new TriMesh.Edge();
            mesh.AppendToEdgeList(edge);
            // Create new halfedges
            TriMesh.HalfEdge hf0 = new TriMesh.HalfEdge();
            mesh.AppendToHalfedgeList(hf0);
            hf0.Opposite = new TriMesh.HalfEdge();
            mesh.AppendToHalfedgeList(hf0.Opposite);
            // Connect opposite halfedge to inner halfedge
            hf0.Opposite.Opposite = hf0;
            // Connect edge to halfedges
            edge.HalfEdge0 = hf0;
            // Connect halfedges to edge
            hf0.Edge          = edge;
            hf0.Opposite.Edge = edge;
            // Connect halfedges to vertices
            hf0.ToVertex          = to;
            hf0.Opposite.ToVertex = from;
            // Connect vertex to outgoing halfedge if it doesn't have one yet
            if (from.HalfEdge == null)
            {
                from.HalfEdge = hf0;
            }
            return(edge);
        }
예제 #3
0
        public static TriMesh.Vertex VertexSplit(TriMesh.Vertex v1, TriMesh.Vertex share1,
                                                 TriMesh.Vertex share2, Vector3D v1Position, Vector3D v2Position, int fixedIndex)
        {
            TriMesh.HalfEdge[] hfs = FindGroup(v1, share1, share2);

            TriMesh mesh = (TriMesh)v1.Mesh;

            v1.Traits.Position = v1Position;
            v1.HalfEdge        = hfs[0];

            TriMesh.Vertex v2 = new TriMesh.Vertex();
            v2.Traits            = new VertexTraits(v2Position);
            v2.Traits.FixedIndex = fixedIndex;
            v2.HalfEdge          = hfs[1];
            mesh.AppendToVertexList(v2);

            for (int i = 0; i < hfs.Length - 1; i++)
            {
                hfs[i].Opposite.ToVertex = v2;
            }

            TriMesh.HalfEdge[] triangle1 = AddInnerTriangle(mesh, v1, v2, share1);
            InsertEdge(mesh, triangle1[1], hfs[0]);

            TriMesh.HalfEdge[] triangle2 = AddInnerTriangle(mesh, v2, v1, share2);
            InsertEdge(mesh, triangle2[1], hfs[hfs.Length - 1]);

            TriMesh.Edge edge = new TriMesh.Edge();
            edge.HalfEdge0        = triangle1[0];
            triangle1[0].Edge     = edge;
            triangle2[0].Edge     = edge;
            triangle1[0].Opposite = triangle2[0];
            triangle2[0].Opposite = triangle1[0];
            mesh.AppendToEdgeList(edge);

            return(v2);
        }
예제 #4
0
        /// <summary>
        /// Adds a face to the mesh with the specified face traits.
        /// </summary>
        /// <param name="faceTraits">The custom traits for the face to add to the mesh.</param>
        /// <param name="faceVertices">The vertices of the face in counterclockwise order.</param>
        /// <returns>The face created by this method.</returns>
        /// <exception cref="BadTopologyException">
        /// Thrown when fewer than three vertices are given or the vertices cannot form a valid face.
        /// </exception>
        /// <exception cref="ArgumentNullException">Thrown when a null vertex is given.</exception>
        private static TriMesh.Face CreateFace(TriMesh mesh, params TriMesh.Vertex[] faceVertices)
        {
            int n = faceVertices.Length;

            // Require at least 3 vertices
            if (n < 3)
            {
                throw new BadTopologyException("Cannot create a polygon with fewer than three vertices.");
            }
            TriMesh.Edge       e;
            TriMesh.Face       f;
            TriMesh.HalfEdge[] faceHalfedges = new TriMesh.HalfEdge[n];
            bool[]             isNewEdge     = new bool[n], isUsedVertex = new bool[n];
            for (int i = 0; i < n; i++)
            {
                int j = (i + 1) % n;
                faceHalfedges[i] = faceVertices[i].FindHalfedgeTo(faceVertices[j]);
            }

            // Make sure input is (mostly) acceptable before making any changes to the mesh
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;
                if (faceVertices[i] == null)
                {
                    throw new ArgumentNullException("Can't add a null vertex to a face.");
                }
                if (!faceVertices[i].OnBoundary)
                {
                    throw new BadTopologyException("Can't add an edge to a vertex on the interior of a mesh.");
                }
                // Find existing halfedges for this face
                faceHalfedges[i] = faceVertices[i].FindHalfedgeTo(faceVertices[j]);
                isNewEdge[i]     = (faceHalfedges[i] == null);
                isUsedVertex[i]  = (faceVertices[i].HalfEdge != null);
                if (!isNewEdge[i] && !faceHalfedges[i].OnBoundary)
                {
                    throw new BadTopologyException("Can't add more than two faces to an edge.");
                }
            }
            // Create face
            f = new TriMesh.Face(default(FaceTraits));
            mesh.AppendToFaceList(f);
            // Create new edges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;
                if (isNewEdge[i])
                {
                    // Create new edge
                    e = new TriMesh.Edge();
                    mesh.AppendToEdgeList(e);
                    // Create new halfedges
                    faceHalfedges[i] = new TriMesh.HalfEdge();
                    mesh.AppendToHalfedgeList(faceHalfedges[i]);
                    faceHalfedges[i].Opposite = new TriMesh.HalfEdge();
                    mesh.AppendToHalfedgeList(faceHalfedges[i].Opposite);
                    // Connect opposite halfedge to inner halfedge
                    faceHalfedges[i].Opposite.Opposite = faceHalfedges[i];
                    // Connect edge to halfedges
                    e.HalfEdge0 = faceHalfedges[i];
                    // Connect halfedges to edge
                    faceHalfedges[i].Edge          = e;
                    faceHalfedges[i].Opposite.Edge = e;
                    // Connect halfedges to vertices
                    faceHalfedges[i].ToVertex          = faceVertices[j];
                    faceHalfedges[i].Opposite.ToVertex = faceVertices[i];
                    // Connect vertex to outgoing halfedge if it doesn't have one yet
                    if (faceVertices[i].HalfEdge == null)
                    {
                        faceVertices[i].HalfEdge = faceHalfedges[i];
                    }
                }
                if (faceHalfedges[i].Face != null)
                {
                    throw new BadTopologyException("An inner halfedge already has a face assigned to it.");
                }
                // Connect inner halfedge to face
                faceHalfedges[i].Face = f;
            }

            // Connect next/previous halfedges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;

                // Outer halfedges
                if (isNewEdge[i] && isNewEdge[j] && isUsedVertex[j])  // Both edges are new and vertex has faces connected already
                {
                    TriMesh.HalfEdge closeHalfedge = null;
                    // Find the closing halfedge of the first available opening
                    foreach (TriMesh.HalfEdge h in faceVertices[j].HalfEdges)
                    {
                        if (h.Face == null)
                        {
                            closeHalfedge = h;
                            break;
                        }
                    }
                    TriMesh.HalfEdge openHalfedge = closeHalfedge.Previous;
                    // Link new outer halfedges into this opening
                    faceHalfedges[i].Opposite.Previous = openHalfedge;
                    openHalfedge.Next = faceHalfedges[i].Opposite;
                    faceHalfedges[j].Opposite.Next = closeHalfedge;
                    closeHalfedge.Previous         = faceHalfedges[j].Opposite;
                }
                else if (isNewEdge[i] && isNewEdge[j])  // Both edges are new
                {
                    faceHalfedges[i].Opposite.Previous = faceHalfedges[j].Opposite;
                    faceHalfedges[j].Opposite.Next     = faceHalfedges[i].Opposite;
                }
                else if (isNewEdge[i] && !isNewEdge[j])  // This is new, next is old
                {
                    faceHalfedges[i].Opposite.Previous = faceHalfedges[j].Previous;
                    faceHalfedges[j].Previous.Next     = faceHalfedges[i].Opposite;
                }
                else if (!isNewEdge[i] && isNewEdge[j])  // This is old, next is new
                {
                    faceHalfedges[i].Next.Previous = faceHalfedges[j].Opposite;
                    faceHalfedges[j].Opposite.Next = faceHalfedges[i].Next;
                }
                // Relink faces before adding new edges if they are in the way of a new face
                else if (!isNewEdge[i] && !isNewEdge[j] && faceHalfedges[i].Next != faceHalfedges[j])
                {
                    TriMesh.HalfEdge closeHalfedge = faceHalfedges[i].Opposite;
                    // Find the closing halfedge of the opening opposite the opening halfedge i is on
                    do
                    {
                        closeHalfedge = closeHalfedge.Previous.Opposite;
                    } while (closeHalfedge.Face != null && closeHalfedge != faceHalfedges[j] &&
                             closeHalfedge != faceHalfedges[i].Opposite);
                    if (closeHalfedge == faceHalfedges[j] || closeHalfedge == faceHalfedges[i].Opposite)
                    {
                        throw new BadTopologyException("Unable to find an opening to relink an existing face.");
                    }
                    TriMesh.HalfEdge openHalfedge = closeHalfedge.Previous;
                    // Remove group of faces between two openings, close up gap to form one opening
                    openHalfedge.Next = faceHalfedges[i].Next;
                    faceHalfedges[i].Next.Previous = openHalfedge;
                    // Insert group of faces into target opening
                    faceHalfedges[j].Previous.Next = closeHalfedge;
                    closeHalfedge.Previous         = faceHalfedges[j].Previous;
                }
                // Inner halfedges
                faceHalfedges[i].Next     = faceHalfedges[j];
                faceHalfedges[j].Previous = faceHalfedges[i];
            }
            // Connect face to an inner halfedge
            f.HalfEdge = faceHalfedges[0];
            return(f);
        }
예제 #5
0
        public TriMesh.Vertex VertexSplit1(TriMesh.Vertex v, TriMesh.Vertex vshard1, TriMesh.Vertex vshard2, Vector3D v1Position, Vector3D v2Position, int v2FixedIndex)
        {
            //1.Get two group of verties
            TriMesh.HalfEdge[] processGroup = FindGroup(v, vshard1, vshard2);

            TriMesh mesh = (TriMesh)v.Mesh;

            TriMesh.Vertex v1        = null;
            TriMesh.Vertex v2        = null;
            TriMesh.Vertex newVertex = null;

            v1 = v;
            v.Traits.Position = v1Position;
            v2        = new TriMesh.Vertex();
            v2.Traits = new VertexTraits(Vector3D.Zero);
            newVertex = v2;
            newVertex.Traits.FixedIndex = v2FixedIndex;
            v2.Mesh            = v.Mesh;
            v2.Traits.Position = v2Position;

            //2.Process the Topology
            TriMesh.HalfEdge hf1Origin = processGroup[0];
            TriMesh.HalfEdge hf2Origin = processGroup[processGroup.Length - 1];

            //Add new edge
            TriMesh.HalfEdge hf3        = new TriMesh.HalfEdge();
            TriMesh.HalfEdge hf3Oppsite = new TriMesh.HalfEdge();
            TriMesh.Edge     edge       = new TriMesh.Edge();

            hf3.Opposite        = hf3Oppsite;
            hf3Oppsite.Opposite = hf3;

            edge.HalfEdge0  = hf3;
            edge.HalfEdge1  = hf3Oppsite;
            hf3.Edge        = edge;
            hf3Oppsite.Edge = edge;

            hf3.ToVertex        = v2;
            hf3Oppsite.ToVertex = v1;

            //Handle hf1Origin which is outter hafledge [INNER]
            TriMesh.HalfEdge hf1 = new TriMesh.HalfEdge();
            hf1.Opposite = hf1Origin;
            hf1.ToVertex = v1;

            TriMesh.HalfEdge hf1Other = new TriMesh.HalfEdge();
            hf1Other.Opposite = hf1Origin.Opposite;
            hf1Other.ToVertex = hf1Origin.ToVertex;

            hf1.Previous  = hf1Other;
            hf1Other.Next = hf1;

            hf1.Next          = hf3;
            hf3.Previous      = hf1;
            hf1Other.Previous = hf3;
            hf3.Next          = hf1Other;

            //Handle hf2Origin which is inner hafledge [INNER]
            TriMesh.HalfEdge hf2 = new TriMesh.HalfEdge();
            hf2.Opposite = hf2Origin;
            hf2.ToVertex = v2;

            TriMesh.HalfEdge hf2Other = new TriMesh.HalfEdge();
            hf2Other.Opposite = hf2Origin.Opposite;
            hf2Other.ToVertex = hf2Origin.ToVertex;

            hf2.Previous  = hf2Other;
            hf2Other.Next = hf2;

            hf2.Next            = hf3Oppsite;
            hf3Oppsite.Previous = hf2;
            hf2Other.Previous   = hf3Oppsite;
            hf3Oppsite.Next     = hf2Other;


            TriMesh.Face face1 = new TriMesh.Face();
            TriMesh.Face face2 = new TriMesh.Face();

            face1.HalfEdge = hf3;
            hf3.Face       = face1;
            hf1.Face       = face1;
            hf1Other.Face  = face1;

            face2.HalfEdge  = hf3Oppsite;
            hf3Oppsite.Face = face2;
            hf2.Face        = face2;
            hf2Other.Face   = face2;

            //Process the outside
            TriMesh.Edge     edge1            = new TriMesh.Edge();
            TriMesh.HalfEdge hf1OriginOppsite = hf1Origin.Opposite;

            hf1Origin.Opposite = hf1;
            hf1.Edge           = hf1Origin.Edge;

            hf1OriginOppsite.Opposite = hf1Other;
            hf1OriginOppsite.ToVertex = v2;
            hf1OriginOppsite.Edge     = edge1;
            hf1Other.Edge             = edge1;
            edge1.HalfEdge0           = hf1Other;
            edge1.HalfEdge1           = hf1OriginOppsite;

            TriMesh.Edge     edge2            = new TriMesh.Edge();
            TriMesh.HalfEdge hf2OriginOppsite = hf2Origin.Opposite;

            hf2Origin.Opposite = hf2;
            hf2.Edge           = hf2Origin.Edge;

            hf2OriginOppsite.Opposite = hf2Other;
            hf2OriginOppsite.ToVertex = v1;
            hf2OriginOppsite.Edge     = edge2;
            hf2Other.Edge             = edge2;
            edge2.HalfEdge0           = hf2Other;
            edge2.HalfEdge1           = hf2OriginOppsite;

            v1.HalfEdge = hf1Origin;
            v2.HalfEdge = hf2Origin;

            mesh.AppendToEdgeList(edge);
            mesh.AppendToEdgeList(edge1);
            mesh.AppendToEdgeList(edge2);
            mesh.AppendToFaceList(face1);
            mesh.AppendToFaceList(face2);
            mesh.AppendToHalfedgeList(hf1);
            mesh.AppendToHalfedgeList(hf1Other);
            mesh.AppendToHalfedgeList(hf2);
            mesh.AppendToHalfedgeList(hf2Other);
            mesh.AppendToHalfedgeList(hf3);
            mesh.AppendToHalfedgeList(hf3Oppsite);
            mesh.AppendToVertexList(newVertex);

            for (int i = 1; i < processGroup.Length - 1; i++)
            {
                processGroup[i].Opposite.ToVertex = newVertex;
            }


            //mesh.FixIndex();

            return(newVertex);
        }
예제 #6
0
        private static void InsertEdge(TriMesh mesh, TriMesh.HalfEdge inner, TriMesh.HalfEdge outer)
        {
            //TriMesh.Edge left = new TriMesh.Edge();
            //left.HalfEdge0 = outer;
            //outer.Edge = left;
            //inner.Next.Edge = left;

            //TriMesh.Edge right = outer.Edge;
            //right.HalfEdge0 = inner;
            //inner.Edge = right;
            //outer.Opposite.Edge = right;

            //inner.Opposite = outer.Opposite;
            //inner.Next.Opposite = outer;
            //outer.Opposite.Opposite = inner;
            //outer.Opposite = inner.Next;
            inner.Opposite = outer.Opposite;
            inner.Next.Opposite = outer;
            outer.Opposite.Opposite = inner;
            outer.Opposite = inner.Next;
            outer.Edge.HalfEdge0 = outer;

            TriMesh.Edge edge = new TriMesh.Edge();
            edge.HalfEdge0 = inner;
            inner.Edge = edge;
            inner.Opposite.Edge = edge;
            inner.Next.Edge = outer.Edge;

            mesh.AppendToEdgeList(edge);
        }
예제 #7
0
        /// <summary>
        /// Adds a face to the mesh with the specified face traits.
        /// </summary>
        /// <param name="faceTraits">The custom traits for the face to add to the mesh.</param>
        /// <param name="faceVertices">The vertices of the face in counterclockwise order.</param>
        /// <returns>The face created by this method.</returns>
        /// <exception cref="BadTopologyException">
        /// Thrown when fewer than three vertices are given or the vertices cannot form a valid face.
        /// </exception>
        /// <exception cref="ArgumentNullException">Thrown when a null vertex is given.</exception>
        private static TriMesh.Face CreateFace(TriMesh mesh, params TriMesh.Vertex[] faceVertices)
        {
            int n = faceVertices.Length; 
            // Require at least 3 vertices
            if (n < 3)
            {
                throw new BadTopologyException("Cannot create a polygon with fewer than three vertices.");
            }  
            TriMesh.Edge e;
            TriMesh.Face f;
            TriMesh.HalfEdge[] faceHalfedges = new TriMesh.HalfEdge[n];
            bool[] isNewEdge = new bool[n], isUsedVertex = new bool[n]; 
            for (int i = 0; i < n; i++)
            {
                int j = (i + 1) % n; 
                faceHalfedges[i] = faceVertices[i].FindHalfedgeTo(faceVertices[j]);

            }

            // Make sure input is (mostly) acceptable before making any changes to the mesh
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n; 
                if (faceVertices[i] == null)
                {
                    throw new ArgumentNullException("Can't add a null vertex to a face.");
                }
                if (!faceVertices[i].OnBoundary)
                { 
                    throw new BadTopologyException("Can't add an edge to a vertex on the interior of a mesh.");
                } 
                // Find existing halfedges for this face
                faceHalfedges[i] = faceVertices[i].FindHalfedgeTo(faceVertices[j]);
                isNewEdge[i] = (faceHalfedges[i] == null);
                isUsedVertex[i] = (faceVertices[i].HalfEdge != null); 
                if (!isNewEdge[i] && !faceHalfedges[i].OnBoundary)
                { 
                    throw new BadTopologyException("Can't add more than two faces to an edge.");
                }
            } 
            // Create face
            f = new TriMesh.Face(default(FaceTraits));
            mesh.AppendToFaceList(f); 
            // Create new edges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n; 
                if (isNewEdge[i])
                {
                    // Create new edge
                    e = new TriMesh.Edge();
                    mesh.AppendToEdgeList(e); 
                    // Create new halfedges
                    faceHalfedges[i] = new TriMesh.HalfEdge();
                    mesh.AppendToHalfedgeList(faceHalfedges[i]); 
                    faceHalfedges[i].Opposite = new TriMesh.HalfEdge();
                    mesh.AppendToHalfedgeList(faceHalfedges[i].Opposite); 
                    // Connect opposite halfedge to inner halfedge
                    faceHalfedges[i].Opposite.Opposite = faceHalfedges[i]; 
                    // Connect edge to halfedges
                    e.HalfEdge0 = faceHalfedges[i]; 
                    // Connect halfedges to edge
                    faceHalfedges[i].Edge = e;
                    faceHalfedges[i].Opposite.Edge = e; 
                    // Connect halfedges to vertices
                    faceHalfedges[i].ToVertex = faceVertices[j];
                    faceHalfedges[i].Opposite.ToVertex = faceVertices[i]; 
                    // Connect vertex to outgoing halfedge if it doesn't have one yet
                    if (faceVertices[i].HalfEdge == null)
                    {
                        faceVertices[i].HalfEdge = faceHalfedges[i];
                    }
                } 
                if (faceHalfedges[i].Face != null)
                {
                    throw new BadTopologyException("An inner halfedge already has a face assigned to it.");
                } 
                // Connect inner halfedge to face
                faceHalfedges[i].Face = f; 
            }

            // Connect next/previous halfedges
            for (int i = 0; i < n; ++i)
            {
                int j = (i + 1) % n;

                // Outer halfedges
                if (isNewEdge[i] && isNewEdge[j] && isUsedVertex[j])  // Both edges are new and vertex has faces connected already
                {
                    TriMesh.HalfEdge closeHalfedge = null; 
                    // Find the closing halfedge of the first available opening
                    foreach (TriMesh.HalfEdge h in faceVertices[j].HalfEdges)
                    {
                        if (h.Face == null)
                        {
                            closeHalfedge = h;
                            break;
                        }
                    } 
                    TriMesh.HalfEdge openHalfedge = closeHalfedge.Previous; 
                    // Link new outer halfedges into this opening
                    faceHalfedges[i].Opposite.Previous = openHalfedge;
                    openHalfedge.Next = faceHalfedges[i].Opposite;
                    faceHalfedges[j].Opposite.Next = closeHalfedge;
                    closeHalfedge.Previous = faceHalfedges[j].Opposite;
                }
                else if (isNewEdge[i] && isNewEdge[j])  // Both edges are new
                {
                    faceHalfedges[i].Opposite.Previous = faceHalfedges[j].Opposite;
                    faceHalfedges[j].Opposite.Next = faceHalfedges[i].Opposite;
                }
                else if (isNewEdge[i] && !isNewEdge[j])  // This is new, next is old
                {
                    faceHalfedges[i].Opposite.Previous = faceHalfedges[j].Previous;
                    faceHalfedges[j].Previous.Next = faceHalfedges[i].Opposite;
                }
                else if (!isNewEdge[i] && isNewEdge[j])  // This is old, next is new
                {
                    faceHalfedges[i].Next.Previous = faceHalfedges[j].Opposite;
                    faceHalfedges[j].Opposite.Next = faceHalfedges[i].Next;
                }
                // Relink faces before adding new edges if they are in the way of a new face
                else if (!isNewEdge[i] && !isNewEdge[j] && faceHalfedges[i].Next != faceHalfedges[j]) 
                {
                    TriMesh.HalfEdge closeHalfedge = faceHalfedges[i].Opposite; 
                    // Find the closing halfedge of the opening opposite the opening halfedge i is on
                    do
                    {
                        closeHalfedge = closeHalfedge.Previous.Opposite;
                    } while (closeHalfedge.Face != null && closeHalfedge != faceHalfedges[j] 
                             && closeHalfedge != faceHalfedges[i].Opposite); 
                    if (closeHalfedge == faceHalfedges[j] || closeHalfedge == faceHalfedges[i].Opposite)
                    {
                        throw new BadTopologyException("Unable to find an opening to relink an existing face.");
                    } 
                    TriMesh.HalfEdge openHalfedge = closeHalfedge.Previous; 
                    // Remove group of faces between two openings, close up gap to form one opening
                    openHalfedge.Next = faceHalfedges[i].Next;
                    faceHalfedges[i].Next.Previous = openHalfedge; 
                    // Insert group of faces into target opening
                    faceHalfedges[j].Previous.Next = closeHalfedge;
                    closeHalfedge.Previous = faceHalfedges[j].Previous;
                } 
                // Inner halfedges
                faceHalfedges[i].Next = faceHalfedges[j];
                faceHalfedges[j].Previous = faceHalfedges[i];
            }
            // Connect face to an inner halfedge
            f.HalfEdge = faceHalfedges[0];
            return f;
        }