public void Tetrahedron() { VertexBase vA = new VertexBase(); VertexBase vB = new VertexBase(); VertexBase vC = new VertexBase(); VertexBase vD = new VertexBase(); mesh.Add(vA); mesh.Add(vB); mesh.Add(vC); mesh.Add(vD); EdgeBase e1 = mesh.AddEdge(vA, vB); EdgeBase e2 = mesh.AddEdge(vB, vC); EdgeBase e3 = mesh.AddEdge(vC, vA); EdgeBase e4 = mesh.AddEdge(vA, vD); EdgeBase e5 = mesh.AddEdge(vB, vD); EdgeBase e6 = mesh.AddEdge(vC, vD); FaceBase fa = mesh.AddFace(e1, e2, e3); FaceBase fb = mesh.AddFace(e1, e5, e4); FaceBase fc = mesh.AddFace(e2, e6, e5); FaceBase fd = mesh.AddFace(e3, e4, e6); Assert.AreNotEqual(e1, e2); Assert.AreNotEqual(e2, e3); Assert.AreNotEqual(e3, e4); Assert.AreNotEqual(e4, e5); Assert.AreNotEqual(e5, e6); Assert.AreEqual(3, vA.Degree); Assert.AreEqual(3, vB.Degree); Assert.AreEqual(3, vC.Degree); Assert.AreEqual(3, vD.Degree); Assert.AreEqual(4, mesh.VertexCount); Assert.AreEqual(6, mesh.EdgeCount); Assert.AreEqual(3, fa.EdgeCount); Assert.AreEqual(3, fb.EdgeCount); Assert.AreEqual(3, fc.EdgeCount); Assert.AreEqual(3, fd.EdgeCount); }
/// <summary> /// The default delagate for splitting faces, used unless an alternative was supplied to the constructor. /// </summary> /// <param name="mesh">The mesh in which the face and vertices reside</param> /// <param name="face">The face to be split; continues to exist as one of the 'new' faces</param> /// <param name="v1">Vertex on the new edge</param> /// <param name="v2">Vertex on the new edge</param> /// <returns>A new face above the new diagonal.</returns> private static TEdge SplitFace(Mesh <TVertex, TEdge, TFace> mesh, FaceBase face, ChainVertex v1, ChainVertex v2) { if (v1.Chain < v2.Chain) { return(mesh.SplitFace(face, v1.Vertex, v2.Vertex)); } if (v1.Chain > v2.Chain) { return(mesh.SplitFace(face, v2.Vertex, v1.Vertex)); } Debug.Assert(v1.Chain == v2.Chain); if (v1.Chain == Chain.Right) { return(mesh.SplitFace(face, v2.Vertex, v1.Vertex)); } return(mesh.SplitFace(face, v1.Vertex, v2.Vertex)); //var faces = edge.Faces; //TFace otherFace = (TFace) faces.First; //return otherFace; }
private void Triangulate() { // From O'Rourke (1994) Computational Geometry in C, section 2.1 // Monotone Partitioning // To identify the chains: The vertices in each chain of a monotone // polygon are sorted with respect to the line of monotonicity [y-axis]. // Then the vertices can be sorted by the y-axis in linear time: Find a // Highest vertex, find a lowest, and partition the boundary between the two // chains. The vertices in each chain are sorted with respect to y. // Two sorted lists of vertices can be merged in linear time into one list // sorted by y. Debug.Assert(face.EdgeCount > 3); var vertices = face.Vertices.Cast <TVertex>(); IComparer <Point2D> pointComparer = new HighYLowXComparer(); // TODO: Replace with Transform extension method. Consider removing transform comparer IComparer <TVertex> vertexComparer = new TransformComparer <TVertex, Point2D>(pointComparer, v => v.Position); Pair <TVertex, TVertex> minMax = vertices.MinMax(vertexComparer); meshUtilities = new MonotoneMeshUtilities <TVertex>(pointComparer); LeftToRightEdgeComparer xEdgeComparer = new LeftToRightEdgeComparer(); sweeplineUtilities = new SweeplineUtilities(xEdgeComparer); // Assign each vertex to the left or right chain IEnumerable <ChainVertex> leftChainReversed = TraceLeftAndUp(minMax.First); IEnumerable <ChainVertex> leftChain = leftChainReversed.Reverse(); IEnumerable <ChainVertex> rightChain = TraceRightAndDown(minMax.Second); // From Berg et al (2000) Computational Geometry Algorithms and Applications // 1. Merge the vertices on the right chain with those on the left // chain into one sequence sorted on decreasing y-coordinate. If // two vertices have the same y-coordinate the left one comes first. // Let u1 to un denote the sorted sequence. IComparer <ChainVertex> chainVertexComparer = vertexComparer.Transform <ChainVertex, TVertex>(p => p.Vertex).Invert(); IEnumerable <ChainVertex> mergedVertices = rightChain.MergeSorted(leftChain, chainVertexComparer); List <ChainVertex> u = new List <ChainVertex>(mergedVertices); // 2. Initialize an empty stack and push u1 and u2 onto it // This stack contains all the vertices of the polygon that have // already been encountered, but which may require additional diagonals Stack <ChainVertex> stack = new Stack <ChainVertex>(); stack.Push(u[0]); stack.Push(u[1]); for (int j = 2; j < u.Count - 1; ++j) { // If u[j] and the vertext on top of the stack are of different chains if (u[j].Chain != stack.Peek().Chain) { // Insert a diagonal from u[j] to each popped vertex... // We progessively subdivide the new faces created by the splitting // so we must keep track of which face to split FaceBase faceToSplit = face; while (stack.Count > 1) { TEdge edge = splitFace(mesh, faceToSplit, u[j], stack.Pop()); faceToSplit = edge.Faces.First; // First is always the new face } // ... except the last one if (stack.Count > 0) { stack.Pop(); } // Push u j-1 and uj onto the stack stack.Push(u[j - 1]); stack.Push(u[j]); } else { // Pop one vertex from the stack; this vertex is already connected ChainVertex previous = stack.Pop(); // We progressively split triangles from the original face TFace faceToSplit = face; while (stack.Count > 0 && CanInsertDiagonal(u[j], stack.Peek(), previous)) { previous = stack.Pop(); splitFace(mesh, faceToSplit, u[j], previous); } // Push the last vertex popped back onto the stack stack.Push(previous); // Push uj onto the stack stack.Push(u[j]); } } // Add diagonals from un to all vertices except the first and last one stack.Pop(); // Which chain is the stack of vertices on - affects which face we split Chain stackChain = stack.Peek().Chain; FaceBase finalFaceToSplit = face; while (stack.Count > 1) { TEdge edge = splitFace(mesh, finalFaceToSplit, u[u.Count - 1], stack.Pop()); finalFaceToSplit = stackChain == Chain.Left ? edge.Faces.First : edge.Faces.Second; } Debug.Assert(stack.Count == 1); triangulated = true; }