예제 #1
0
        /// <summary>
        /// Determines the collinearity between a point and a face.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="face">The face.</param>
        /// <param name="inFront">
        /// A value that is positive if the point is in front. And the value is proportional to the
        /// distance of the point from the edge.
        /// </param>
        /// <returns>The collinearity type.</returns>
        internal static Collinearity GetCollinearity(Vector3F point, DcelFace face, out float inFront)
        {
            Vector3F v0        = face.Boundary.Origin.Position;
            Vector3F normal    = face.Normal;
            Vector3F v0ToPoint = point - v0;

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

            inFront = dot;

            var normalLengthSquared = normal.LengthSquared;

            //if (normalLengthSquared < Numeric.EpsilonFSquared)
            //{
            //  // Make edge checks.
            //  if (GetCollinearity(point, face.Boundary) == Collinearity.NotCollinear
            //      && GetCollinearity(point, face.Boundary.Next) == Collinearity.NotCollinear)
            //    return Collinearity.NotCollinear;
            //
            //  // Not on the triangle line.
            //  return Collinearity.NotCollinear;
            //}

            if (dot * dot > Numeric.EpsilonF * normalLengthSquared * v0ToPoint.LengthSquared)
            {
                if (dot > 0)
                {
                    return(Collinearity.NotCollinearInFront); // Face is visible from point.
                }
                if (dot < 0)
                {
                    return(Collinearity.NotCollinearBehind); // Face is not visible from point.
                }
            }

            return(Collinearity.Collinear);
        }
예제 #2
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
            });
        }
예제 #3
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 };
        }
예제 #4
0
        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 (Vector3F.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);
        }
예제 #5
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;
        }
예제 #6
0
        // Checks if the face has a boundary and if all boundary edges are set to this face.
        private bool IsValid(DcelFace face, out string errorDescription)
        {
            // TODO: Do we need anything special for holes?

              Debug.Assert(face != null);

              var boundary = face.Boundary;
              if (boundary == null)
              {
            errorDescription = "Face.Boundary is null.";
            return false;
              }

              if (boundary.Face != face)
              {
            errorDescription = "A boundary edge of a face links to another face.";
            return false;
              }

              if (boundary.Next == boundary)
              {
            errorDescription = "Edge.Next links to itself.";
            return false;
              }

              try
              {
            // Check if boundary is closed and if all edges are linked to the face.
            boundary.InternalTag = 1;
            var edge = boundary.Next;
            while (edge != boundary && (edge == null || edge.InternalTag != 1))
            {
              if (edge == null)
              {
            errorDescription = "Edge.Next is not set for a face boundary edge.";
            return false;
              }

              if (edge.Face != face)
              {
            errorDescription = "A boundary edge of a face links to another face.";
            return false;
              }

              edge.InternalTag = 1;
              edge = edge.Next;
            }

            if (edge != boundary)
            {
              errorDescription = "Face has stray boundary edges.";
              return false;
            }
              }
              finally
              {
            ResetInternalTags();
              }

              errorDescription = null;
              return true;
        }
예제 #7
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);
        }
예제 #8
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;
        }
예제 #9
0
 /// <summary>
 /// Determines the collinearity between a point and a face.
 /// </summary>
 /// <param name="point">The point.</param>
 /// <param name="face">The face.</param>
 /// <returns>The collinearity type.</returns>
 internal static Collinearity GetCollinearity(Vector3F point, DcelFace face)
 {
     float dummy;
       return GetCollinearity(point, face, out dummy);
 }
예제 #10
0
        /// <summary>
        /// Determines the collinearity between a point and a face.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="face">The face.</param>
        /// <param name="inFront">
        /// A value that is positive if the point is in front. And the value is proportional to the
        /// distance of the point from the edge.
        /// </param>
        /// <returns>The collinearity type.</returns>
        internal static Collinearity GetCollinearity(Vector3F point, DcelFace face, out float inFront)
        {
            Vector3F v0 = face.Boundary.Origin.Position;
              Vector3F normal = face.Normal;
              Vector3F v0ToPoint = point - v0;

              float dot = Vector3F.Dot(v0ToPoint, normal);
              inFront = dot;

              var normalLengthSquared = normal.LengthSquared;
              //if (normalLengthSquared < Numeric.EpsilonFSquared)
              //{
              //  // Make edge checks.
              //  if (GetCollinearity(point, face.Boundary) == Collinearity.NotCollinear
              //      && GetCollinearity(point, face.Boundary.Next) == Collinearity.NotCollinear)
              //    return Collinearity.NotCollinear;
              //
              //  // Not on the triangle line.
              //  return Collinearity.NotCollinear;
              //}

              if (dot * dot > Numeric.EpsilonF * normalLengthSquared * v0ToPoint.LengthSquared)
              {
            if (dot > 0)
              return Collinearity.NotCollinearInFront;  // Face is visible from point.
            if (dot < 0)
              return Collinearity.NotCollinearBehind;   // Face is not visible from point.
              }

              return Collinearity.Collinear;
        }
예제 #11
0
        // Checks if the face has a boundary and if all boundary edges are set to this face.
        private bool IsValid(DcelFace face, out string errorDescription)
        {
            // TODO: Do we need anything special for holes?

            Debug.Assert(face != null);

            var boundary = face.Boundary;

            if (boundary == null)
            {
                errorDescription = "Face.Boundary is null.";
                return(false);
            }

            if (boundary.Face != face)
            {
                errorDescription = "A boundary edge of a face links to another face.";
                return(false);
            }

            if (boundary.Next == boundary)
            {
                errorDescription = "Edge.Next links to itself.";
                return(false);
            }

            try
            {
                // Check if boundary is closed and if all edges are linked to the face.
                boundary.InternalTag = 1;
                var edge = boundary.Next;
                while (edge != boundary && (edge == null || edge.InternalTag != 1))
                {
                    if (edge == null)
                    {
                        errorDescription = "Edge.Next is not set for a face boundary edge.";
                        return(false);
                    }

                    if (edge.Face != face)
                    {
                        errorDescription = "A boundary edge of a face links to another face.";
                        return(false);
                    }

                    edge.InternalTag = 1;
                    edge             = edge.Next;
                }

                if (edge != boundary)
                {
                    errorDescription = "Face has stray boundary edges.";
                    return(false);
                }
            }
            finally
            {
                ResetInternalTags();
            }

            errorDescription = null;
            return(true);
        }
예제 #12
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.");
        }
예제 #13
0
        /// <summary>
        /// Grows the mesh so that it includes the given point.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="face">
        /// A face which is visible from the given point. Can be <see langword="null"/>.
        /// </param>
        /// <remarks>
        /// <para>
        /// This method assumes that this DCEL mesh represents a convex, closed mesh. The convex mesh
        /// is grown so that it includes the given <paramref name="point"/>. The final mesh will
        /// contain the original mesh.
        /// </para>
        /// </remarks>
        private void GrowConvex(Vector3F point, DcelFace face)
        {
            // Depending on the current convex hull type we have to use different growing methods.
              switch (_type)
              {
            case ConvexHullType.Empty:
              GrowEmptyConvex(point);
              break;
            case ConvexHullType.Point:
              GrowPointConvex(point);
              break;
            case ConvexHullType.Linear:
              GrowLinearConvex(point);
              break;
            case ConvexHullType.Planar:
              GrowPlanarConvex(point);
              break;
            default:
              Debug.Assert(_type == ConvexHullType.Spatial);
              //bool merged =
              GrowSpatialConvex(point, face);

              // Degenerate face removal is currently disabled because it is too slow for large
              // input sets.
              //// Check for degenerate faces
              //if (merged)
              //{
              //  bool degenerate = false;
              //  foreach (var face in _faces)
              //  {
              //    if (face.Normal.LengthSquared < Numeric.EpsilonF * Numeric.EpsilonF)
              //    {
              //      degenerate = true;
              //      break;
              //    }
              //  }
              //  if (degenerate)
              //  {
              //    // Remove degenerate faces.
              //    // Since there are no duplicate vertices. This method will only rearrange faces
              //    // it will not add faces.
              //    RemoveDegenerateTriangles();
              //  }
              //}
              break;
              }

            #if DEBUG
              // These tests might be too slow for some unit tests
              // (e.g. DigitalRune.Physics.Tests.MassTest.ScaledConvexMass()).
              //Debug.Assert(_mesh.IsValid(), "DCEL mesh is not valid.");
              //Debug.Assert(_mesh.IsConvex(), "DCEL mesh is not convex.");
              //Debug.Assert(_mesh.Contains(point, Numeric.EpsilonF * 10), "Point is outside convex hull.");
              //Debug.Assert(_type != ConvexHullType.Spatial || _mesh.IsTriangleMesh(), "Convex hull should be a triangle mesh but isn't.");
              //Debug.Assert(_mesh.Faces.All(f => f.Tag <= 0), "DCEL mesh tags are not reset.");
            #endif
        }
예제 #14
0
        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();
        }
예제 #15
0
        /// <summary>
        /// Determines the collinearity between a point and a face.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="face">The face.</param>
        /// <returns>The collinearity type.</returns>
        internal static Collinearity GetCollinearity(Vector3F point, DcelFace face)
        {
            float dummy;

            return(GetCollinearity(point, face, out dummy));
        }
예제 #16
0
        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 (Vector3F.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;
        }
예제 #17
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.");
        }