/// <summary> /// Complexifies the tessellation so that no edge is longer than provided the maximum edge length /// or for adding the provided number of faces - whichever comes first /// </summary> /// <param name="ts">The ts.</param> /// <param name="numberOfFaces">The number of new faces to add.</param> /// <param name="maxLength">The maximum length.</param> public static void Complexify(TessellatedSolid ts, int numberOfFaces, double maxLength) { var edgeQueue = new SimplePriorityQueue <Edge, double>(new ReverseSort()); foreach (var e in ts.Edges) { edgeQueue.Enqueue(e, e.Length); } var addedEdges = new List <Edge>(); var addedVertices = new List <Vertex>(); var addedFaces = new List <PolygonalFace>(); var edge = edgeQueue.Dequeue(); var iterations = numberOfFaces > 0 ? (int)Math.Ceiling(numberOfFaces / 2.0) : numberOfFaces; while (iterations-- != 0 && edge.Length >= maxLength) { var origLeftFace = edge.OtherFace; var origRightFace = edge.OwnedFace; var leftFarVertex = origLeftFace.OtherVertex(edge); var rightFarVertex = origRightFace.OtherVertex(edge); var fromVertex = edge.From; var toVertex = edge.To; var addedVertex = new Vertex(DetermineIntermediateVertexPosition(fromVertex, toVertex)); // modify original faces with new intermediate vertex var index = origLeftFace.Vertices.IndexOf(toVertex); origLeftFace.Vertices[index] = addedVertex; origLeftFace.Update(); addedVertex.Faces.Add(origLeftFace); index = origRightFace.Vertices.IndexOf(toVertex); origRightFace.Vertices[index] = addedVertex; origRightFace.Update(); addedVertex.Faces.Add(origRightFace); var newLeftFace = new PolygonalFace(new[] { toVertex, addedVertex, leftFarVertex }); var newRightFace = new PolygonalFace(new[] { addedVertex, toVertex, rightFarVertex }); toVertex.Faces.Remove(origLeftFace); toVertex.Faces.Remove(origRightFace); var inlineEdge = new Edge(addedVertex, toVertex, newRightFace, newLeftFace, true); toVertex.Edges.Remove(edge); edge.To = addedVertex; addedVertex.Edges.Add(edge); edge.Update(); var newLeftEdge = new Edge(leftFarVertex, addedVertex, origLeftFace, newLeftFace, true); var newRightEdge = new Edge(rightFarVertex, addedVertex, newRightFace, origRightFace, true); origLeftFace.AddEdge(newLeftEdge); origRightFace.AddEdge(newRightEdge); var bottomEdge = toVertex.Edges.First(e => e.OtherVertex(toVertex) == leftFarVertex); if (bottomEdge.OwnedFace == origLeftFace) { bottomEdge.OwnedFace = newLeftFace; } else { bottomEdge.OtherFace = newLeftFace; } newLeftFace.AddEdge(bottomEdge); bottomEdge.Update(); bottomEdge = toVertex.Edges.First(e => e.OtherVertex(toVertex) == rightFarVertex); if (bottomEdge.OwnedFace == origRightFace) { bottomEdge.OwnedFace = newRightFace; } else { bottomEdge.OtherFace = newRightFace; } newRightFace.AddEdge(bottomEdge); bottomEdge.Update(); // need to re-add the edge. It was modified in the SplitEdge function (now, half the lenght), but // it may still be met by this criteria edgeQueue.Enqueue(edge, edge.Length); edgeQueue.Enqueue(inlineEdge, inlineEdge.Length); addedEdges.Add(inlineEdge); edgeQueue.Enqueue(newLeftEdge, newLeftEdge.Length); addedEdges.Add(newLeftEdge); edgeQueue.Enqueue(newRightEdge, newRightEdge.Length); addedEdges.Add(newRightEdge); addedFaces.Add(newLeftFace); addedFaces.Add(newRightFace); addedVertices.Add(addedVertex); edge = edgeQueue.First(); } ts.AddVertices(addedVertices); ts.AddEdges(addedEdges); ts.AddFaces(addedFaces); }
/// <summary> /// Simplifies the model by merging the eliminating edges that are closer together /// than double the shortest edge length /// </summary> /// <param name="ts">The ts.</param> public static void SimplifyFlatPatches(this TessellatedSolid ts) { // throw new NotImplementedException(); var edgesToRemove = new List <Edge>(); var edgesToAdd = new List <Edge>(); var facesToRemove = new List <PolygonalFace>(); var facesToAdd = new List <PolygonalFace>(); var verticesToRemove = new List <Vertex>(); var flats = TVGL.MiscFunctions.FindFlats(ts.Faces); if (ts.Primitives == null) { ts.Primitives = new List <PrimitiveSurface>(); } foreach (var flat in flats) { if (flat.InnerEdges.Count < flat.Faces.Count) { continue; } var newFaces = new List <PolygonalFace>(); var outerEdgeHashSet = new HashSet <Edge>(flat.OuterEdges); facesToRemove.AddRange(flat.Faces); edgesToRemove.AddRange(flat.InnerEdges); var innerVertices = new HashSet <Vertex>(flat.InnerEdges.Select(e => e.To)); innerVertices.UnionWith(flat.InnerEdges.Select(e => e.From)); innerVertices.RemoveWhere(v => outerEdgeHashSet.Overlaps(v.Edges)); verticesToRemove.AddRange(innerVertices); var vertexLoops = OrganizeIntoLoop(flat.OuterEdges, flat.Normal); List <List <Vertex[]> > triangulatedListofLists = TriangulatePolygon.Run(new[] { vertexLoops }, flat.Normal); var triangulatedList = triangulatedListofLists.SelectMany(tl => tl).ToList(); var oldEdgeDictionary = flat.OuterEdges.ToDictionary(TessellatedSolid.SetAndGetEdgeChecksum); Dictionary <long, Edge> newEdgeDictionary = new Dictionary <long, Edge>(); foreach (var triangle in triangulatedList) { var newFace = new PolygonalFace(triangle, flat.Normal); if (newFace.Area.IsNegligible() && newFace.Normal.Any(double.IsNaN)) { continue; } newFaces.Add(newFace); for (var j = 0; j < 3; j++) { var fromVertex = newFace.Vertices[j]; var toVertex = newFace.NextVertexCCW(fromVertex); var checksum = TessellatedSolid.GetEdgeChecksum(fromVertex, toVertex); if (oldEdgeDictionary.ContainsKey(checksum)) { //fix up old outer edge. var edge = oldEdgeDictionary[checksum]; if (fromVertex == edge.From) { edge.OwnedFace = newFace; } else { edge.OtherFace = newFace; } newFace.AddEdge(edge); oldEdgeDictionary.Remove(checksum); } else if (newEdgeDictionary.ContainsKey(checksum)) { //Finish creating edge. var newEdge = newEdgeDictionary[checksum]; newEdge.OtherFace = newFace; newFace.AddEdge(newEdge); newEdgeDictionary.Remove(checksum); edgesToAdd.Add(newEdge); } else { newEdgeDictionary.Add(checksum, new Edge(fromVertex, toVertex, newFace, null, false, checksum)); } } } ts.Primitives.Add(new Flat(newFaces)); } ts.RemoveVertices(verticesToRemove); //todo: check if the order of these five commands ts.RemoveFaces(facesToRemove); // matters. There may be an ordering that is more efficient ts.AddFaces(facesToAdd); ts.RemoveEdges(edgesToRemove); ts.AddEdges(edgesToAdd); }