/// <summary> /// Stores the face with negligible area. /// </summary> /// <param name="ts">The ts.</param> /// <param name="face">The face.</param> private static void StoreFaceWithNegligibleArea(TessellatedSolid ts, PolygonalFace face) { //This is not truly an error, to don't change the NoErrors boolean. if (ts.Errors.FacesWithNegligibleArea == null) { ts.Errors.FacesWithNegligibleArea = new List <PolygonalFace> { face } } ; else if (!ts.Errors.FacesWithNegligibleArea.Contains(face)) { ts.Errors.FacesWithNegligibleArea.Add(face); } }
/// <summary> /// Stores the edge has bad angle. /// </summary> /// <param name="ts">The ts.</param> /// <param name="edge">The edge.</param> private static void StoreEdgeHasBadAngle(TessellatedSolid ts, Edge edge) { ts.Errors.NoErrors = false; if (ts.Errors.EdgesWithBadAngle == null) { ts.Errors.EdgesWithBadAngle = new List <Edge> { edge } } ; else if (!ts.Errors.EdgesWithBadAngle.Contains(edge)) { ts.Errors.EdgesWithBadAngle.Add(edge); } }
/// <summary> /// Stores the face with one vertex. /// </summary> /// <param name="ts">The ts.</param> /// <param name="face">The face.</param> private static void StoreFaceWithOneVertex(TessellatedSolid ts, PolygonalFace face) { ts.Errors.NoErrors = false; if (ts.Errors.FacesWithOneVertex == null) { ts.Errors.FacesWithOneVertex = new List <PolygonalFace> { face } } ; else { ts.Errors.FacesWithOneVertex.Add(face); } }
/// <summary> /// Stores the duplicate face. /// </summary> /// <param name="ts">The ts.</param> /// <param name="faceVertexIndices">The face vertex indices.</param> internal static void StoreDuplicateFace(TessellatedSolid ts, int[] faceVertexIndices) { ts.Errors.NoErrors = false; if (ts.Errors.DuplicateFaces == null) { ts.Errors.DuplicateFaces = new List <int[]> { faceVertexIndices } } ; else { ts.Errors.DuplicateFaces.Add(faceVertexIndices); } }
/// <summary> /// Stores the single sided edge. /// </summary> /// <param name="ts">The ts.</param> /// <param name="singledSidedEdge">The singled sided edge.</param> internal static void StoreSingleSidedEdge(TessellatedSolid ts, Edge singledSidedEdge) { ts.Errors.NoErrors = false; if (ts.Errors.SingledSidedEdges == null) { ts.Errors.SingledSidedEdges = new List <Edge> { singledSidedEdge } } ; else { ts.Errors.SingledSidedEdges.Add(singledSidedEdge); } }
/// <summary> /// Stores the vert does not link back to edge. /// </summary> /// <param name="ts">The ts.</param> /// <param name="edge">The edge.</param> /// <param name="vert">The vert.</param> private static void StoreVertDoesNotLinkBackToEdge(TessellatedSolid ts, Edge edge, Vertex vert) { ts.Errors.NoErrors = false; if (ts.Errors.VertsThatDoNotLinkBackToEdge == null) { ts.Errors.VertsThatDoNotLinkBackToEdge = new List <(Edge, Vertex)> { (edge, vert) } } ; else { ts.Errors.VertsThatDoNotLinkBackToEdge.Add((edge, vert)); } }
/// <summary> /// Stores the edge does not link back to vertex. /// </summary> /// <param name="ts">The ts.</param> /// <param name="vertex">The vertex.</param> /// <param name="edge">The edge.</param> private static void StoreEdgeDoesNotLinkBackToVertex(TessellatedSolid ts, Vertex vertex, Edge edge) { ts.Errors.NoErrors = false; if (ts.Errors.EdgesThatDoNotLinkBackToVertex == null) { ts.Errors.EdgesThatDoNotLinkBackToVertex = new List <(Vertex, Edge)> { (vertex, edge) } } ; else { ts.Errors.EdgesThatDoNotLinkBackToVertex.Add((vertex, edge)); } }
/// <summary> /// Stores the face does not link back to vertex. /// </summary> /// <param name="ts">The ts.</param> /// <param name="vertex">The vertex.</param> /// <param name="face">The face.</param> private static void StoreFaceDoesNotLinkBackToVertex(TessellatedSolid ts, Vertex vertex, PolygonalFace face) { ts.Errors.NoErrors = false; if (ts.Errors.FacesThatDoNotLinkBackToVertex == null) { ts.Errors.FacesThatDoNotLinkBackToVertex = new List <(Vertex, PolygonalFace)> { (vertex, face) } } ; else { ts.Errors.FacesThatDoNotLinkBackToVertex.Add((vertex, face)); } }
/// <summary> /// Stores the vertex does not link back to face. /// </summary> /// <param name="ts">The ts.</param> /// <param name="face">The face.</param> /// <param name="vertex">The vertex.</param> private static void StoreVertexDoesNotLinkBackToFace(TessellatedSolid ts, PolygonalFace face, Vertex vertex) { ts.Errors.NoErrors = false; if (ts.Errors.VertsThatDoNotLinkBackToFace == null) { ts.Errors.VertsThatDoNotLinkBackToFace = new List <(PolygonalFace, Vertex)> { (face, vertex) } } ; else { ts.Errors.VertsThatDoNotLinkBackToFace.Add((face, vertex)); } }
/// <summary> /// Initializes a new instance of the <see cref="Edge" /> class. /// </summary> /// <param name="fromVertex">From vertex.</param> /// <param name="toVertex">To vertex.</param> /// <param name="ownedFace">The face.</param> /// <param name="otherFace">The other face.</param> /// <param name="doublyLinkedVertices">if set to <c>true</c> [doubly linked vertices].</param> /// <param name="edgeReference">The edge reference.</param> /// <exception cref="Exception"></exception> public Edge(Vertex fromVertex, Vertex toVertex, PolygonalFace ownedFace, PolygonalFace otherFace, bool doublyLinkedVertices, long edgeReference = 0) : this(fromVertex, toVertex, doublyLinkedVertices) { if (edgeReference > 0) { EdgeReference = edgeReference; } else { TessellatedSolid.SetAndGetEdgeChecksum(this); } _ownedFace = ownedFace; _otherFace = otherFace; ownedFace?.AddEdge(this); otherFace?.AddEdge(this); }
/// <summary> /// Stores the face does not link back to edge. /// </summary> /// <param name="ts">The ts.</param> /// <param name="edge">The edge.</param> /// <param name="face">The face.</param> private static void StoreFaceDoesNotLinkBackToEdge(TessellatedSolid ts, Edge edge, PolygonalFace face) { ts.Errors.NoErrors = false; if (ts.Errors.FacesThatDoNotLinkBackToEdge == null) { ts.Errors.FacesThatDoNotLinkBackToEdge = new List <(Edge, PolygonalFace)> { (edge, face) } } ; else { ts.Errors.FacesThatDoNotLinkBackToEdge.Add((edge, face)); } }
/// <summary> /// Stores the edge does not link back to face. /// </summary> /// <param name="ts">The ts.</param> /// <param name="face">The face.</param> /// <param name="edge">The edge.</param> private static void StoreEdgeDoesNotLinkBackToFace(TessellatedSolid ts, PolygonalFace face, Edge edge) { ts.Errors.NoErrors = false; if (ts.Errors.EdgesThatDoNotLinkBackToFace == null) { ts.Errors.EdgesThatDoNotLinkBackToFace = new List <(PolygonalFace, Edge)> { (face, edge) } } ; else { ts.Errors.EdgesThatDoNotLinkBackToFace.Add((face, edge)); } }
/// <summary> /// Makes the model visual3 d. /// </summary> /// <param name="ts">The ts.</param> /// <returns>Visual3D.</returns> private static Visual3D MakeModelVisual3D(TessellatedSolid ts) { var defaultMaterial = MaterialHelper.CreateMaterial( new System.Windows.Media.Color { A = ts.SolidColor.A, B = ts.SolidColor.B, G = ts.SolidColor.G, R = ts.SolidColor.R }); if (ts.HasUniformColor) { return(MakeModelVisual3DSameColorFaces(ts.Faces, defaultMaterial)); } return(MakeModelVisual3DMultiColorFaces(ts.Faces, defaultMaterial)); }
/// <summary> /// Shows the specified tessellated solid in a Helix toolkit window. /// </summary> /// <param name="tessellatedSolid">The tessellated solid.</param> /// <param name="seconds">The seconds.</param> public static void Show(TessellatedSolid tessellatedSolid, int seconds = 0) { var window = new Window3DPlot(); window.view1.Children.Add(MakeModelVisual3D(tessellatedSolid)); window.view1.FitView(window.view1.Camera.LookDirection, window.view1.Camera.UpDirection); if (seconds > 0) { window.Show(); Thread.Sleep(seconds * 1000); window.Close(); } else { window.Show(); } }
/// <summary> /// Finds the minimum bounding box oriented along a particular Direction. /// </summary> /// <param name="ts">The ts.</param> /// <param name="times"></param> /// <param name="volumes"></param> /// <returns>BoundingBox.</returns> //private public static BoundingBox OrientedBoundingBox_Test(TessellatedSolid ts, out List <double> times, out List <double> volumes) //, out List<List<double[]>> volumeData2) { var vertices = ts.ConvexHull.Vertices.Any() ? ts.ConvexHull.Vertices : ts.Vertices; times = new List <double>(); volumes = new List <double>(); //var flats = ListFunctions.Flats(ts.Faces.ToList()); var now = DateTime.Now; Message.output("Beginning OBB Test", 2); var boundingBox1 = OrientedBoundingBox(vertices); times.Add((DateTime.Now - now).TotalMilliseconds); volumes.Add(boundingBox1.Volume); //Message.output("Time Elapsed for PCA Approach = " ,4); //Message.output("Volume for PCA Approach= " + boundingBox1.Volume,4); now = DateTime.Now; Message.output("Beginning OBB Test", 2); var boundingBox12 = Find_via_PCA_ApproachNR(vertices); times.Add((DateTime.Now - now).TotalMilliseconds); volumes.Add(boundingBox12.Volume); //Message.output("Time Elapsed for PCA Approach = " ,4 ); //Message.output("Volume for PCA Approach= " + boundingBox1.Volume); now = DateTime.Now; var boundingBox2 = Find_via_ChanTan_AABB_Approach(vertices); times.Add((DateTime.Now - now).TotalMilliseconds); volumes.Add(boundingBox2.Volume); Message.output("Time Elapsed for ChanTan Approach = " + (DateTime.Now - now), 4); Message.output("Volume for ChanTan Approach = " + boundingBox2.Volume, 4); //now = DateTime.Now; //Message.output("Beginning OBB Test"); //var boundingBox1 = Find_via_MC_ApproachOne(ts, out volumeData1); //Message.output("Time Elapsed for MC Approach One = " + (DateTime.Now - now),4); //now = DateTime.Now; //var boundingBox2 = Find_via_BM_ApproachTwo(ts, out volumeData2); //Message.output("Time Elapsed for BM Approach Two = " + (DateTime.Now - now),4); return(boundingBox2); }
public void CompletePostSerialization(TessellatedSolid ts) { Faces = new List <PolygonalFace>(); var stringList = _faceIndices.Split(','); var listLength = stringList.Length; for (int i = 0; i < listLength; i++) { var face = ts.Faces[int.Parse(stringList[i])]; Faces.Add(face); face.BelongsToPrimitive = this; } Vertices = new List <Vertex>(); stringList = _vertexIndices.Split(','); listLength = stringList.Length; for (int i = 0; i < listLength; i++) { Vertices.Add(ts.Vertices[int.Parse(stringList[i])]); } if (!string.IsNullOrWhiteSpace(_innerEdgeIndices)) { _innerEdges = new List <Edge>(); stringList = _innerEdgeIndices.Split(','); listLength = stringList.Length; for (int i = 0; i < listLength; i++) { _innerEdges.Add(ts.Edges[int.Parse(stringList[i])]); } } if (!string.IsNullOrWhiteSpace(_outerEdgeIndices)) { _outerEdges = new List <Edge>(); stringList = _outerEdgeIndices.Split(','); listLength = stringList.Length; for (int i = 0; i < listLength; i++) { _outerEdges.Add(ts.Edges[int.Parse(stringList[i])]); } } Area = Faces.Sum(f => f.Area); }
/// <summary> /// Find the volume of a tesselated solid with a slower method. /// This method could be exteded to find partial volumes of a solid (e.g. volume between two planes) /// </summary> /// <param name="ts"></param> /// <returns></returns> private static double VolumeViaAreaDecomposition(TessellatedSolid ts) { var normal = new[] { 1.0, 0.0, 0.0 }; //Direction is irrellevant var stepSize = 0.01; var volume = 0.0; var areas = DirectionalDecomposition.NonUniformAreaDecomposition(ts, normal, stepSize); //Trapezoidal approximation. This should be accurate since the lines betweens data points are linear for (var i = 1; i < areas.Count; i++) { var deltaX = areas[i][0] - areas[i - 1][0]; if (deltaX < 0) { throw new Exception("Error in your implementation. This should never occur"); } volume = volume + .5 * (areas[i][1] + areas[i - 1][1]) * deltaX; } return(volume); }
internal TVGLConvexHull(IList <Vertex> allVertices, IList <Vertex> convexHullPoints, IList <int> convexHullFaceIndices, double tolerance) { Vertices = convexHullPoints.ToArray(); var numCvxHullFaces = convexHullFaceIndices.Count / 3; Faces = new PolygonalFace[numCvxHullFaces]; var checkSumMultipliers = new long[3]; for (var i = 0; i < 3; i++) { checkSumMultipliers[i] = (long)Math.Pow(Constants.CubeRootOfLongMaxValue, i); } var alreadyCreatedFaces = new HashSet <long>(); for (int i = 0; i < numCvxHullFaces; i++) { var orderedIndices = new List <int> { convexHullFaceIndices[3 * i], convexHullFaceIndices[3 * i + 1], convexHullFaceIndices[3 * i + 2] }; orderedIndices.Sort(); var checksum = orderedIndices.Select((t, j) => t * checkSumMultipliers[j]).Sum(); if (alreadyCreatedFaces.Contains(checksum)) { continue; } alreadyCreatedFaces.Add(checksum); var faceVertices = new[] { allVertices[convexHullFaceIndices[3 * i]], allVertices[convexHullFaceIndices[3 * i + 1]], allVertices[convexHullFaceIndices[3 * i + 2]], }; Faces[i] = new PolygonalFace(faceVertices, false); } Edges = MakeEdges(Faces, Vertices); SurfaceArea = Faces.Sum(face => face.Area); TessellatedSolid.CalculateVolumeAndCenter(Faces, tolerance, out Volume, out Center); }
/// <summary> /// Initializes a new instance of the <see cref="Edge" /> class. /// </summary> /// <param name="fromVertex">From vertex.</param> /// <param name="toVertex">To vertex.</param> /// <param name="ownedFace">The face.</param> /// <param name="otherFace">The other face.</param> /// <param name="doublyLinkedVertices">if set to <c>true</c> [doubly linked vertices].</param> /// <param name="edgeReference">The edge reference.</param> /// <exception cref="Exception"></exception> public Edge(Vertex fromVertex, Vertex toVertex, PolygonalFace ownedFace, PolygonalFace otherFace, bool doublyLinkedVertices, long edgeReference = 0) : this(fromVertex, toVertex, doublyLinkedVertices) { if (edgeReference > 0) { EdgeReference = edgeReference; } else { TessellatedSolid.SetAndGetEdgeChecksum(this); } _ownedFace = ownedFace; _otherFace = otherFace; if (ownedFace != null) { ownedFace.AddEdge(this); } if (otherFace != null) { otherFace.AddEdge(this); } DefineInternalEdgeAngle(); }
/// <summary> /// Shows vertex paths. Assumes paths are closed. /// </summary> /// <param name="vertices">The vertices.</param> /// <param name="colors">The colors.</param> /// <param name="ts">The ts.</param> public static void ShowGaussSphereWithIntensity(IList <Vertex> vertices, IList <Color> colors, TessellatedSolid ts) { var window = new Window3DPlot(); var pt0 = new Point3D(ts.Center[0], ts.Center[1], ts.Center[2]); var x = ts.XMax - ts.XMin; var y = ts.YMax - ts.YMin; var z = ts.ZMax - ts.ZMin; var radius = System.Math.Max(System.Math.Max(x, y), z) / 2; //Add the solid to the visual var model = MakeModelVisual3D(ts); window.view1.Children.Add(model); //Add a transparent unit sphere to the visual var sphere = new SphereVisual3D(); sphere.Radius = radius; sphere.Center = pt0; sphere.Material = MaterialHelper.CreateMaterial(new System.Windows.Media.Color { A = 15, R = 200, G = 200, B = 200 }); //window.view1.Children.Add(sphere); var i = 0; foreach (var point in vertices) { var color = colors[i]; var pt1 = new Point3D(pt0.X + point.X * radius, pt0.Y + point.Y * radius, pt0.Z + point.Z * radius); //No create a line collection by doubling up the points var lineCollection = new List <Point3D>(); lineCollection.Add(pt0); lineCollection.Add(pt1); var systemColor = new System.Windows.Media.Color(); systemColor.A = 255; systemColor.R = color.R; systemColor.G = color.G; systemColor.B = color.B; var lines = new LinesVisual3D { Points = new Point3DCollection(lineCollection), Color = systemColor, Thickness = 5 }; var pointsVisual = new PointsVisual3D { Color = systemColor, Size = 5 }; pointsVisual.Points = new Point3DCollection() { pt1 }; window.view1.Children.Add(pointsVisual); window.view1.Children.Add(lines); i++; } window.view1.FitView(window.view1.Camera.LookDirection, window.view1.Camera.UpDirection); window.ShowDialog(); }
/// <summary> /// Shows the vertex paths with solid. /// </summary> /// <param name="paths"></param> /// <param name="closePaths"></param> /// <param name="solid"></param> public static void ShowVertexPaths(IList <List <double[]> > paths, bool closePaths = true, TessellatedSolid solid = null) { var window = new Window3DPlot(); var models = new List <Visual3D>(); if (solid != null) { var model = MakeModelVisual3D(solid); models.Add(model); window.view1.Children.Add(model); } foreach (var path in paths) { var contour = path.Select(point => new Point3D(point[0], point[1], point[2])).ToList(); //Now create a line collection by doubling up the points var lineCollection = new List <Point3D>(); foreach (var t in contour) { lineCollection.Add(t); lineCollection.Add(t); } lineCollection.RemoveAt(0); if (closePaths) { lineCollection.Add(lineCollection.First()); } var lines = new LinesVisual3D { Points = new Point3DCollection(lineCollection) }; window.view1.Children.Add(lines); } window.view1.FitView(window.view1.Camera.LookDirection, window.view1.Camera.UpDirection); window.ShowDialog(); }
/// <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> /// Complexifies the tessellation so that no edge is longer than provided the maximum edge length. /// </summary> /// <param name="ts">The ts.</param> /// <param name="maxLength">The tolerance.</param> public static void Complexify(this TessellatedSolid ts, double maxLength) { Complexify(ts, -1, maxLength); }
/// <summary> /// Complexifies the tessellation by adding more faces of the provided number. /// </summary> /// <param name="ts">The ts.</param> /// <param name="numberOfNewFaces">The number of faces.</param> public static void Complexify(this TessellatedSolid ts, int numberOfNewFaces) { Complexify(ts, numberOfNewFaces, 0.0); }
/// <summary> /// Complexifies the model by splitting the any edges that are half or more than the longest edge. /// </summary> /// <param name="ts">The ts.</param> public static void Complexify(this TessellatedSolid ts) { Complexify(ts, ts.NumberOfFaces / 2, ts.Edges.Max(x => x.Length) * 0.5); }
/// <summary> /// Simplifies the tessellation so that no edge are shorter than provided the minimum edge length /// or until the provided number of faces are removed - whichever comes first. /// </summary> /// <param name="ts">The ts.</param> /// <param name="numberOfFaces">The number of faces to remove.</param> /// <param name="minLength">The minimum length.</param> public static void Simplify(TessellatedSolid ts, int numberOfFaces, double minLength) { if (ts.Errors != null) { Message.output( "** The model should be free of errors before running this routine (run TessellatedSolid.Repair()).", 1); } var sortedEdges = ts.Edges.OrderBy(e => e.Length).ToList(); var removedEdges = new SortedSet <Edge>(new SortByIndexInList()); var removedVertices = new SortedSet <Vertex>(new SortByIndexInList()); var removedFaces = new SortedSet <PolygonalFace>(new SortByIndexInList()); var edge = sortedEdges[0]; var iterations = numberOfFaces > 0 ? (int)Math.Ceiling(numberOfFaces / 2.0) : numberOfFaces; while (iterations != 0 && edge.Length <= minLength) { sortedEdges.RemoveAt(0); // naming conventions to ease the latter topological changes var removedVertex = edge.From; var keepVertex = edge.To; var leftFace = edge.OtherFace; var rightFace = edge.OwnedFace; var leftRemoveEdge = leftFace.OtherEdge(keepVertex); var rightRemoveEdge = rightFace.OtherEdge(keepVertex); var leftKeepEdge = leftFace.OtherEdge(removedVertex); var rightKeepEdge = rightFace.OtherEdge(removedVertex); var leftFarVertex = leftFace.OtherVertex(edge); var rightFarVertex = rightFace.OtherVertex(edge); // this is a topologically important check. It ensures that the edge is not deleted if // it serves an important role in ensuring the proper topology of the solid var otherEdgesOnTheKeepSide = keepVertex.Edges.Where(e => e != edge && e != leftKeepEdge && e != rightKeepEdge).ToList(); otherEdgesOnTheKeepSide.Remove(edge); var otherEdgesOnTheRemoveSide = removedVertex.Edges.Where(e => e != edge && e != leftRemoveEdge && e != rightRemoveEdge).ToList(); otherEdgesOnTheRemoveSide.Remove(edge); if (leftFarVertex != rightFarVertex && !otherEdgesOnTheKeepSide.Select(e => e.OtherVertex(keepVertex)) .Intersect(otherEdgesOnTheRemoveSide.Select(e => e.OtherVertex(removedVertex))) .Any()) { iterations--; //now that we passed that test, we can be assured that the reduction will go through // move the keepVertex keepVertex.Position = DetermineIntermediateVertexPosition(removedVertex, keepVertex); // add and remove to the lists at the top of this method removedEdges.Add(edge); removedEdges.Add(leftRemoveEdge); sortedEdges.Remove(leftRemoveEdge); removedEdges.Add(rightRemoveEdge); sortedEdges.Remove(rightRemoveEdge); removedFaces.Add(leftFace); removedFaces.Add(rightFace); removedVertices.Add(removedVertex); keepVertex.Faces.Remove(leftFace); keepVertex.Faces.Remove(rightFace); // the keepVertex's other faces need to be updated given the change in position of keepVertex foreach (var face in keepVertex.Faces) { face.Update(); } // remove the removedVertex from the faces and update their positions with the keepVertex foreach (var face in removedVertex.Faces) { if (face == leftFace || face == rightFace) { continue; } var index = face.Vertices.IndexOf(removedVertex); face.Vertices[index] = keepVertex; face.Update(); keepVertex.Faces.Add(face); } keepVertex.Edges.Remove(edge); // update the edges since the keepVertex moved foreach (var currentEdge in keepVertex.Edges) { if (currentEdge != leftKeepEdge && currentEdge != rightKeepEdge) { currentEdge.Update(); } } // transfer the edges from the removedVertex to the keepVertex foreach (var transferEdge in removedVertex.Edges) { if (transferEdge == leftRemoveEdge || transferEdge == rightRemoveEdge) { continue; } if (transferEdge.From == removedVertex) { transferEdge.From = keepVertex; } else { transferEdge.To = keepVertex; } transferEdge.Update(); keepVertex.Edges.Add(transferEdge); } leftFarVertex.Edges.Remove(leftRemoveEdge); leftFarVertex.Faces.Remove(leftFace); rightFarVertex.Edges.Remove(rightRemoveEdge); rightFarVertex.Faces.Remove(rightFace); var upperFace = leftRemoveEdge.OwnedFace == leftFace ? leftRemoveEdge.OtherFace : leftRemoveEdge.OwnedFace; if (leftKeepEdge.OwnedFace == leftFace) { leftKeepEdge.OwnedFace = upperFace; } else { leftKeepEdge.OtherFace = upperFace; } upperFace.AddEdge(leftKeepEdge); leftKeepEdge.Update(); upperFace = rightRemoveEdge.OwnedFace == rightFace ? rightRemoveEdge.OtherFace : rightRemoveEdge.OwnedFace; if (rightKeepEdge.OwnedFace == rightFace) { rightKeepEdge.OwnedFace = upperFace; } else { rightKeepEdge.OtherFace = upperFace; } upperFace.AddEdge(rightKeepEdge); rightKeepEdge.Update(); } if (sortedEdges.Any()) { edge = sortedEdges[0]; } else { break; } } ts.RemoveEdges(removedEdges.Select(e => e.IndexInList).ToList()); ts.RemoveFaces(removedFaces.Select(f => f.IndexInList).ToList()); ts.RemoveVertices(removedVertices.Select(v => v.IndexInList).ToList()); }
/// <summary> /// Simplifies the tessellation so that no edge are shorter than provided the minimum edge length. /// </summary> /// <param name="ts">The ts.</param> /// <param name="minLength">The minimum length.</param> public static void Simplify(this TessellatedSolid ts, double minLength) { Simplify(ts, -1, minLength); }
/// <summary> /// Simplifies the tessellation by removing the provided number of faces. /// </summary> /// <param name="ts">The ts.</param> /// <param name="numberOfFacesToRemove">The number of faces.</param> public static void Simplify(this TessellatedSolid ts, int numberOfFacesToRemove) { Simplify(ts, numberOfFacesToRemove, double.PositiveInfinity); }
/// <summary> /// Makes the model visual3 d. /// </summary> /// <param name="ts">The ts.</param> /// <returns>Visual3D.</returns> private static Visual3D MakeModelVisual3D(TessellatedSolid ts) { var defaultMaterial = MaterialHelper.CreateMaterial( new System.Windows.Media.Color { A = ts.SolidColor.A, B = ts.SolidColor.B, G = ts.SolidColor.G, R = ts.SolidColor.R }); if (ts.HasUniformColor) { var positions = ts.Faces.SelectMany( f => f.Vertices.Select(v => new Point3D(v.Position[0], v.Position[1], v.Position[2]))); var normals = ts.Faces.SelectMany(f => f.Vertices.Select(v => new Vector3D(f.Normal[0], f.Normal[1], f.Normal[2]))); return(new ModelVisual3D { Content = new GeometryModel3D { Geometry = new MeshGeometry3D { Positions = new Point3DCollection(positions), // TriangleIndices = new Int32Collection(triIndices), Normals = new Vector3DCollection(normals) }, Material = defaultMaterial } }); } var result = new ModelVisual3D(); foreach (var f in ts.Faces) { var vOrder = new Point3DCollection(); for (var i = 0; i < 3; i++) { vOrder.Add(new Point3D(f.Vertices[i].X, f.Vertices[i].Y, f.Vertices[i].Z)); } var c = f.Color == null ? defaultMaterial : MaterialHelper.CreateMaterial(new System.Windows.Media.Color { A = f.Color.A, B = f.Color.B, G = f.Color.G, R = f.Color.R }); result.Children.Add(new ModelVisual3D { Content = new GeometryModel3D { Geometry = new MeshGeometry3D { Positions = vOrder }, Material = c } }); } return(result); }
/// <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); }