/// <summary>
        /// Determines the collinearity between a point and an edge.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="edge">The edge.</param>
        /// <returns>
        /// The type of collinearity.
        /// </returns>
        internal static Collinearity GetCollinearity(Vector3 point, DcelEdge edge)
        {
            Vector3 v0      = edge.Origin.Position;
            Vector3 v1      = edge.Twin.Origin.Position;
            Vector3 segment = v1 - v0;
            float   segmentLengthSquared = segment.LengthSquared();

            // Vector from segment to point.
            Vector3 v0ToPoint = point - v0;

            // Compute normal component of v0ToPoint (= v0ToPoint - "v0ToPoint projected on segment").
            float   v0ToPointDotSegment   = Vector3.Dot(v0ToPoint, segment);
            Vector3 normalFromLineToPoint = v0ToPoint - v0ToPointDotSegment / segmentLengthSquared * segment;

            if (normalFromLineToPoint.LengthSquared() > Numeric.EpsilonF * (1 + segmentLengthSquared))
            {
                // point is not on line.
                return(Collinearity.NotCollinear);
            }

            if (v0ToPointDotSegment < -Numeric.EpsilonF * segmentLengthSquared)
            {
                return(Collinearity.CollinearBefore);
            }
            if (v0ToPointDotSegment > (1 + Numeric.EpsilonF) * segmentLengthSquared)
            {
                return(Collinearity.CollinearAfter);
            }

            return(Collinearity.CollinearContained);
        }
 /// <summary>
 /// Adds the given edge to the stack if its <see cref="DcelEdge.Tag"/> is not equal to the given
 /// tag.
 /// </summary>
 /// <param name="edge">The edge. (Can be <see langword="null"/>.)</param>
 /// <param name="stack">The stack.</param>
 /// <param name="tag">The tag.</param>
 private static void AddUntaggedEdgeToStack(DcelEdge edge, Stack <DcelEdge> stack, int tag)
 {
     Debug.Assert(stack != null);
     if (edge != null && edge.Tag != tag)
     {
         stack.Push(edge);
     }
 }
示例#3
0
        /// <summary>
        /// Builds the component lists.
        /// </summary>
        /// <param name="edge">The current edge.</param>
        /// <remarks>
        /// This method calls itself recursively.
        /// </remarks>
        private void BuildLists(DcelEdge edge)
        {
            if (edge == null)
            {
                return;
            }

            Stack <DcelEdge> todoStack = new Stack <DcelEdge>();

            todoStack.Push(edge);
            while (todoStack.Count > 0)
            {
                edge = todoStack.Pop();

                if (edge.InternalTag == 1)
                {
                    continue;
                }

                // Register edge.
                edge.InternalTag = 1;
                _edges.Add(edge);

                // Register new vertices.
                if (edge.Origin != null && edge.Origin.InternalTag != 1)
                {
                    edge.Origin.InternalTag = 1;
                    _vertices.Add(edge.Origin);

                    AddUntaggedEdgeToStackInternal(edge.Origin.Edge, todoStack, 1);
                }

                // Register new faces.
                if (edge.Face != null && edge.Face.InternalTag != 1)
                {
                    edge.Face.InternalTag = 1;
                    _faces.Add(edge.Face);

                    AddUntaggedEdgeToStackInternal(edge.Face.Boundary, todoStack, 1);
                    if (edge.Face.Holes != null)
                    {
                        for (int i = 0; i < edge.Face.Holes.Count; i++)
                        {
                            AddUntaggedEdgeToStackInternal(edge.Face.Holes[i], todoStack, 1);
                        }
                    }
                }

                // Follow neighboring edges.
                AddUntaggedEdgeToStackInternal(edge.Next, todoStack, 1);
                AddUntaggedEdgeToStackInternal(edge.Previous, todoStack, 1);
                AddUntaggedEdgeToStackInternal(edge.Twin, todoStack, 1);
            }

            // Reset tags.
            ResetInternalTags();
        }
        /// <summary>
        /// Sets the tags of the DCEL mesh component and linked components to the given tag value.
        /// </summary>
        /// <param name="edge">The edge. (Can be <see langword="null"/>.)</param>
        /// <param name="tag">The tag.</param>
        /// <remarks>
        /// This method does nothing if <paramref name="edge"/> is already tagged with the given value.
        /// </remarks>
        private static void TagLinkedComponents(DcelEdge edge, int tag)
        {
            // Important: This method does not create recursive calls. This could
            // lead to stack overflows very quickly!

            if (edge == null || edge.Tag == tag)
            {
                return;
            }

            Stack <DcelEdge> todoStack = new Stack <DcelEdge>();

            todoStack.Push(edge);

            while (todoStack.Count > 0)
            {
                edge = todoStack.Pop();

                if (edge.Tag == tag)
                {
                    continue;
                }

                // Tag edge.
                edge.Tag = tag;

                // Tag vertex
                if (edge.Origin != null)
                {
                    edge.Origin.Tag = 1;
                }

                // Tag faces.
                if (edge.Face != null && edge.Face.Tag != tag)
                {
                    edge.Face.Tag = tag;

                    AddUntaggedEdgeToStack(edge.Face.Boundary, todoStack, tag);
                    if (edge.Face.Holes != null)
                    {
                        for (int i = 0; i < edge.Face.Holes.Count; i++)
                        {
                            AddUntaggedEdgeToStack(edge.Face.Holes[i], todoStack, tag);
                        }
                    }
                }

                // Follow connected edges.
                AddUntaggedEdgeToStack(edge.Next, todoStack, 1);
                AddUntaggedEdgeToStack(edge.Previous, todoStack, 1);
                AddUntaggedEdgeToStack(edge.Twin, todoStack, 1);
            }
        }
示例#5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="VertexAdjacency"/> class.
        /// </summary>
        /// <param name="mesh">The mesh for which the adjacency information is built.</param>
        /// <exception cref="NotSupportedException">
        /// Too many vertices in convex hull. Max. 65534 vertices in convex hull are supported.
        /// </exception>
        public VertexAdjacency(DcelMesh mesh)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            int numberOfVertices = mesh.Vertices.Count;

            if (numberOfVertices >= ushort.MaxValue)
            {
                throw new NotSupportedException("Too many vertices in convex hull. Max. 65534 vertices in convex hull are supported.");
            }

            Debug.Assert(mesh.Vertices.All(v => v.Tag == 0), "Tags of DcelMesh should be cleared.");

            // Store the index of each vertex in its tag for fast lookup.
            for (int i = 0; i < numberOfVertices; i++)
            {
                mesh.Vertices[i].Tag = i;
            }

            ListIndices = new ushort[numberOfVertices + 1];
            List <ushort> adjacencyLists = new List <ushort>(numberOfVertices * 4);

            for (int i = 0; i < numberOfVertices; i++)
            {
                DcelVertex vertex = mesh.Vertices[i];
                ListIndices[i] = (ushort)adjacencyLists.Count;

                // Gather all adjacent vertices.
                DcelEdge startEdge = vertex.Edge;
                DcelEdge edge      = startEdge;
                do
                {
                    DcelVertex adjacentVertex = edge.Next.Origin;
                    int        adjacentIndex  = adjacentVertex.Tag;
                    Debug.Assert(mesh.Vertices[adjacentIndex] == adjacentVertex, "Indices stored in DcelVertex.Tag are invalid.");
                    adjacencyLists.Add((ushort)adjacentIndex);
                    edge = edge.Twin.Next;
                } while (edge != startEdge);
            }

            // Add one additional entry which determines the end of the last adjacency list.
            ListIndices[numberOfVertices] = (ushort)adjacencyLists.Count;

            // Copy adjacency lists into static array.
            Lists = adjacencyLists.ToArray();
        }
        // Links to half-edges via Twin links if they are on the same edge (same vertices).
        private static void TryLink(DcelEdge edge0, DcelEdge edge1)
        {
            var start0 = edge0.Origin;
            var end0   = edge0.Next.Origin;

            var start1 = edge1.Origin;
            var end1   = edge1.Next.Origin;

            Debug.Assert(edge0 != edge1 && !(start0 == start1 && end0 == end1), "Two edges are identical.");

            if (start0 == end1 && end0 == start1)
            {
                edge0.Twin = edge1;
                edge1.Twin = edge0;
            }
        }
示例#7
0
        // edge vertices must have the plane distance in DcelVertex.UserData. This method computes
        // where the edge is split.
        private static Vector3 GetCutPosition(DcelEdge edge)
        {
            var startVertex   = edge.Origin;
            var startDistance = (float)startVertex.UserData;

            var endVertex   = edge.Twin.Origin;
            var endDistance = (float)endVertex.UserData;

            // Get interpolation parameter.
            var parameter = Math.Abs(startDistance) / (Math.Abs(startDistance) + Math.Abs(endDistance));

            // Get position where edge is cut.
            var cutPosition = startVertex.Position * (1 - parameter) + endVertex.Position * parameter;

            return(cutPosition);
        }
        /// <summary>
        /// Determines the collinearity between a point and an edge of a face.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="edge">The edge.</param>
        /// <param name="faceNormal">The face normal (of the face of the edge).</param>
        /// <param name="inFront">
        /// A value that is positive if the point is in front (outside of the face).
        /// 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, DcelEdge edge, Vector3 faceNormal, out float inFront)
        {
            Vector3 v0      = edge.Origin.Position;
            Vector3 v1      = edge.Twin.Origin.Position;
            Vector3 segment = v1 - v0;
            float   segmentLengthSquared = segment.LengthSquared();

            // See Geometric Tools for Computer Graphics, p. 736 for explanation in 2D.
            // Here we do the same in 3D. Our face normal is normalized.
            Debug.Assert(faceNormal.IsNumericallyNormalized);

            // The needed normal is computed from the face normal.
            // The normal lies in the face plane and points away from the face.
            Vector3 normal = Vector3.Cross(segment, faceNormal);
            float   normalLengthSquared = normal.LengthSquared();

            Vector3 v0ToPoint = point - v0;
            float   v0ToPointLengthSquared = v0ToPoint.LengthSquared();

            float dot = Vector3.Dot(v0ToPoint, normal);

            inFront = dot;
            if (dot * dot > Numeric.EpsilonF * normalLengthSquared * v0ToPointLengthSquared)
            {
                if (dot > 0)
                {
                    return(Collinearity.NotCollinearInFront); // Edge is visible from point.
                }
                if (dot < 0)
                {
                    return(Collinearity.NotCollinearBehind); // Edge is not visible from point.
                }
            }

            dot = Vector3.Dot(segment, v0ToPoint);

            if (dot < -Numeric.EpsilonF * segmentLengthSquared)
            {
                return(Collinearity.CollinearBefore);
            }
            if (dot > (1 + Numeric.EpsilonF) * segmentLengthSquared)
            {
                return(Collinearity.CollinearAfter);
            }

            return(Collinearity.CollinearContained);
        }
示例#9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DcelVertex"/> class with a given position and
 /// edge.
 /// </summary>
 /// <param name="position">The position.</param>
 /// <param name="edge">The edge.</param>
 public DcelVertex(Vector3 position, DcelEdge edge)
 {
     Position = position;
     Edge     = edge;
 }
        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);
        }
示例#11
0
        /// <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);
        }
示例#12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DcelFace"/> class from a given edge.
 /// </summary>
 /// <param name="boundary">An edge of the outer boundary.</param>
 public DcelFace(DcelEdge boundary)
 {
     Boundary = boundary;
 }
        /// <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();
        }