private void SubdivideAllFacesWithInternalFace() { var edges = new HashSet <HalfEdge>(HalfEdges.Where(a => a.Primary)); var newVertices = new HashSet <Vertex>(); foreach (var edge in edges) { newVertices.Add(edge.Split(GetVertex(edge.End.Position * 0.5f + edge.Twin.End.Position * 0.5f)).End); } WalkFaces((f, m) => { var verts = f.Vertices.ToArray(); f.Delete(); GetFace(verts.Where(a => newVertices.Contains(a))); for (int i = 0; i < verts.Length; i++) { if (newVertices.Contains(verts[i])) { GetFace( verts[i], verts[(i + 1) % verts.Length], verts[(i + 2) % verts.Length] ); } } }); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Makes a deep copy of this object. </summary> /// /// <remarks> Darrell Plank, 12/31/2017. </remarks> /// /// <returns> A copy of this object. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// public Mesh Clone() { var newMesh = Factory.CreateMesh(); var oldToNewVertex = new Dictionary <Vertex, Vertex>(); var oldToNewHalfEdge = new Dictionary <HalfEdge, HalfEdge>(); var oldToNewFace = new Dictionary <Face, Face>(); var newVertices = new List <Vertex>(); var newHalfEdges = new List <HalfEdge>(); var newFaces = new List <Face>(); foreach (var vertex in Vertices.Where(v => v.IsAlive)) { var newVertex = Factory.CreateVertex(newMesh, vertex.Position.Clone()); Factory.CloneVertex(newVertex, vertex); newVertices.Add(newVertex); oldToNewVertex[vertex] = newVertex; } foreach (var halfEdge in HalfEdges.Where(he => he.IsAlive)) { var newHalfEdge = Factory.CreateHalfEdge(oldToNewVertex[halfEdge.InitVertex], null, null, null); Factory.CloneHalfEdge(newHalfEdge, halfEdge, oldToNewVertex); newHalfEdges.Add(newHalfEdge); oldToNewHalfEdge[halfEdge] = newHalfEdge; } foreach (var face in Faces.Where(f => f.IsAlive)) { var newFace = Factory.CreateFace(); newFace.HalfEdge = oldToNewHalfEdge[face.HalfEdge]; Factory.CloneFace(newFace, face, oldToNewHalfEdge, oldToNewVertex); newFaces.Add(newFace); oldToNewFace[face] = newFace; } newMesh.VerticesInternal = newVertices; newMesh.HalfEdgesInternal = newHalfEdges; newMesh.FacesInternal = newFaces; newMesh.PatchClone( this, oldToNewVertex, oldToNewHalfEdge, oldToNewFace); #if DEBUG newMesh.Validate(); #endif return(newMesh); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Validates this mesh. </summary> /// /// <remarks> TODO: Think about this /// How much do we really want to do at this top level? Without making certain assumptions /// it's really difficult to do anything useful but we want to be as flexible as possible. /// Since we currently only create elements by adding faces, it's pretty safe, I think, to /// insist that all faces have at least three edges. /// /// Darrell Plank, 12/8/2017. </remarks> /// /// <returns> True if it succeeds, false if it fails. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// public virtual bool Validate() { if (VerticesInternal.Count == 0) { if (HalfEdgesInternal.Count != 0 && FacesInternal.Count != 0) { throw new MeshNavException("No vertices but we have other elements"); } } if (Faces.Any(face => face.Edges().Take(3).Count() != 3 && !face.IsBoundary)) { throw new MeshNavException("Faces with less than three sides not allowed"); } if (Faces.Where(f => f.IsAlive).Any(f => !f.HalfEdge.IsAlive)) { throw new MeshNavException("Live faces point to dead halfedges"); } if (Vertices.Where(v => v.IsAlive).Any(v => v.Mesh != this || !v.Edge.IsAlive)) { throw new MeshNavException("Live vertices point at dead half edges or don't belong to this mesh"); } if (HalfEdges.Where(he => he.IsAlive).Any(he => !he.InitVertex.IsAlive || he.Face != null && !he.Face.IsAlive)) { throw new MeshNavException("Live halfedge points at dead component"); } if (HalfEdges.Where(he => he.IsAlive).Any(he => he.InitVertex == he.Opposite.InitVertex)) { throw new MeshNavException("Edge and opposite oriented identically"); } return(true); }