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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            todoStack.Push(edge);

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

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

                // Tag edge.
                edge.Tag = tag;

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

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

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

                // Follow connected edges.
                AddUntaggedEdgeToStack(edge.Next, todoStack, 1);
                AddUntaggedEdgeToStack(edge.Previous, todoStack, 1);
                AddUntaggedEdgeToStack(edge.Twin, todoStack, 1);
            }
        }
        /// <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();
        }
Exemple #7
0
        // Links to half-edges via Twin links if they are on the same edge (same vertices).
        private static void TryLink(DcelEdge edge0, DcelEdge edge1)
        {
            var start0 = edge0.Origin;
            var end0   = edge0.Next.Origin;

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

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

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

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

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

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

            return(cutPosition);
        }
Exemple #9
0
        /// <summary>
        /// Determines the collinearity between a point and an edge of a face.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="edge">The edge.</param>
        /// <param name="faceNormal">The face normal (of the face of the edge).</param>
        /// <param name="inFront">
        /// A value that is positive if the point is in front (outside of the face).
        /// And the value is proportional to the distance of the point from the edge.
        /// </param>
        /// <returns>The collinearity type.</returns>
        internal static Collinearity GetCollinearity(Vector3F point, DcelEdge edge, Vector3F faceNormal, out float inFront)
        {
            Vector3F v0      = edge.Origin.Position;
            Vector3F v1      = edge.Twin.Origin.Position;
            Vector3F segment = v1 - v0;
            float    segmentLengthSquared = segment.LengthSquared;

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

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

            Vector3F v0ToPoint = point - v0;
            float    v0ToPointLengthSquared = v0ToPoint.LengthSquared;

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

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

            dot = Vector3F.Dot(segment, v0ToPoint);

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

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

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

              Stack<DcelEdge> todoStack = new Stack<DcelEdge>();
              todoStack.Push(edge);

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

            if (edge.Tag == tag)
              continue;

            // Tag edge.
            edge.Tag = tag;

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

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

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

            // Follow connected edges.
            AddUntaggedEdgeToStack(edge.Next, todoStack, 1);
            AddUntaggedEdgeToStack(edge.Previous, todoStack, 1);
            AddUntaggedEdgeToStack(edge.Twin, todoStack, 1);
              }
        }
Exemple #11
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);
        }
Exemple #12
0
 // Same as AddUntaggedEdgeToStack but with InternalTag
 private static void AddUntaggedEdgeToStackInternal(DcelEdge edge, Stack<DcelEdge> stack, int internalTag)
 {
     Debug.Assert(stack != null);
       if (edge != null && edge.InternalTag != internalTag)
     stack.Push(edge);
 }
Exemple #13
0
        // Links to half-edges via Twin links if they are on the same edge (same vertices).
        private static void TryLink(DcelEdge edge0, DcelEdge edge1)
        {
            var start0 = edge0.Origin;
              var end0 = edge0.Next.Origin;

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

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

              if (start0 == end1 && end0 == start1)
              {
            edge0.Twin = edge1;
            edge1.Twin = edge0;
              }
        }
        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();
        }
Exemple #15
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DcelVertex"/> class with a given position and
 /// edge.
 /// </summary>
 /// <param name="position">The position.</param>
 /// <param name="edge">The edge.</param>
 public DcelVertex(Vector3F position, DcelEdge edge)
 {
     Position = position;
       Edge = edge;
 }
Exemple #16
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);
        }
Exemple #17
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.");
        }
Exemple #18
0
        /// <summary>
        /// Determines the collinearity between a point and an edge of a face.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="edge">The edge.</param>
        /// <param name="faceNormal">The face normal (of the face of the edge).</param>
        /// <param name="inFront">
        /// A value that is positive if the point is in front (outside of the face). 
        /// And the value is proportional to the distance of the point from the edge.
        /// </param>
        /// <returns>The collinearity type.</returns>
        internal static Collinearity GetCollinearity(Vector3F point, DcelEdge edge, Vector3F faceNormal, out float inFront)
        {
            Vector3F v0 = edge.Origin.Position;
              Vector3F v1 = edge.Twin.Origin.Position;
              Vector3F segment = v1 - v0;
              float segmentLengthSquared = segment.LengthSquared;

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

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

              Vector3F v0ToPoint = point - v0;
              float v0ToPointLengthSquared = v0ToPoint.LengthSquared;

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

              dot = Vector3F.Dot(segment, v0ToPoint);

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

              return Collinearity.CollinearContained;
        }
Exemple #19
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 };
        }
        /// <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
            });
        }
Exemple #21
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DcelFace"/> class from a given edge.
 /// </summary>
 /// <param name="boundary">An edge of the outer boundary.</param>
 public DcelFace(DcelEdge boundary)
 {
     Boundary = boundary;
 }
Exemple #22
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DcelFace"/> class from a given edge.
 /// </summary>
 /// <param name="boundary">An edge of the outer boundary.</param>
 public DcelFace(DcelEdge boundary)
 {
     Boundary = boundary;
 }
Exemple #23
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;
        }
Exemple #24
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;
        }
Exemple #25
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.");
        }
Exemple #26
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DcelVertex"/> class with a given position and
 /// edge.
 /// </summary>
 /// <param name="position">The position.</param>
 /// <param name="edge">The edge.</param>
 public DcelVertex(Vector3F position, DcelEdge edge)
 {
     Position = position;
     Edge     = edge;
 }
Exemple #27
0
        // edge vertices must have the plane distance in DcelVertex.UserData. This method computes
        // where the edge is split.
        private static Vector3F GetCutPosition(DcelEdge edge)
        {
            var startVertex = edge.Origin;
              var startDistance = (float)startVertex.UserData;

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

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

              // Get position where edge is cut.
              var cutPosition = startVertex.Position * (1 - parameter) + endVertex.Position * parameter;
              return cutPosition;
        }
Exemple #28
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;
        }
Exemple #29
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;
        }