/// <summary> /// Adds a halfedge to the halfedge list. /// </summary> /// <param name="halfedge">The halfedge to add.</param> protected void AppendToHalfedgeList(DXHalfedge halfedge) { halfedge.Index = halfedges.Count; halfedges.Add(halfedge); }
/// <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 DXFace CreateFace(TFaceTraits faceTraits, params DXVertex[] faceVertices) { int n = faceVertices.Length; // Require at least 3 vertices if (n < 3) { throw new DXBadTopologyException("Cannot create a polygon with fewer than three vertices."); } DXEdge e; DXFace f; DXHalfedge[] faceHalfedges = new DXHalfedge[n]; bool[] isNewEdge = new bool[n], isUsedVertex = new bool[n]; // 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 DXBadTopologyException("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 DXBadTopologyException("Can't add more than two faces to an edge."); } } // Create face f = new DXFace(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 DXEdge(); mesh.AppendToEdgeList(e); // Create new halfedges faceHalfedges[i] = new DXHalfedge(); mesh.AppendToHalfedgeList(faceHalfedges[i]); faceHalfedges[i].Opposite = new DXHalfedge(); 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 DXBadTopologyException("An inner halfedge already has a face assigned to it."); } // Connect inner halfedge to face faceHalfedges[i].Face = f; //Debug.Assert(faceHalfedges[i].FromVertex == faceVertices[i] && faceHalfedges[i].ToVertex == faceVertices[j]); } // 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 { DXHalfedge closeHalfedge = null; // Find the closing halfedge of the first available opening foreach (DXHalfedge h in faceVertices[j].Halfedges) { if (h.Face == null) { closeHalfedge = h; break; } } //Debug.Assert(closeHalfedge != null); DXHalfedge 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; } else if (!isNewEdge[i] && !isNewEdge[j] && faceHalfedges[i].Next != faceHalfedges[j]) // Relink faces before adding new edges if they are in the way of a new face { DXHalfedge 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 DXBadTopologyException("Unable to find an opening to relink an existing face."); } DXHalfedge 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); }