/// <summary> /// Determines the collinearity between a point and a face. /// </summary> /// <param name="point">The point.</param> /// <param name="face">The face.</param> /// <param name="inFront"> /// A value that is positive if the point is in front. And the value is proportional to the /// distance of the point from the edge. /// </param> /// <returns>The collinearity type.</returns> internal static Collinearity GetCollinearity(Vector3 point, DcelFace face, out float inFront) { Vector3 v0 = face.Boundary.Origin.Position; Vector3 normal = face.Normal; Vector3 v0ToPoint = point - v0; float dot = Vector3.Dot(v0ToPoint, normal); inFront = dot; var normalLengthSquared = normal.LengthSquared(); //if (normalLengthSquared < Numeric.EpsilonFSquared) //{ // // Make edge checks. // if (GetCollinearity(point, face.Boundary) == Collinearity.NotCollinear // && GetCollinearity(point, face.Boundary.Next) == Collinearity.NotCollinear) // return Collinearity.NotCollinear; // // // Not on the triangle line. // return Collinearity.NotCollinear; //} if (dot * dot > Numeric.EpsilonF * normalLengthSquared * v0ToPoint.LengthSquared()) { if (dot > 0) { return(Collinearity.NotCollinearInFront); // Face is visible from point. } if (dot < 0) { return(Collinearity.NotCollinearBehind); // Face is not visible from point. } } return(Collinearity.Collinear); }
public static DcelMesh FromTriangleMesh(TriangleMesh mesh) { // TODO: To optimize, check tricks of TriangleMeshShape.ComputeNeighbors. if (mesh == null) { throw new ArgumentNullException("mesh"); } if (mesh.Vertices == null || mesh.Indices == null) { throw new ArgumentException("Input mesh has no vertices or vertex indices."); } // Create vertices. int numberOfVertices = mesh.Vertices.Count; var vertices = new List <DcelVertex>(numberOfVertices); foreach (var position in mesh.Vertices) { vertices.Add(new DcelVertex(position, null)); } // Weld similar vertices. for (int i = 0; i < numberOfVertices; i++) { for (int j = i + 1; j < numberOfVertices; j++) { if (Vector3.AreNumericallyEqual(vertices[i].Position, vertices[j].Position)) { vertices[i] = vertices[j]; } } } // Create edges and faces for each triangle. // We need at least 3 edges for each triangle. We might need more edges if we have to // connect unconnected islands of triangles. var edges = new List <DcelEdge>(mesh.NumberOfTriangles * 3 * 2); var faces = new List <DcelFace>(mesh.NumberOfTriangles); for (int i = 0; i < mesh.NumberOfTriangles; i++) { // Get triangle indices. var index0 = mesh.Indices[i * 3 + 0]; var index1 = mesh.Indices[i * 3 + 1]; var index2 = mesh.Indices[i * 3 + 2]; // Get DCEL vertices. var vertex0 = vertices[index0]; var vertex1 = vertices[index1]; var vertex2 = vertices[index2]; // Create 3 edges. var edge0 = new DcelEdge(); var edge1 = new DcelEdge(); var edge2 = new DcelEdge(); // Create 1 face. var face = new DcelFace(); // Fill out face info. face.Boundary = edge0; // Fill out vertex info. vertex0.Edge = edge0; vertex1.Edge = edge1; vertex2.Edge = edge2; // Fill out edge info. // Twin links are created later. edge0.Face = face; edge0.Origin = vertex0; edge0.Next = edge1; edge0.Previous = edge2; edge1.Face = face; edge1.Origin = vertex1; edge1.Next = edge2; edge1.Previous = edge0; edge2.Face = face; edge2.Origin = vertex2; edge2.Next = edge0; edge2.Previous = edge1; // Add to lists. edges.Add(edge0); edges.Add(edge1); edges.Add(edge2); faces.Add(face); } // Connect triangles that share an edge. for (int i = 0; i < faces.Count; i++) { // Get face and its 3 edges. var faceI = faces[i]; var edgeI0 = faceI.Boundary; var edgeI1 = edgeI0.Next; var edgeI2 = edgeI1.Next; Debug.Assert(edgeI2.Next == edgeI0); for (int j = i + 1; j < faces.Count; j++) { // Get face and its 3 edges. var faceJ = faces[j]; var edgeJ0 = faceJ.Boundary; var edgeJ1 = edgeJ0.Next; var edgeJ2 = edgeJ1.Next; Debug.Assert(edgeJ2.Next == edgeJ0); TryLink(edgeI0, edgeJ0); TryLink(edgeI0, edgeJ1); TryLink(edgeI0, edgeJ2); TryLink(edgeI1, edgeJ0); TryLink(edgeI1, edgeJ1); TryLink(edgeI1, edgeJ2); TryLink(edgeI2, edgeJ0); TryLink(edgeI2, edgeJ1); TryLink(edgeI2, edgeJ2); } } // If the mesh is not closed, we have to add twin edges at the boundaries foreach (var edge in edges.ToArray()) { if (edge.Twin == null) { var twin = new DcelEdge(); twin.Origin = edge.Next.Origin; twin.Twin = edge; edge.Twin = twin; edges.Add(twin); } } // Yet, twin.Next/Previous were not set. foreach (var edge in edges) { if (edge.Previous == null) { // The previous edge has not been set. // Search the edges around the origin until we find the previous unconnected edge. var origin = edge.Origin; var originEdge = edge.Twin.Next; // Another edge with the same origin. while (originEdge.Twin.Next != null) { Debug.Assert(originEdge.Origin == origin); originEdge = originEdge.Twin.Next; } var previous = originEdge.Twin; previous.Next = edge; edge.Previous = previous; } } // Check if we have one connected mesh. if (vertices.Count > 0) { const int Tag = 1; TagLinkedComponents(vertices[0], Tag); // Check if all components were reached. if (vertices.Any(v => v.Tag != Tag) || edges.Any(e => e.Tag != Tag) || faces.Any(f => f.Tag != Tag)) { throw new NotSupportedException("The triangle mesh consists of several unconnected components or sub-meshes."); } } var dcelMesh = new DcelMesh { Vertex = vertices.FirstOrDefault() }; dcelMesh.ResetTags(); return(dcelMesh); }
/// <summary> /// Cuts mesh with a plane. /// </summary> /// <param name="plane">The plane.</param> /// <returns> /// <see langword="true"/> if the mesh was cut; otherwise, <see langword="false"/> if the mesh /// was not modified. /// </returns> /// <remarks> /// <para> /// The mesh is cut with the given plane. If any parts are in front of the plane, they are /// removed. Edges and faces that go through the plane are cut. The resulting mesh is closed /// with a new face in the cut plane. /// </para> /// <para> /// If the whole mesh is in front of the plane, the whole mesh is removed (<see cref="Vertex"/> /// is set to <see langword="null"/>). If the whole mesh is behind the plane, this method does /// nothing. /// </para> /// <para> /// <strong>Prerequisites:</strong> This operation uses <see cref="DcelVertex.UserData"/> of the /// vertices and it assumes that the mesh is valid, convex and closed. All faces must be /// triangles or convex polygons. /// </para> /// </remarks> public bool CutConvex(Plane plane) { // Cuts the mesh with the given plane. The part over the plane is removed. // Prerequisites: // - Mesh is valid, convex and closed. // - Faces are convex polygons. // - Vertex user data is null. // Notes: // - Vertex user data is used. // Get AABB of whole mesh. var aabb = GetAabb(); // Create an epsilon relative to the mesh size. float epsilon = Numeric.EpsilonF * (1 + aabb.Extent.Length); // Store distance d to plane in all vertices. d is negative if vertex is below the plane. float minDistance = Single.PositiveInfinity; float maxDistance = Single.NegativeInfinity; foreach (var vertex in Vertices) { float d = Vector3.Dot(vertex.Position, plane.Normal) - plane.DistanceFromOrigin; vertex.UserData = d; minDistance = Math.Min(d, minDistance); maxDistance = Math.Max(d, maxDistance); } if (minDistance > -epsilon) { // All vertices are in or above the plane. - The whole mesh is cut away. Vertex = null; Dirty = true; return(true); } if (maxDistance < epsilon) { // All vertices are in or under the plane. - The mesh is not cut. return(false); } // Now, we know that the mesh must be cut. // Find a first edge that starts under/in the plane and must be cut. DcelEdge first = null; foreach (var edge in Edges) { var startDistance = (float)edge.Origin.UserData; var endDistance = (float)edge.Twin.Origin.UserData; if (startDistance < -epsilon && // The edge starts below the plane and the start vertex is definitely not on the plane. endDistance > -epsilon) // The edge ends in or above the plane. { first = edge; break; } } Debug.Assert(first != null, "The mesh is cut by the plane, but could not find an edge that is cut!?"); // The cut will be sealed with this face. DcelFace cap = new DcelFace(); var outEdge = first; // Edge that goes out of the plane and has to be cut. DcelVertex newVertex = new DcelVertex(); newVertex.Position = GetCutPosition(outEdge); newVertex.Edge = first.Twin; do { // Find inEdge (edge that goes into the plane and must be cut). var inEdge = outEdge.Next; while ((float)inEdge.Twin.Origin.UserData > -epsilon) // Loop until the edge end is definitely under the plane. { inEdge = inEdge.Next; } // The face will be cut between outEdge and inEdge. // Two new half edges: var newEdge = new DcelEdge(); // Edge for the cut face. var capEdge = new DcelEdge(); // Twin edge on the cap. // Update outEdge. outEdge.Next = newEdge; // Init newEdge. newEdge.Face = outEdge.Face; newEdge.Origin = newVertex; newEdge.Previous = outEdge; newEdge.Next = inEdge; newEdge.Twin = capEdge; // Find next newVertex on inEdge. if (inEdge.Twin != first) { // Find cut on inEdge. newVertex = new DcelVertex(); newVertex.Position = GetCutPosition(inEdge); newVertex.Edge = inEdge; } else { // We have come full circle. The inEdge is the twin of the first outEdge. newVertex = first.Next.Origin; } // Init capEdge. capEdge.Face = cap; capEdge.Origin = newVertex; capEdge.Twin = newEdge; if (cap.Boundary == null) { cap.Boundary = capEdge; } else { capEdge.Next = outEdge.Twin.Previous.Twin; capEdge.Next.Previous = capEdge; } // Update inEdge. inEdge.Origin = newVertex; inEdge.Previous = newEdge; // Make sure the cut face does not link to a removed edge. outEdge.Face.Boundary = outEdge; // The next outEdge is the twin of the inEdge. outEdge = inEdge.Twin; } while (outEdge != first); // We still have to link the first and the last cap edge. var firstCapEdge = cap.Boundary; var lastCapEdge = first.Twin.Previous.Twin; firstCapEdge.Next = lastCapEdge; lastCapEdge.Previous = firstCapEdge; // Make sure DcelMesh.Vertex is not one of the removed vertices. Vertex = newVertex; Dirty = true; // Clear vertex user data. foreach (var vertex in Vertices) { vertex.UserData = null; } return(true); }
/// <summary> /// Determines the collinearity between a point and a face. /// </summary> /// <param name="point">The point.</param> /// <param name="face">The face.</param> /// <returns>The collinearity type.</returns> internal static Collinearity GetCollinearity(Vector3 point, DcelFace face) { float dummy; return(GetCollinearity(point, face, out dummy)); }
// Checks if the face has a boundary and if all boundary edges are set to this face. private bool IsValid(DcelFace face, out string errorDescription) { // TODO: Do we need anything special for holes? Debug.Assert(face != null); var boundary = face.Boundary; if (boundary == null) { errorDescription = "Face.Boundary is null."; return(false); } if (boundary.Face != face) { errorDescription = "A boundary edge of a face links to another face."; return(false); } if (boundary.Next == boundary) { errorDescription = "Edge.Next links to itself."; return(false); } try { // Check if boundary is closed and if all edges are linked to the face. boundary.InternalTag = 1; var edge = boundary.Next; while (edge != boundary && (edge == null || edge.InternalTag != 1)) { if (edge == null) { errorDescription = "Edge.Next is not set for a face boundary edge."; return(false); } if (edge.Face != face) { errorDescription = "A boundary edge of a face links to another face."; return(false); } edge.InternalTag = 1; edge = edge.Next; } if (edge != boundary) { errorDescription = "Face has stray boundary edges."; return(false); } } finally { ResetInternalTags(); } errorDescription = null; return(true); }
/// <summary> /// Creates a mesh for unit cube. /// </summary> /// <returns>The DCEL mesh that represent a unit cube.</returns> /// <remarks> /// The cube is centered at the origin and has 6 faces. The edge length is 2. /// </remarks> public static DcelMesh CreateCube() { var vertex0 = new DcelVertex(new Vector3(-1, -1, -1), null); var vertex1 = new DcelVertex(new Vector3(1, -1, -1), null); var vertex2 = new DcelVertex(new Vector3(-1, 1, -1), null); var vertex3 = new DcelVertex(new Vector3(1, 1, -1), null); var vertex4 = new DcelVertex(new Vector3(-1, -1, 1), null); var vertex5 = new DcelVertex(new Vector3(1, -1, 1), null); var vertex6 = new DcelVertex(new Vector3(-1, 1, 1), null); var vertex7 = new DcelVertex(new Vector3(1, 1, 1), null); var near = new DcelFace(); // +z face var far = new DcelFace(); // -z face var top = new DcelFace(); // +y face var bottom = new DcelFace(); // -y face var left = new DcelFace(); // -x face var right = new DcelFace(); // +x face var edge01 = new DcelEdge { Origin = vertex0, Face = bottom }; var edge10 = new DcelEdge { Origin = vertex1, Face = far }; var edge13 = new DcelEdge { Origin = vertex1, Face = right }; var edge31 = new DcelEdge { Origin = vertex3, Face = far }; var edge23 = new DcelEdge { Origin = vertex2, Face = far }; var edge32 = new DcelEdge { Origin = vertex3, Face = top }; var edge02 = new DcelEdge { Origin = vertex0, Face = far }; var edge20 = new DcelEdge { Origin = vertex2, Face = left }; var edge26 = new DcelEdge { Origin = vertex2, Face = top }; var edge62 = new DcelEdge { Origin = vertex6, Face = left }; var edge37 = new DcelEdge { Origin = vertex3, Face = right }; var edge73 = new DcelEdge { Origin = vertex7, Face = top }; var edge04 = new DcelEdge { Origin = vertex0, Face = left }; var edge40 = new DcelEdge { Origin = vertex4, Face = bottom }; var edge15 = new DcelEdge { Origin = vertex1, Face = bottom }; var edge51 = new DcelEdge { Origin = vertex5, Face = right }; var edge45 = new DcelEdge { Origin = vertex4, Face = near }; var edge54 = new DcelEdge { Origin = vertex5, Face = bottom }; var edge57 = new DcelEdge { Origin = vertex5, Face = near }; var edge75 = new DcelEdge { Origin = vertex7, Face = right }; var edge46 = new DcelEdge { Origin = vertex4, Face = left }; var edge64 = new DcelEdge { Origin = vertex6, Face = near }; var edge67 = new DcelEdge { Origin = vertex6, Face = top }; var edge76 = new DcelEdge { Origin = vertex7, Face = near }; vertex0.Edge = edge01; vertex1.Edge = edge10; vertex2.Edge = edge20; vertex3.Edge = edge31; vertex4.Edge = edge40; vertex5.Edge = edge57; vertex6.Edge = edge67; vertex7.Edge = edge76; near.Boundary = edge57; far.Boundary = edge10; left.Boundary = edge62; right.Boundary = edge51; top.Boundary = edge67; bottom.Boundary = edge01; edge01.Twin = edge10; edge10.Twin = edge01; edge13.Twin = edge31; edge31.Twin = edge13; edge23.Twin = edge32; edge32.Twin = edge23; edge02.Twin = edge20; edge20.Twin = edge02; edge26.Twin = edge62; edge62.Twin = edge26; edge37.Twin = edge73; edge73.Twin = edge37; edge04.Twin = edge40; edge40.Twin = edge04; edge15.Twin = edge51; edge51.Twin = edge15; edge45.Twin = edge54; edge54.Twin = edge45; edge57.Twin = edge75; edge75.Twin = edge57; edge46.Twin = edge64; edge64.Twin = edge46; edge67.Twin = edge76; edge76.Twin = edge67; edge10.Next = edge02; edge02.Next = edge23; edge23.Next = edge31; edge31.Next = edge10; // far edge02.Previous = edge10; edge23.Previous = edge02; edge31.Previous = edge23; edge10.Previous = edge31; edge45.Next = edge57; edge57.Next = edge76; edge76.Next = edge64; edge64.Next = edge45; // near edge57.Previous = edge45; edge76.Previous = edge57; edge64.Previous = edge76; edge45.Previous = edge64; edge62.Next = edge20; edge20.Next = edge04; edge04.Next = edge46; edge46.Next = edge62; // left edge20.Previous = edge62; edge04.Previous = edge20; edge46.Previous = edge04; edge62.Previous = edge46; edge51.Next = edge13; edge13.Next = edge37; edge37.Next = edge75; edge75.Next = edge51; // right edge13.Previous = edge51; edge37.Previous = edge13; edge75.Previous = edge37; edge51.Previous = edge75; edge67.Next = edge73; edge73.Next = edge32; edge32.Next = edge26; edge26.Next = edge67; // top edge73.Previous = edge67; edge32.Previous = edge73; edge26.Previous = edge32; edge67.Previous = edge26; edge01.Next = edge15; edge15.Next = edge54; edge54.Next = edge40; edge40.Next = edge01; // bottom edge15.Previous = edge01; edge54.Previous = edge15; edge40.Previous = edge54; edge01.Previous = edge40; return(new DcelMesh { Vertex = vertex0 }); }
internal void MergeCoplanarFaces() { UpdateCache(); foreach (DcelEdge edge in _edges) { if (edge.InternalTag == 1) { // Edge has already been visited. continue; } DcelEdge twin = edge.Twin; DcelFace face0 = edge.Face; DcelFace face1 = twin.Face; if (face0 != null && face1 != null) { // Compare face normals. Vector3 normal0 = face0.Normal; Vector3 normal1 = face1.Normal; float cosα = Vector3.Dot(normal0, normal1) / (normal0.Length * normal1.Length); if (Numeric.AreEqual(cosα, 1)) { // Faces are coplanar: // --> Merge faces and remove edge. // Get vertices and edges. DcelVertex vertex0 = edge.Origin; // Start vertex of edge. DcelVertex vertex1 = twin.Origin; // End vertex of edge. DcelEdge vertex0Incoming = edge.Previous; DcelEdge vertex0Outgoing = twin.Next; DcelEdge vertex1Incoming = twin.Previous; DcelEdge vertex1Outgoing = edge.Next; // Update vertices. vertex0.Edge = vertex0Outgoing; vertex1.Edge = vertex1Outgoing; // Update edges. vertex0Incoming.Next = vertex0Outgoing; vertex0Outgoing.Previous = vertex0Incoming; vertex1Incoming.Next = vertex1Outgoing; vertex1Outgoing.Previous = vertex1Incoming; // Throw away face1. Make sure that all edges point to face0. DcelEdge current = vertex0Outgoing; do { current.Face = face0; current = current.Next; } while (current != vertex0Outgoing); Dirty = true; } // Mark edges as visited. edge.InternalTag = 1; twin.InternalTag = 1; } } ResetTags(); }