/// <summary>
 /// Sets the tags of the DCEL mesh component and linked components to the given tag value.
 /// </summary>
 /// <param name="vertex">The vertex. (Can be <see langword="null"/>.)</param>
 /// <param name="tag">The tag.</param>
 /// <remarks>
 /// This method does nothing if <paramref name="vertex"/> is already tagged with the given
 /// value.
 /// </remarks>
 private static void TagLinkedComponents(DcelVertex vertex, int tag)
 {
     if (vertex != null && vertex.Tag != tag)
     {
         vertex.Tag = tag;
         TagLinkedComponents(vertex.Edge, tag);
     }
 }
Example #2
0
 /// <summary>
 /// Sets the tags of the DCEL mesh component and linked components to the given tag value.
 /// </summary>
 /// <param name="vertex">The vertex. (Can be <see langword="null"/>.)</param>
 /// <param name="tag">The tag.</param>
 /// <remarks>
 /// This method does nothing if <paramref name="vertex"/> is already tagged with the given
 /// value.
 /// </remarks>
 private static void TagLinkedComponents(DcelVertex vertex, int tag)
 {
     if (vertex != null && vertex.Tag != tag)
       {
     vertex.Tag = tag;
     TagLinkedComponents(vertex.Edge, tag);
       }
 }
Example #3
0
        /// <summary>
        /// Converts this mesh to a <see cref="TriangleMesh"/>.
        /// </summary>
        /// <returns>A triangle mesh that represents the same mesh.</returns>
        /// <remarks>
        /// This mesh must consist only of faces that are convex polygons (triangles, quads, etc.).
        /// Concave faces will not be triangulated correctly.
        /// </remarks>
        /// <exception cref="GeometryException">The DCEL mesh is invalid.</exception>
        public TriangleMesh ToTriangleMesh()
        {
            TriangleMesh triMesh = new TriangleMesh();

            UpdateCache();

            int numberOfVertices = Vertices.Count;
            int numberOfFaces    = Faces.Count;

            for (int i = 0; i < numberOfVertices; i++)
            {
                DcelVertex vertex = Vertices[i];

                // Store the vertex indices in the InternalTags of the vertices.
                vertex.InternalTag = i;

                // Add all vertices to the triangle mesh.
                triMesh.Vertices.Add(vertex.Position);
            }

            for (int i = 0; i < numberOfFaces; i++)
            {
                var face      = Faces[i];
                var startEdge = face.Boundary;
                var nextEdge  = startEdge.Next;

                // Tag edges to see if they have been visited.
                startEdge.InternalTag = 1;
                nextEdge.InternalTag  = 1;

                // Triangulate polygon.
                var v0 = startEdge.Origin;
                var v1 = nextEdge.Origin;
                nextEdge = nextEdge.Next;
                while (nextEdge != startEdge)
                {
                    if (nextEdge == null || nextEdge.InternalTag == 1)
                    {
                        throw new GeometryException("DCEL mesh is invalid.");
                    }

                    nextEdge.InternalTag = 1;
                    var v2 = nextEdge.Origin;

                    // The tags of the vertices store their index number.
                    triMesh.Indices.Add(v0.InternalTag);
                    triMesh.Indices.Add(v1.InternalTag);
                    triMesh.Indices.Add(v2.InternalTag);

                    v1       = v2;
                    nextEdge = nextEdge.Next;
                }
            }

            ResetInternalTags();

            return(triMesh);
        }
Example #4
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();
        }
        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.
                    Vector3F normal0 = face0.Normal;
                    Vector3F normal1 = face1.Normal;
                    float    cosĪ±    = Vector3F.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();
        }
        /// <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()
        {
            #region ----- Vertices -----
            var vertex0 = new DcelVertex(new Vector3F(-1, -1, -1), null);
            var vertex1 = new DcelVertex(new Vector3F(1, -1, -1), null);
            var vertex2 = new DcelVertex(new Vector3F(-1, 1, -1), null);
            var vertex3 = new DcelVertex(new Vector3F(1, 1, -1), null);
            var vertex4 = new DcelVertex(new Vector3F(-1, -1, 1), null);
            var vertex5 = new DcelVertex(new Vector3F(1, -1, 1), null);
            var vertex6 = new DcelVertex(new Vector3F(-1, 1, 1), null);
            var vertex7 = new DcelVertex(new Vector3F(1, 1, 1), null);
            #endregion

            #region ----- Faces -----
            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
            #endregion

            #region ----- Edges -----
            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
            };
            #endregion

            #region ----- Set DcelVertex.Edge -----
            vertex0.Edge = edge01;
            vertex1.Edge = edge10;
            vertex2.Edge = edge20;
            vertex3.Edge = edge31;
            vertex4.Edge = edge40;
            vertex5.Edge = edge57;
            vertex6.Edge = edge67;
            vertex7.Edge = edge76;
            #endregion

            #region ----- Set DcelFace.Boundary -----
            near.Boundary   = edge57;
            far.Boundary    = edge10;
            left.Boundary   = edge62;
            right.Boundary  = edge51;
            top.Boundary    = edge67;
            bottom.Boundary = edge01;
            #endregion

            #region ----- Set DcelEdge.Twin -----
            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;
            #endregion

            #region ----- Set DcelEdge.Next/Previous -----
            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;
            #endregion

            return(new DcelMesh {
                Vertex = vertex0
            });
        }
Example #7
0
        /// <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()
        {
            #region ----- Vertices -----
              var vertex0 = new DcelVertex(new Vector3F(-1, -1, -1), null);
              var vertex1 = new DcelVertex(new Vector3F( 1, -1, -1), null);
              var vertex2 = new DcelVertex(new Vector3F(-1,  1, -1), null);
              var vertex3 = new DcelVertex(new Vector3F( 1,  1, -1), null);
              var vertex4 = new DcelVertex(new Vector3F(-1, -1,  1), null);
              var vertex5 = new DcelVertex(new Vector3F( 1, -1,  1), null);
              var vertex6 = new DcelVertex(new Vector3F(-1,  1,  1), null);
              var vertex7 = new DcelVertex(new Vector3F( 1,  1,  1), null);
              #endregion

              #region ----- Faces -----
              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
              #endregion

              #region ----- Edges -----
              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 };
              #endregion

              #region ----- Set DcelVertex.Edge -----
              vertex0.Edge = edge01;
              vertex1.Edge = edge10;
              vertex2.Edge = edge20;
              vertex3.Edge = edge31;
              vertex4.Edge = edge40;
              vertex5.Edge = edge57;
              vertex6.Edge = edge67;
              vertex7.Edge = edge76;
              #endregion

              #region ----- Set DcelFace.Boundary -----
              near.Boundary = edge57;
              far.Boundary = edge10;
              left.Boundary = edge62;
              right.Boundary = edge51;
              top.Boundary = edge67;
              bottom.Boundary = edge01;
              #endregion

              #region ----- Set DcelEdge.Twin -----
              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;
              #endregion

              #region ----- Set DcelEdge.Next/Previous -----
              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;
              #endregion

              return new DcelMesh { Vertex = vertex0 };
        }
Example #8
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 = Vector3F.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;
        }
Example #9
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 = Vector3F.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);
        }
Example #10
0
        /// <summary>
        /// Merges a point to a convex hull that is a polygon.
        /// </summary>
        /// <param name="point">The point.</param>
        private void GrowPlanarConvex(Vector3F point)
        {
            Debug.Assert(_mesh != null);
              Debug.Assert(_mesh.Vertices.Count > 2, "DCEL mesh with 3 or more vertices expected.");
              Debug.Assert(_type == ConvexHullType.Planar, "DCEL mesh with 2 faces expected.");

              DcelFace frontFace = _mesh.Vertex.Edge.Face;
              DcelFace backFace = frontFace.Boundary.Twin.Face;

              // This check: if (!_isPlanar)
              // is important! Because for planar input points we could not find an initial
              // tetrahedron. All points are in a plane. But sometimes GetCollinearity detects that
              // a point is not in the plane and this creates very degenerate spatial convex hulls.
              // For real spatial input points we have no problem because we have a good initial
              // tetrahedron.
              if (!_isPlanar)
              {
            var collinearity = DcelMesh.GetCollinearity(point, frontFace);
            if (collinearity == Collinearity.NotCollinearInFront)
            {
              // We build a pyramid where the front face is removed.
              GrowPlanarConvexToSpatial(frontFace, point);
              return;
            }

            if (collinearity == Collinearity.NotCollinearBehind)
            {
              // We build a pyramid where the back face is removed.
              GrowPlanarConvexToSpatial(backFace, point);
              return;
            }
              }

              // ----- The point is in the plane.

              // If we get to here, the initial tetrahedron is flat, so we create a flat hull.
              _isPlanar = true;

              // Get normal of front face.
              Vector3F faceNormal = frontFace.Normal / frontFace.Normal.Length;

              // Find a visible edge.
              DcelEdge currentEdge = _mesh.Vertex.Edge;
              DcelEdge visibleEdge = null;
              do
              {
            float distance;
            DcelMesh.GetCollinearity(point, currentEdge, faceNormal, out distance);
            if (distance >= 0)
            {
              // Current is visible.
              visibleEdge = currentEdge;
              break;
            }

            // Get next edge.
            currentEdge = currentEdge.Next;

              } while (currentEdge != _mesh.Vertex.Edge);

              if (visibleEdge == null)
              {
            // Point is in the convex hull.
            return;
              }

              // Move the point a bit away to avoid numerical problems.
              Vector3F v0 = visibleEdge.Origin.Position;
              Vector3F v1 = visibleEdge.Twin.Origin.Position;
              Vector3F segment = v1 - v0;
              Vector3F normal = Vector3F.Cross(segment, faceNormal);
              point += Numeric.EpsilonF * normal;

              // Now we need to find the lower vertex and upper vertex between which
              // the edges will be removed.
              // Two new edges will be added: (lowerVertex, Point) and (Point, upperVertex).
              DcelVertex lowerVertex = null;
              DcelVertex upperVertex = null;

              // Find upperVertex.
              currentEdge = visibleEdge.Next;
              do
              {
            float distance;
            DcelMesh.GetCollinearity(point, currentEdge, faceNormal, out distance);
            if (distance < 0)
            {
              // Edge is not visible. We have found the upper vertex.
              upperVertex = currentEdge.Origin;
              break;
            }

            currentEdge = currentEdge.Next;
              } while (currentEdge != visibleEdge.Next);

              // upperVertex == null should not happen. But numerical problems can always occur.
              // In that case we do nothing.
              Debug.Assert(upperVertex != null, "Could not find upperVertex.");
              if (upperVertex == null)
            return;

              // Find lowerVertex
              currentEdge = visibleEdge.Previous;
              do
              {
            float distance;
            DcelMesh.GetCollinearity(point, currentEdge, faceNormal, out distance);
            if (distance < 0)
            {
              // Edge is not visible. The end point of the edge is the lower vertex.
              lowerVertex = currentEdge.Next.Origin;
              break;
            }

            currentEdge = currentEdge.Previous;
              } while (currentEdge != visibleEdge.Previous);

              // lowerVertex == null should not happen. But numerical problems can always occur.
              // In that case we do nothing.
              Debug.Assert(lowerVertex != null, "Could not find lowerVertex.");
              if (lowerVertex == null)
            return;

              Debug.Assert(lowerVertex != upperVertex, "upperVertex and lowerVertex should not be identical.");

            #if DEBUG
              // Get the first visible edge of front face. This edge starts at lowerVertex.
              var firstVisibleEdge = lowerVertex.Edge;
              if (firstVisibleEdge.Face != frontFace)
            firstVisibleEdge = lowerVertex.Edge.Previous.Twin;

              // Check if all edges between lowerVertex and upperVertex are visible.
              var edge = firstVisibleEdge;
              while (edge.Origin != upperVertex)
              {
            float distance;
            DcelMesh.GetCollinearity(point, edge, faceNormal, out distance);
            Debug.Assert(distance >= 0);
            edge = edge.Next;
              }

              // Check if all edges between upperVertex and lowerVertex are not visible.
              while (edge.Origin != lowerVertex)
              {
            float distance;
            DcelMesh.GetCollinearity(point, edge, faceNormal, out distance);
            Debug.Assert(distance < 0);
            edge = edge.Next;
              }
            #endif

              // Create a new vertex for the point.
              DcelVertex newVertex = new DcelVertex(point, null);
              newVertex.Edge = new DcelEdge();

              // Add two new edges between lowerVertex and upperVertex.
              DcelEdge lowerEdge = new DcelEdge();
              DcelEdge lowerEdgeTwin = new DcelEdge();
              DcelEdge upperEdge = newVertex.Edge;
              DcelEdge upperEdgeTwin = new DcelEdge();

              lowerEdge.Origin = lowerVertex;
              lowerEdge.Face = frontFace;
              lowerEdge.Next = upperEdge;
              lowerEdge.Twin = lowerEdgeTwin;

              lowerEdgeTwin.Origin = newVertex;
              lowerEdgeTwin.Face = backFace;
              lowerEdgeTwin.Previous = upperEdgeTwin;
              lowerEdgeTwin.Twin = lowerEdge;

              upperEdge.Origin = newVertex;
              upperEdge.Face = frontFace;
              upperEdge.Previous = lowerEdge;
              upperEdge.Twin = upperEdgeTwin;

              upperEdgeTwin.Origin = upperVertex;
              upperEdgeTwin.Face = backFace;
              upperEdgeTwin.Next = lowerEdgeTwin;
              upperEdgeTwin.Twin = upperEdge;

              lowerEdge.Previous = (lowerVertex.Edge.Face == frontFace) ? lowerVertex.Edge.Previous : lowerVertex.Edge.Twin;
              upperEdge.Next = (upperVertex.Edge.Face == frontFace) ? upperVertex.Edge : upperVertex.Edge.Twin.Next;
              lowerEdgeTwin.Next = lowerEdge.Previous.Twin;
              upperEdgeTwin.Previous = upperEdge.Next.Twin;

              // Correct faces.
              frontFace.Boundary = currentEdge;   // The last currentEdge was not visible, so it will stay.
              backFace.Boundary = currentEdge.Twin;

              // Correct lower and upper vertex.
              lowerVertex.Edge = lowerEdge;
              upperVertex.Edge = upperEdge.Next;

              // Correct edges before lowerEdge and after upperEdge.
              lowerEdge.Previous.Next = lowerEdge;
              lowerEdgeTwin.Next.Previous = lowerEdgeTwin;
              upperEdge.Next.Previous = upperEdge;
              upperEdgeTwin.Previous.Next = upperEdgeTwin;

              // _mesh.Vertex could be one of the removed vertices. --> Set a vertex that is in the hull.
              _mesh.Vertex = lowerVertex;

              _mesh.Dirty = true;

              Debug.Assert(_mesh.IsTwoSidedPolygon(), "DCEL mesh should be a two-sided polygon.");
        }
Example #11
0
        /// <summary>
        /// Merges a point to a convex hull that is a line segment.
        /// </summary>
        /// <param name="point">The point.</param>
        private void GrowLinearConvex(Vector3F point)
        {
            Debug.Assert(_mesh != null);
              Debug.Assert(_mesh.Vertices.Count == 2, "DCEL mesh with 2 vertices was expected.");
              Debug.Assert(_type == ConvexHullType.Linear);
              Debug.Assert(!Vector3F.AreNumericallyEqual(_mesh.Vertex.Position, point));
              Debug.Assert(!Vector3F.AreNumericallyEqual(_mesh.Vertices[1].Position, point));

              // Abort if the point is equal to an existing point.
              if (Vector3F.AreNumericallyEqual(point, _mesh.Vertex.Position)
              || Vector3F.AreNumericallyEqual(point, _mesh.Vertex.Edge.Twin.Origin.Position))
            return;

              var edge = _mesh.Vertex.Edge;
              var collinearity = DcelMesh.GetCollinearity(point, _mesh.Vertex.Edge);
              switch (collinearity)
              {
            case Collinearity.CollinearBefore:     // The point is the new start of the line segment.
              edge.Origin.Position = point;
              _mesh.Dirty = true;
              return;
            case Collinearity.CollinearAfter:      // The point is the new end of the line segment.
              edge.Twin.Origin.Position = point;
              _mesh.Dirty = true;
              return;
            case Collinearity.CollinearContained:  // The point is in the line segment.
              // Point is in convex hull.
              return;
              }

              Debug.Assert(collinearity == Collinearity.NotCollinear
                   || collinearity == Collinearity.NotCollinearBehind
                   || collinearity == Collinearity.NotCollinearInFront);

              // Not Collinear --> Make triangle.
              _type = ConvexHullType.Planar;

              // Create new components.
              DcelVertex v0 = _mesh.Vertex;
              DcelVertex v1 = edge.Twin.Origin;

              DcelVertex v2 = new DcelVertex(point, null);
              DcelEdge e01 = edge;
              DcelEdge e10 = e01.Twin;
              DcelEdge e12 = new DcelEdge();
              DcelEdge e21 = new DcelEdge();
              DcelEdge e02 = new DcelEdge();
              DcelEdge e20 = new DcelEdge();
              DcelFace f012 = new DcelFace();
              DcelFace f210 = new DcelFace();

              // Link with existing components.
              e01.Next = e12; e01.Previous = e20;
              e10.Next = e02; e10.Previous = e21;

              // Link new components.
              v2.Edge = e20;

              e12.Origin = v1;
              e21.Origin = v2;
              e20.Origin = v2;
              e02.Origin = v0;

              e12.Twin = e21;
              e21.Twin = e12;
              e20.Twin = e02;
              e02.Twin = e20;

              e12.Previous = e01;
              e21.Previous = e02;
              e20.Previous = e12;
              e02.Previous = e10;

              e12.Next = e20;
              e21.Next = e10;
              e20.Next = e01;
              e02.Next = e21;

              // Link face.
              f012.Boundary = e01;
              f210.Boundary = e21;
              e01.Face = e12.Face = e20.Face = f012;
              e21.Face = e10.Face = e02.Face = f210;

              // Update _faces.
              _faces.Add(f012);
              _faces.Add(f210);

              _mesh.Dirty = true;

              Debug.Assert(_mesh.Faces.Count == _faces.Count, "Internal error in ConvexHullBuilder.");
              Debug.Assert(_mesh.IsTriangleMesh(), "DCEL mesh should be a triangle mesh.");
              Debug.Assert(_mesh.IsTwoSidedPolygon(), "DCEL mesh should be a two-sided polygon.");
        }
Example #12
0
        /// <summary>
        /// Merges a point to a 3D convex hull.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="startFace">
        /// A face which is visible from the given point. Can be <see langword="null"/>.
        /// </param>
        /// <returns><see langword="true"/> if the convex hull was grown.</returns>
        private bool GrowSpatialConvex(Vector3F point, DcelFace startFace)
        {
            Debug.Assert(_mesh != null);
              Debug.Assert(_mesh.Vertices.Count > 3, "DCEL mesh with more than 3 vertices expected.");
              Debug.Assert(_mesh.Faces.Count > 2, "DCEL mesh with more than 2 face expected.");

              // ----- General convex hull growth.

              // Find a face which is visible from point.
              DcelFace visibleFace = null;

              // TODO: It can happen that startFace is not in _mesh.Faces.
              // That means, _faces might contain more faces than _mesh.Faces!
              //Debug.Assert(startFace == null || _mesh.Faces.Contains(startFace), "Internal error in ConvexHullBuilder.");

              // Test the given face first. It should be visible, but the caller might have
              // made a different collinearity check which is not robust.
              if (startFace != null)
              {
            float distance;
            DcelMesh.GetCollinearity(point, startFace, out distance);
            if (distance > 0)
              visibleFace = startFace;
              }

              // startFace was not robust. Find another visible face.
              if (visibleFace == null)
              {
            int numberOfFaces = _faces.Count;
            for (int i = 0; i < numberOfFaces; i++)
            {
              DcelFace face = _faces[i];

              if (face.Tag < 0) // No need to check faces which are on the final hull.
            continue;

              float distance;
              DcelMesh.GetCollinearity(point, face, out distance);
              if (distance > 0)
              {
            visibleFace = face;
            break;
              }
            }
              }

              if (visibleFace == null)
              {
            // Nothing to do because point is in the hull.
            return false;
              }

              // Check if tags are all <= 0.
              // Temporarily removed because this occurs with the Sponza model:
              //      Debug.Assert(_mesh.Faces.All(f => f.Tag <= 0), "All tags should be <= 0.");

              // ----- Find and remove visible faces.
              // Use of tags:
              // - Face.Tag = -1 ... face on final hull. (Only set by of Create()).
              // - Face.Tag = 0 ... default
              // - Face.Tag = 1 ... face is visible and on stack; this face will be removed from the mesh.
              // - Edge.Tag = 0 ... default
              // - Edge.Tag = 1 ... edge is on boundary of visible area, visible area will be replaced
              //                    so this edge is on the boundary of a temporary hole.
              // - Edge.Tag = 2 ... hole edge that was handled

              _taggedEdges.Clear();

              // Push not handled visible faces on a stack.
              Stack<DcelFace> visibleFaces = new Stack<DcelFace>();
              visibleFaces.Push(visibleFace);
              visibleFace.Tag = 1; // Mark as "on the stack".

              _faces.Remove(visibleFace);

              // We store the hole edge where the neighbor triangle faces away most extreme from point.
              double minInFrontValue = double.PositiveInfinity;
              DcelEdge startEdge = null;

              // Search for all visible faces.
              while (visibleFaces.Count > 0)
              {
            visibleFace = visibleFaces.Pop();

            // Get the 3 triangle edges of the visible face.
            DcelEdge[] edges = new DcelEdge[3];
            edges[0] = visibleFace.Boundary;
            edges[1] = edges[0].Next;
            edges[2] = edges[1].Next;

            // Add visible neighbors to the stack.
            for (int i = 0; i < 3; i++)
            {
              DcelFace neighbor = edges[i].Twin.Face;

              // Maybe neighbor was already removed?
              if (neighbor == null)
            continue;

              // Get a measure for how visible the face is (> 0 means visible).
              float inFrontValue;
              //var collinearity =
              DcelMesh.GetCollinearity(point, neighbor, out inFrontValue);

              // If the point is in front of the face or in the face plane, then we can remove
              // the face.
              if (inFrontValue >= 0)    // Using >= 0 without epsilon is important for numerical stability!
              {
            // Neighbor is visible or collinear. Push to stack (unless it is already on the stack).
            if (neighbor.Tag != 1)
            {
              _faces.Remove(neighbor);
              visibleFaces.Push(neighbor);
              neighbor.Tag = 1;
            }
              }
              else
              {
            if (minInFrontValue > inFrontValue)
            {
              startEdge = edges[i];
              minInFrontValue = inFrontValue;
            }

            // Neighbor is not visible. This edge is part of the boundary of the hole.
            // Set tag to 1 to mark the edge.
            edges[i].Tag = 1;
            _taggedEdges.Add(edges[i]);
              }
            }
              }

              // Temporarily removed because this occurs with the Sponza model:
              //      Debug.Assert(startEdge != null);
              if (startEdge == null)
            throw new GeometryException("Could not generate convex hull.");

              // Create a new vertex for the point.
              DcelVertex newVertex = new DcelVertex(point, null);

              // Traverse the boundary of the hole beginning at startEdge.
              DcelEdge currentEdge = startEdge;
              DcelEdge previousEdge = null;
              do
              {
            // Mark currentEdge as handled.
            currentEdge.Tag = 2;

            // Update the vertex-edge link because the link could point to a removed edge.
            currentEdge.Origin.Edge = currentEdge;

            // The next edge is part of the hole boundary and has not been handled yet.

            // Create new face.
            DcelFace face = new DcelFace();
            face.Boundary = currentEdge;
            currentEdge.Face = face;
            _faces.Add(face);

            // Create 2 half-edges.
            currentEdge.Next = new DcelEdge();
            currentEdge.Previous = new DcelEdge();
            _taggedEdges.Add(currentEdge.Next);
            _taggedEdges.Add(currentEdge.Previous);

            currentEdge.Next.Face = face;
            currentEdge.Next.Origin = currentEdge.Twin.Origin;
            currentEdge.Next.Previous = currentEdge;
            currentEdge.Next.Next = currentEdge.Previous;
            currentEdge.Next.Tag = 2;

            currentEdge.Previous.Face = face;
            currentEdge.Previous.Origin = newVertex;
            currentEdge.Previous.Previous = currentEdge.Next;
            currentEdge.Previous.Next = currentEdge;
            currentEdge.Previous.Tag = 2;

            // Link new face with previous new face.
            if (previousEdge != null)
            {
              previousEdge.Next.Twin = currentEdge.Previous;
              currentEdge.Previous.Twin = previousEdge.Next;
            }

            // Search for next edge that is marked as a hole boundary edge.
            previousEdge = currentEdge;
            currentEdge = currentEdge.Twin.Previous.Twin;
            int i = 0;
            while (currentEdge.Tag == 0)
            {
              currentEdge = currentEdge.Previous.Twin;
              i++;
              if (i == 100)
            throw new GeometryException("Could not generate convex hull.");
            }

              } while (currentEdge.Tag == 1);

              Debug.Assert(previousEdge != null);
              Debug.Assert(currentEdge == startEdge);

              // Link the last new face with the first new face.
              previousEdge.Next.Twin = currentEdge.Previous;
              currentEdge.Previous.Twin = previousEdge.Next;

              // Set the data for the new vertex.
              newVertex.Edge = startEdge.Previous;

              // Set mesh.Vertex to a vertex that is part of the hull.
              _mesh.Vertex = startEdge.Origin;

              // Reset edge tags. No need to reset face tags because marked faces are not
              // part of the mesh anymore.
              int numberOfEdges = _taggedEdges.Count;
              for (int i = 0; i < numberOfEdges; i++)
            _taggedEdges[i].Tag = 0;

              _taggedEdges.Clear();

              _mesh.Dirty = true;

              return true;
        }
Example #13
0
        /// <summary>
        /// Merges a point to a convex hull that is also a point.
        /// </summary>
        /// <param name="point">The point.</param>
        private void GrowPointConvex(Vector3F point)
        {
            Debug.Assert(_mesh != null);
              Debug.Assert(_mesh.Vertices.Count == 1, "DCEL mesh with 1 vertex was expected.");
              Debug.Assert(_type == ConvexHullType.Point);

              // Abort if the point is equal to the existing point in the mesh.
              if (Vector3F.AreNumericallyEqual(_mesh.Vertex.Position, point))
            return;

              DcelVertex newVertex = new DcelVertex(point, null);
              DcelEdge edge = new DcelEdge();
              DcelEdge twin = new DcelEdge();

              // Link everything.
              edge.Origin = _mesh.Vertex;
              twin.Origin = newVertex;
              edge.Twin = twin;
              twin.Twin = edge;
              _mesh.Vertex.Edge = edge;
              newVertex.Edge = twin;

              _mesh.Dirty = true;
              _type = ConvexHullType.Linear;
        }
Example #14
0
        /// <summary>
        /// Removes the given face and connects the face edges to the given point.
        /// </summary>
        /// <param name="face">The face.</param>
        /// <param name="point">The point.</param>
        private void GrowPlanarConvexToSpatial(DcelFace face, Vector3F point)
        {
            _faces.Remove(face);

              // Create a new vertex for the point.
              DcelVertex newVertex = new DcelVertex(point, null);

              // Traverse the boundary of the face.
              DcelEdge currentEdge = face.Boundary;
              DcelEdge previousEdge = null;
              do
              {
            // Create new face.
            DcelFace newface = new DcelFace();
            newface.Boundary = currentEdge;
            currentEdge.Face = newface;

            _faces.Add(newface);

            // Create 2 half-edges.
            currentEdge.Next = new DcelEdge();
            currentEdge.Previous = new DcelEdge();

            currentEdge.Next.Face = newface;
            currentEdge.Next.Origin = currentEdge.Twin.Origin;
            currentEdge.Next.Previous = currentEdge;
            currentEdge.Next.Next = currentEdge.Previous;

            currentEdge.Previous.Face = newface;
            currentEdge.Previous.Origin = newVertex;
            currentEdge.Previous.Previous = currentEdge.Next;
            currentEdge.Previous.Next = currentEdge;

            // Link new face with previous new face.
            if (previousEdge != null)
            {
              previousEdge.Next.Twin = currentEdge.Previous;
              currentEdge.Previous.Twin = previousEdge.Next;
            }

            // Go to next edge.
            previousEdge = currentEdge;
            currentEdge = currentEdge.Twin.Previous.Twin;

            // Go on until no edge points to face anymore.
              } while (currentEdge.Face == face);

              // Link the last new face with the first new face.
              previousEdge.Next.Twin = currentEdge.Previous;
              currentEdge.Previous.Twin = previousEdge.Next;

              Debug.Assert(previousEdge != null);
              Debug.Assert(currentEdge == face.Boundary);
              Debug.Assert(currentEdge.Face != face);

              // Set the data for the new vertex.
              newVertex.Edge = currentEdge.Previous;

              _mesh.Dirty = true;
              _type = ConvexHullType.Spatial;

              Debug.Assert(_mesh.Faces.Count == _faces.Count, "Internal error in ConvexHullBuilder.");
        }