コード例 #1
0
ファイル: Cone.cs プロジェクト: opensourcer2/TVGL
        /// <summary>
        ///     Checks if face should be added to cone
        /// </summary>
        /// <param name="face">The face.</param>
        /// <returns><c>true</c> if [is new member of] [the specified face]; otherwise, <c>false</c>.</returns>
        /// <exception cref="NotImplementedException"></exception>
        public override bool IsNewMemberOf(PolygonalFace face)
        {
            return(false);

            // todo
            throw new NotImplementedException();
        }
コード例 #2
0
        private static bool FaceShouldBeOwnedFace(Edge edge, PolygonalFace face)
        {
            var otherEdgeVector = face.OtherVertex(edge.From, edge.To).Position.subtract(edge.To.Position, 3);
            var isThisNormal    = edge.Vector.crossProduct(otherEdgeVector);

            return(face.Normal.dotProduct(isThisNormal, 3) > 0);
        }
コード例 #3
0
        /// <summary>
        ///     Updates the with.
        /// </summary>
        /// <param name="face">The face.</param>
        public override void UpdateWith(PolygonalFace face)
        {
            var numFaces = Faces.Count;

            double[] inBetweenPoint;
            var      distance = MiscFunctions.SkewedLineIntersection(face.Center, face.Normal, Anchor, Axis,
                                                                     out inBetweenPoint);
            var fractionToMove = 1 / numFaces;
            var moveVector     = Anchor.crossProduct(face.Normal);

            if (moveVector.dotProduct(face.Center.subtract(inBetweenPoint, 3)) < 0)
            {
                moveVector = moveVector.multiply(-1);
            }
            moveVector.normalizeInPlace(3);
            /**** set new Anchor (by averaging in with last n values) ****/
            Anchor =
                Anchor.add(new[]
            {
                moveVector[0] * fractionToMove * distance, moveVector[1] * fractionToMove * distance,
                moveVector[2] * fractionToMove * distance
            }, 3);

            /* to adjust the Axis, we will average the cross products of the new face with all the old faces */
            var totalAxis = new double[3];

            for (var i = 0; i < numFaces; i++)
            {
                var newAxis = face.Normal.crossProduct(Faces[i].Normal);
                if (newAxis.dotProduct(Axis, 3) < 0)
                {
                    newAxis.multiply(-1);
                }
                totalAxis = totalAxis.add(newAxis, 3);
            }
            var numPrevCrossProducts = numFaces * (numFaces - 1) / 2;

            totalAxis = totalAxis.add(Axis.multiply(numPrevCrossProducts), 3);
            /**** set new Axis (by averaging in with last n values) ****/
            Axis = totalAxis.divide(numFaces + numPrevCrossProducts).normalize(3);
            foreach (var v in face.Vertices)
            {
                if (!Vertices.Contains(v))
                {
                    Vertices.Add(v);
                }
            }
            var totalOfRadii = Vertices.Sum(v => MiscFunctions.DistancePointToLine(v.Position, Anchor, Axis));

            /**** set new Radius (by averaging in with last n values) ****/
            Radius = totalOfRadii / Vertices.Count;
            base.UpdateWith(face);
        }
コード例 #4
0
        /// <summary>
        ///     Updates the with.
        /// </summary>
        /// <param name="face">The face.</param>
        public override void UpdateWith(PolygonalFace face)
        {
            var numFaces = Faces.Count;
            var distance = MiscFunctions.SkewedLineIntersection(face.Center, face.Normal, Anchor, Axis,
                                                                out var inBetweenPoint);
            var fractionToMove = 1 / numFaces;
            var moveVector     = Anchor.Cross(face.Normal);

            if (moveVector.Dot(face.Center.Subtract(inBetweenPoint)) < 0)
            {
                moveVector = moveVector * -1;
            }
            moveVector = moveVector.Normalize();
            /**** set new Anchor (by averaging in with last n values) ****/
            Anchor =
                Anchor + new Vector3(
                    moveVector.X * fractionToMove * distance, moveVector.Y * fractionToMove * distance,
                    moveVector.Z * fractionToMove * distance
                    );

            /* to adjust the Axis, we will average the cross products of the new face with all the old faces */
            var totalAxis = new Vector3();

            foreach (var oldFace in Faces)
            {
                var newAxis = face.Normal.Cross(oldFace.Normal);
                if (newAxis.Dot(Axis) < 0)
                {
                    newAxis = -1 * newAxis;
                }
                totalAxis = totalAxis + newAxis;
            }
            var numPrevCrossProducts = numFaces * (numFaces - 1) / 2;

            totalAxis = totalAxis + (Axis * numPrevCrossProducts);
            /**** set new Axis (by averaging in with last n values) ****/
            Axis = totalAxis.Normalize();
            foreach (var v in face.Vertices)
            {
                if (!Vertices.Contains(v))
                {
                    Vertices.Add(v);
                }
            }
            var totalOfRadii = Vertices.Sum(v => MiscFunctions.DistancePointToLine(v.Coordinates, Anchor, Axis));

            /**** set new Radius (by averaging in with last n values) ****/
            Radius = totalOfRadii / Vertices.Count;
            base.UpdateWith(face);
        }
コード例 #5
0
ファイル: TVGLConvexHull.cs プロジェクト: opensourcer2/TVGL
        internal TVGLConvexHull(IList <Vertex> allVertices, IList <Vertex> convexHullPoints,
                                IList <int> convexHullFaceIndices, double[] center = null, double volume = double.NaN,
                                double surfaceArea = double.NaN)
        {
            Vertices = convexHullPoints.ToArray();
            var numCvxHullFaces = convexHullFaceIndices.Count / 3;

            Faces = new PolygonalFace[numCvxHullFaces];
            var checkSumMultipliers = new long[3];

            for (var i = 0; i < 3; i++)
            {
                checkSumMultipliers[i] = (long)Math.Pow(Constants.CubeRootOfLongMaxValue, i);
            }
            var alreadyCreatedFaces = new HashSet <long>();

            for (int i = 0; i < numCvxHullFaces; i++)
            {
                var orderedIndices = new List <int>
                {
                    convexHullFaceIndices[3 * i], convexHullFaceIndices[3 * i + 1], convexHullFaceIndices[3 * i + 2]
                };
                orderedIndices.Sort();
                var checksum = orderedIndices.Select((t, j) => t * checkSumMultipliers[j]).Sum();
                if (alreadyCreatedFaces.Contains(checksum))
                {
                    continue;
                }
                alreadyCreatedFaces.Add(checksum);
                var faceVertices = new[]
                {
                    allVertices[convexHullFaceIndices[3 * i]],
                    allVertices[convexHullFaceIndices[3 * i + 1]],
                    allVertices[convexHullFaceIndices[3 * i + 2]],
                };
                Faces[i] = new PolygonalFace(faceVertices, false);
            }
            Edges = MakeEdges(Faces, Vertices);
            if (center == null || double.IsNaN(volume) || double.IsNaN(surfaceArea))
            {
                TessellatedSolid.DefineCenterVolumeAndSurfaceArea(Faces, out Center, out Volume, out SurfaceArea);
            }
            else
            {
                Center      = center;
                Volume      = volume;
                SurfaceArea = surfaceArea;
            }
        }
コード例 #6
0
ファイル: Flat.cs プロジェクト: opensourcer2/TVGL
        /// <summary>
        /// Updates the with.
        /// </summary>
        /// <param name="face">The face.</param>
        public override void UpdateWith(PolygonalFace face)
        {
            Normal = Normal.multiply(Faces.Count).add(face.Normal, 3).divide(Faces.Count + 1);
            Normal.normalizeInPlace();
            var newVerts           = new List <Vertex>();
            var newDistanceToPlane = 0.0;

            foreach (var v in face.Vertices.Where(v => !Vertices.Contains(v)))
            {
                newVerts.Add(v);
                newDistanceToPlane += v.Position.dotProduct(Normal, 3);
            }
            DistanceToOrigin = (Vertices.Count * DistanceToOrigin + newDistanceToPlane) / (Vertices.Count + newVerts.Count);
            base.UpdateWith(face);
        }
コード例 #7
0
 /// <summary>
 ///     Stores the face with negligible area.
 /// </summary>
 /// <param name="ts">The ts.</param>
 /// <param name="face">The face.</param>
 private static void StoreFaceWithNegligibleArea(TessellatedSolid ts, PolygonalFace face)
 {
     //This is not truly an error, to don't change the NoErrors boolean.
     if (ts.Errors.FacesWithNegligibleArea == null)
     {
         ts.Errors.FacesWithNegligibleArea = new List <PolygonalFace> {
             face
         }
     }
     ;
     else if (!ts.Errors.FacesWithNegligibleArea.Contains(face))
     {
         ts.Errors.FacesWithNegligibleArea.Add(face);
     }
 }
コード例 #8
0
 /// <summary>
 ///     Stores the face with one vertex.
 /// </summary>
 /// <param name="ts">The ts.</param>
 /// <param name="face">The face.</param>
 private static void StoreFaceWithOneVertex(TessellatedSolid ts, PolygonalFace face)
 {
     ts.Errors.NoErrors = false;
     if (ts.Errors.FacesWithOneVertex == null)
     {
         ts.Errors.FacesWithOneVertex = new List <PolygonalFace> {
             face
         }
     }
     ;
     else
     {
         ts.Errors.FacesWithOneVertex.Add(face);
     }
 }
コード例 #9
0
        private static PolygonLight GetPolygonFromFace(PolygonalFace face, Dictionary <int, PointLight> projectedPoints, bool forceToBePositive)
        {
            if (face.Vertices.Count != 3)
            {
                throw new Exception("This method was only developed with triangles in mind.");
            }
            //Make sure the polygon is ordered correctly (we already know this face is positive)
            var points      = face.Vertices.Select(v => projectedPoints[v.IndexInList]).ToList();
            var facePolygon = new PolygonLight(points);

            if (forceToBePositive && facePolygon.Area < 0)
            {
                facePolygon = PolygonLight.Reverse(facePolygon);
            }
            return(facePolygon);
        }
コード例 #10
0
 /// <summary>
 ///     Stores the edge does not link back to face.
 /// </summary>
 /// <param name="ts">The ts.</param>
 /// <param name="face">The face.</param>
 /// <param name="edge">The edge.</param>
 private static void StoreEdgeDoesNotLinkBackToFace(TessellatedSolid ts, PolygonalFace face, Edge edge)
 {
     ts.Errors.NoErrors = false;
     if (ts.Errors.EdgesThatDoNotLinkBackToFace == null)
     {
         ts.Errors.EdgesThatDoNotLinkBackToFace
             = new List <(PolygonalFace, Edge)> {
             (face, edge)
             }
     }
     ;
     else
     {
         ts.Errors.EdgesThatDoNotLinkBackToFace.Add((face, edge));
     }
 }
コード例 #11
0
 /// <summary>
 ///     Stores the vertex does not link back to face.
 /// </summary>
 /// <param name="ts">The ts.</param>
 /// <param name="face">The face.</param>
 /// <param name="vertex">The vertex.</param>
 private static void StoreVertexDoesNotLinkBackToFace(TessellatedSolid ts, PolygonalFace face, Vertex vertex)
 {
     ts.Errors.NoErrors = false;
     if (ts.Errors.VertsThatDoNotLinkBackToFace == null)
     {
         ts.Errors.VertsThatDoNotLinkBackToFace
             = new List <(PolygonalFace, Vertex)> {
             (face, vertex)
             }
     }
     ;
     else
     {
         ts.Errors.VertsThatDoNotLinkBackToFace.Add((face, vertex));
     }
 }
コード例 #12
0
ファイル: Edge.cs プロジェクト: mattMedemaLabs/TVGL
 /// <summary>
 ///     Initializes a new instance of the <see cref="Edge" /> class.
 /// </summary>
 /// <param name="fromVertex">From vertex.</param>
 /// <param name="toVertex">To vertex.</param>
 /// <param name="ownedFace">The face.</param>
 /// <param name="otherFace">The other face.</param>
 /// <param name="doublyLinkedVertices">if set to <c>true</c> [doubly linked vertices].</param>
 /// <param name="edgeReference">The edge reference.</param>
 /// <exception cref="Exception"></exception>
 public Edge(Vertex fromVertex, Vertex toVertex, PolygonalFace ownedFace, PolygonalFace otherFace,
             bool doublyLinkedVertices, long edgeReference = 0) : this(fromVertex, toVertex, doublyLinkedVertices)
 {
     if (edgeReference > 0)
     {
         EdgeReference = edgeReference;
     }
     else
     {
         TessellatedSolid.SetAndGetEdgeChecksum(this);
     }
     _ownedFace = ownedFace;
     _otherFace = otherFace;
     ownedFace?.AddEdge(this);
     otherFace?.AddEdge(this);
 }
コード例 #13
0
ファイル: Sphere.cs プロジェクト: mattMedemaLabs/TVGL
        /// <summary>
        ///     Adds face to sphere
        /// </summary>
        /// <param name="face">The face.</param>
        public override void UpdateWith(PolygonalFace face)
        {
            var distance       = MiscFunctions.DistancePointToLine(Center, face.Center, face.Normal, out var pointOnLine);
            var fractionToMove = 1 / Faces.Count;
            var moveVector     = pointOnLine.Subtract(Center);

            Center =
                Center + new Vector3(
                    moveVector.X * fractionToMove * distance, moveVector.Y * fractionToMove * distance,
                    moveVector.Z * fractionToMove * distance
                    );


            var totalOfRadii = Vertices.Sum(v => Vector3.Distance(Center, v.Coordinates));

            Radius = totalOfRadii / Vertices.Count;
            base.UpdateWith(face);
        }
コード例 #14
0
ファイル: Plane.cs プロジェクト: mattMedemaLabs/TVGL
 /// <summary>
 /// Determines whether [is new member of] [the specified face].
 /// </summary>
 /// <param name="face">The face.</param>
 /// <returns><c>true</c> if [is new member of] [the specified face]; otherwise, <c>false</c>.</returns>
 public override bool IsNewMemberOf(PolygonalFace face)
 {
     if (Tolerance.IsPracticallySame(0.0))
     {
         Tolerance = Constants.ErrorForFaceInSurface;
     }
     if (Faces.Contains(face))
     {
         return(false);
     }
     if (!face.Normal.Dot(Normal).IsPracticallySame(1.0, Tolerance))
     {
         return(false);
     }
     //Return true if all the vertices are within the tolerance
     //Note that the Dot term and distance to origin, must have the same sign,
     //so there is no additional need moth absolute value methods.
     return(face.Vertices.All(v => Normal.Dot(v.Coordinates).IsPracticallySame(DistanceToOrigin, Tolerance)));
 }
コード例 #15
0
        /// <summary>
        ///     Adjusts the position of kept vertex experimental.
        /// </summary>
        /// <param name="keepVertex">The keep vertex.</param>
        /// <param name="removedVertex">The removed vertex.</param>
        /// <param name="removeFace1">The remove face1.</param>
        /// <param name="removeFace2">The remove face2.</param>
        internal static void AdjustPositionOfKeptVertexExperimental(Vertex keepVertex, Vertex removedVertex,
                                                                    PolygonalFace removeFace1, PolygonalFace removeFace2)
        {
            //average positions
            var newPosition = keepVertex.Coordinates + removedVertex.Coordinates;
            var radius      = keepVertex.Coordinates.Distance(removedVertex.Coordinates) / 2.0;

            keepVertex.Coordinates = newPosition.Divide(2);
            var avgNormal = (removeFace1.Normal + removeFace2.Normal).Normalize();
            var otherVertexAvgDistanceToEdgePlane =
                keepVertex.Edges.Select(e => e.OtherVertex(keepVertex).Coordinates.Dot(avgNormal)).Sum() /
                (keepVertex.Edges.Count - 1);
            var distanceOfEdgePlane = keepVertex.Coordinates.Dot(avgNormal);

            // use a sigmoid function to determine how far out to move the vertex
            var x      = 0.05 * (distanceOfEdgePlane - otherVertexAvgDistanceToEdgePlane) / radius;
            var length = 2 * radius * x / Math.Sqrt(1 + x * x) - radius;

            keepVertex.Coordinates = keepVertex.Coordinates + (avgNormal * length);
        }
コード例 #16
0
        /// <summary>
        ///     Adjusts the position of kept vertex experimental.
        /// </summary>
        /// <param name="keepVertex">The keep vertex.</param>
        /// <param name="removedVertex">The removed vertex.</param>
        /// <param name="removeFace1">The remove face1.</param>
        /// <param name="removeFace2">The remove face2.</param>
        internal static void AdjustPositionOfKeptVertexExperimental(Vertex keepVertex, Vertex removedVertex,
                                                                    PolygonalFace removeFace1, PolygonalFace removeFace2)
        {
            //average positions
            var newPosition = keepVertex.Position.add(removedVertex.Position, 3);
            var radius      = keepVertex.Position.subtract(removedVertex.Position, 3).norm2() / 2.0;

            keepVertex.Position = newPosition.divide(2);
            var avgNormal = removeFace1.Normal.add(removeFace2.Normal, 3).normalize(3);
            var otherVertexAvgDistanceToEdgePlane =
                keepVertex.Edges.Select(e => e.OtherVertex(keepVertex).Position.dotProduct(avgNormal, 3)).Sum() /
                (keepVertex.Edges.Count - 1);
            var distanceOfEdgePlane = keepVertex.Position.dotProduct(avgNormal, 3);

            // use a sigmoid function to determine how far out to move the vertex
            var x      = 0.05 * (distanceOfEdgePlane - otherVertexAvgDistanceToEdgePlane) / radius;
            var length = 2 * radius * x / Math.Sqrt(1 + x * x) - radius;

            keepVertex.Position = keepVertex.Position.add(avgNormal.multiply(length), 3);
        }
コード例 #17
0
 /// <summary>
 ///     Determines whether [is new member of] [the specified face].
 /// </summary>
 /// <param name="face">The face.</param>
 /// <returns><c>true</c> if [is new member of] [the specified face]; otherwise, <c>false</c>.</returns>
 public override bool IsNewMemberOf(PolygonalFace face)
 {
     if (Faces.Contains(face))
     {
         return(false);
     }
     if (Math.Abs(face.Normal.dotProduct(Axis, 3)) > Constants.ErrorForFaceInSurface)
     {
         return(false);
     }
     foreach (var v in face.Vertices)
     {
         if (Math.Abs(MiscFunctions.DistancePointToLine(v.Position, Anchor, Axis) - Radius) >
             Constants.ErrorForFaceInSurface * Radius)
         {
             return(false);
         }
     }
     return(true);
 }
コード例 #18
0
ファイル: Sphere.cs プロジェクト: opensourcer2/TVGL
        /// <summary>
        ///     Adds face to sphere
        /// </summary>
        /// <param name="face">The face.</param>
        public override void UpdateWith(PolygonalFace face)
        {
            double[] pointOnLine;
            var      distance       = MiscFunctions.DistancePointToLine(Center, face.Center, face.Normal, out pointOnLine);
            var      fractionToMove = 1 / Faces.Count;
            var      moveVector     = pointOnLine.subtract(Center, 3);

            Center =
                Center.add(new[]
            {
                moveVector[0] * fractionToMove * distance, moveVector[1] * fractionToMove * distance,
                moveVector[2] * fractionToMove * distance
            }, 3);


            var totalOfRadii = Vertices.Sum(v => MiscFunctions.DistancePointToPoint(Center, v.Position));

            Radius = totalOfRadii / Vertices.Count;
            base.UpdateWith(face);
        }
コード例 #19
0
ファイル: PrimitiveSurface.cs プロジェクト: secondmover/TVGL
 /// <summary>
 ///     Updates surface by adding face
 /// </summary>
 /// <param name="face">The face.</param>
 public virtual void UpdateWith(PolygonalFace face)
 {
     Area += face.Area;
     foreach (var v in face.Vertices.Where(v => !Vertices.Contains(v)))
     {
         Vertices.Add(v);
     }
     foreach (var e in face.Edges.Where(e => !InnerEdges.Contains(e)))
     {
         if (_outerEdges.Contains(e))
         {
             _outerEdges.Remove(e);
             _innerEdges.Add(e);
         }
         else
         {
             _outerEdges.Add(e);
         }
     }
     Faces.Add(face);
 }
コード例 #20
0
ファイル: Sphere.cs プロジェクト: opensourcer2/TVGL
 /// <summary>
 ///     Checks if the face is a member of the sphere
 /// </summary>
 /// <param name="face">The face.</param>
 /// <returns>Boolean.</returns>
 public override bool IsNewMemberOf(PolygonalFace face)
 {
     if (Faces.Contains(face))
     {
         return(false);
     }
     if (Math.Abs(face.Normal.dotProduct(face.Center.subtract(Center, 3)) - 1) >
         Constants.ErrorForFaceInSurface)
     {
         return(false);
     }
     foreach (var v in face.Vertices)
     {
         if (Math.Abs(MiscFunctions.DistancePointToPoint(v.Position, Center) - Radius) >
             Constants.ErrorForFaceInSurface * Radius)
         {
             return(false);
         }
     }
     return(true);
 }
コード例 #21
0
ファイル: Sphere.cs プロジェクト: mattMedemaLabs/TVGL
 /// <summary>
 ///     Checks if the face is a member of the sphere
 /// </summary>
 /// <param name="face">The face.</param>
 /// <returns>Boolean.</returns>
 public override bool IsNewMemberOf(PolygonalFace face)
 {
     if (Faces.Contains(face))
     {
         return(false);
     }
     if (Math.Abs(face.Normal.Dot(face.Center - Center) - 1) >
         Constants.ErrorForFaceInSurface)
     {
         return(false);
     }
     foreach (var v in face.Vertices)
     {
         if (Math.Abs(v.Coordinates.Distance(Center) - Radius) >
             Constants.ErrorForFaceInSurface * Radius)
         {
             return(false);
         }
     }
     return(true);
 }
コード例 #22
0
ファイル: TVGLConvexHull.cs プロジェクト: mattMedemaLabs/TVGL
        internal TVGLConvexHull(IList <Vertex> allVertices, IList <Vertex> convexHullPoints,
                                IList <int> convexHullFaceIndices, double tolerance)
        {
            Vertices = convexHullPoints.ToArray();
            var numCvxHullFaces = convexHullFaceIndices.Count / 3;

            Faces = new PolygonalFace[numCvxHullFaces];
            var checkSumMultipliers = new long[3];

            for (var i = 0; i < 3; i++)
            {
                checkSumMultipliers[i] = (long)Math.Pow(Constants.CubeRootOfLongMaxValue, i);
            }
            var alreadyCreatedFaces = new HashSet <long>();

            for (int i = 0; i < numCvxHullFaces; i++)
            {
                var orderedIndices = new List <int>
                {
                    convexHullFaceIndices[3 * i], convexHullFaceIndices[3 * i + 1], convexHullFaceIndices[3 * i + 2]
                };
                orderedIndices.Sort();
                var checksum = orderedIndices.Select((t, j) => t * checkSumMultipliers[j]).Sum();
                if (alreadyCreatedFaces.Contains(checksum))
                {
                    continue;
                }
                alreadyCreatedFaces.Add(checksum);
                var faceVertices = new[]
                {
                    allVertices[convexHullFaceIndices[3 * i]],
                    allVertices[convexHullFaceIndices[3 * i + 1]],
                    allVertices[convexHullFaceIndices[3 * i + 2]],
                };
                Faces[i] = new PolygonalFace(faceVertices, false);
            }
            Edges       = MakeEdges(Faces, Vertices);
            SurfaceArea = Faces.Sum(face => face.Area);
            TessellatedSolid.CalculateVolumeAndCenter(Faces, tolerance, out Volume, out Center);
        }
コード例 #23
0
 /// <summary>
 ///     Initializes a new instance of the <see cref="Edge" /> class.
 /// </summary>
 /// <param name="fromVertex">From vertex.</param>
 /// <param name="toVertex">To vertex.</param>
 /// <param name="ownedFace">The face.</param>
 /// <param name="otherFace">The other face.</param>
 /// <param name="doublyLinkedVertices">if set to <c>true</c> [doubly linked vertices].</param>
 /// <param name="edgeReference">The edge reference.</param>
 /// <exception cref="Exception"></exception>
 public Edge(Vertex fromVertex, Vertex toVertex, PolygonalFace ownedFace, PolygonalFace otherFace,
             bool doublyLinkedVertices, long edgeReference = 0) : this(fromVertex, toVertex, doublyLinkedVertices)
 {
     if (edgeReference > 0)
     {
         EdgeReference = edgeReference;
     }
     else
     {
         TessellatedSolid.SetAndGetEdgeChecksum(this);
     }
     _ownedFace = ownedFace;
     _otherFace = otherFace;
     if (ownedFace != null)
     {
         ownedFace.AddEdge(this);
     }
     if (otherFace != null)
     {
         otherFace.AddEdge(this);
     }
     DefineInternalEdgeAngle();
 }
コード例 #24
0
ファイル: Plane.cs プロジェクト: mattMedemaLabs/TVGL
        /// <summary>
        /// Updates the with.
        /// </summary>
        /// <param name="face">The face.</param>
        public override void UpdateWith(PolygonalFace face)
        {
            if (Faces == null)
            {
                Faces = new HashSet <PolygonalFace>();
            }
            Normal = (Faces.Count * Normal) + face.Normal;
            Normal = Vector3.Normalize(Normal);
            var numNewVerts        = 0;
            var newDistanceToPlane = 0.0;

            if (Vertices == null)
            {
                Vertices = new HashSet <Vertex>();
            }
            foreach (var v in face.Vertices.Where(v => !Vertices.Contains(v)))
            {
                numNewVerts++;
                newDistanceToPlane += v.Coordinates.Dot(Normal);
            }
            DistanceToOrigin = (Vertices.Count * DistanceToOrigin + newDistanceToPlane) / (Vertices.Count + numNewVerts);
            base.UpdateWith(face);
        }
コード例 #25
0
        /// <summary>
        /// Complexifies the tessellation so that no edge is longer than provided the maximum edge length
        /// or for adding the provided number of faces - whichever comes first
        /// </summary>
        /// <param name="ts">The ts.</param>
        /// <param name="numberOfFaces">The number of new faces to add.</param>
        /// <param name="maxLength">The maximum length.</param>
        public static void Complexify(TessellatedSolid ts, int numberOfFaces, double maxLength)
        {
            var edgeQueue = new SimplePriorityQueue <Edge, double>(new ReverseSort());

            foreach (var e in ts.Edges)
            {
                edgeQueue.Enqueue(e, e.Length);
            }
            var addedEdges    = new List <Edge>();
            var addedVertices = new List <Vertex>();
            var addedFaces    = new List <PolygonalFace>();
            var edge          = edgeQueue.Dequeue();
            var iterations    = numberOfFaces > 0 ? (int)Math.Ceiling(numberOfFaces / 2.0) : numberOfFaces;

            while (iterations-- != 0 && edge.Length >= maxLength)
            {
                var origLeftFace   = edge.OtherFace;
                var origRightFace  = edge.OwnedFace;
                var leftFarVertex  = origLeftFace.OtherVertex(edge);
                var rightFarVertex = origRightFace.OtherVertex(edge);
                var fromVertex     = edge.From;
                var toVertex       = edge.To;
                var addedVertex    = new Vertex(DetermineIntermediateVertexPosition(fromVertex, toVertex));
                // modify original faces with new intermediate vertex
                var index = origLeftFace.Vertices.IndexOf(toVertex);
                origLeftFace.Vertices[index] = addedVertex;
                origLeftFace.Update();
                addedVertex.Faces.Add(origLeftFace);
                index = origRightFace.Vertices.IndexOf(toVertex);
                origRightFace.Vertices[index] = addedVertex;
                origRightFace.Update();
                addedVertex.Faces.Add(origRightFace);

                var newLeftFace  = new PolygonalFace(new[] { toVertex, addedVertex, leftFarVertex });
                var newRightFace = new PolygonalFace(new[] { addedVertex, toVertex, rightFarVertex });
                toVertex.Faces.Remove(origLeftFace);
                toVertex.Faces.Remove(origRightFace);

                var inlineEdge = new Edge(addedVertex, toVertex, newRightFace, newLeftFace, true);
                toVertex.Edges.Remove(edge);
                edge.To = addedVertex;
                addedVertex.Edges.Add(edge);
                edge.Update();
                var newLeftEdge  = new Edge(leftFarVertex, addedVertex, origLeftFace, newLeftFace, true);
                var newRightEdge = new Edge(rightFarVertex, addedVertex, newRightFace, origRightFace, true);
                origLeftFace.AddEdge(newLeftEdge);
                origRightFace.AddEdge(newRightEdge);
                var bottomEdge = toVertex.Edges.First(e => e.OtherVertex(toVertex) == leftFarVertex);
                if (bottomEdge.OwnedFace == origLeftFace)
                {
                    bottomEdge.OwnedFace = newLeftFace;
                }
                else
                {
                    bottomEdge.OtherFace = newLeftFace;
                }
                newLeftFace.AddEdge(bottomEdge);
                bottomEdge.Update();

                bottomEdge = toVertex.Edges.First(e => e.OtherVertex(toVertex) == rightFarVertex);
                if (bottomEdge.OwnedFace == origRightFace)
                {
                    bottomEdge.OwnedFace = newRightFace;
                }
                else
                {
                    bottomEdge.OtherFace = newRightFace;
                }
                newRightFace.AddEdge(bottomEdge);
                bottomEdge.Update();


                // need to re-add the edge. It was modified in the SplitEdge function (now, half the lenght), but
                // it may still be met by this criteria
                edgeQueue.Enqueue(edge, edge.Length);
                edgeQueue.Enqueue(inlineEdge, inlineEdge.Length);
                addedEdges.Add(inlineEdge);
                edgeQueue.Enqueue(newLeftEdge, newLeftEdge.Length);
                addedEdges.Add(newLeftEdge);
                edgeQueue.Enqueue(newRightEdge, newRightEdge.Length);
                addedEdges.Add(newRightEdge);
                addedFaces.Add(newLeftFace);
                addedFaces.Add(newRightFace);
                addedVertices.Add(addedVertex);
                edge = edgeQueue.First();
            }
            ts.AddVertices(addedVertices);
            ts.AddEdges(addedEdges);
            ts.AddFaces(addedFaces);
        }
コード例 #26
0
        /// <summary>
        ///     Combines the vertices of edge.
        /// </summary>
        /// <param name="edge">The edge.</param>
        /// <param name="removedVertexOut">The removed vertex out.</param>
        /// <param name="removedEdge1Out">The removed edge1 out.</param>
        /// <param name="removedEdge2Out">The removed edge2 out.</param>
        /// <param name="removedFace1">The removed face1.</param>
        /// <param name="removedFace2">The removed face2.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        private static bool CombineVerticesOfEdge(Edge edge, out Vertex removedVertexOut, out Edge removedEdge1Out,
                                                  out Edge removedEdge2Out, out PolygonalFace removedFace1, out PolygonalFace removedFace2)
        {
            var keepVertex    = edge.To;   // arbitrarily choose the To as the keep vertex, but this may be swapped below
            var removedVertex = edge.From; // if the To has some missing faces

            if (keepVertex == removedVertex)
            {
                removedVertexOut = null;
                removedEdge2Out  = removedEdge1Out = null;
                removedFace1     = removedFace2 = null;
                return(false);
            }
            removedFace1 = edge.OwnedFace;
            removedFace2 = edge.OtherFace;
            var removedEdge1 = removedFace1 == null ? null : removedFace1.OtherEdge(keepVertex, true);
            var removedEdge2 = removedFace2 == null ? null : removedFace2.OtherEdge(keepVertex, true);
            var keepEdge1    = removedFace1 == null ? null : removedFace1.OtherEdge(removedVertex, true);
            var keepEdge2    = removedFace2 == null ? null : removedFace2.OtherEdge(removedVertex, true);

            if (removedEdge1 != null && removedEdge2 != null && (keepEdge1 == null || keepEdge2 == null))
            {
                // swap with removed.
                var tempVertex = keepVertex;
                keepVertex    = removedVertex;
                removedVertex = tempVertex;
                var tempEdge = keepEdge1;
                keepEdge1    = removedEdge1;
                removedEdge1 = tempEdge;
                tempEdge     = keepEdge2;
                keepEdge2    = removedEdge2;
                removedEdge2 = tempEdge;
            }
            var otherEdgesOnTheKeepSide =
                keepVertex.Edges.Where(e => e != edge && e != keepEdge1 && e != keepEdge2).ToList();
            var otherEdgesOnTheRemoveSide =
                removedVertex.Edges.Where(e => e != edge && e != removedEdge1 && e != removedEdge2).ToList();

            if ( // this is a topologically important check. It ensures that the edge is not deleted if
                 // it serves an important role in ensuring the proper topology of the solid
                otherEdgesOnTheKeepSide.Select(e => e.OtherVertex(keepVertex))
                .Intersect(otherEdgesOnTheRemoveSide.Select(e => e.OtherVertex(removedVertex)))
                .Any())
            {
                removedVertexOut = null;
                removedEdge2Out  = removedEdge1Out = null;
                removedFace1     = removedFace2 = null;
                return(false);
            }
            // move edges connected to removeVertex to the keepVertex and let keepVertex link back to these edges
            foreach (var e in otherEdgesOnTheRemoveSide)
            {
                keepVertex.Edges.Add(e);
                if (e.From == removedVertex)
                {
                    e.From = keepVertex;
                }
                else
                {
                    e.To = keepVertex;
                }
            }
            // move faces connected to removeVertex to the keepVertex and let keepVertex link back to these edges.
            foreach (var face in removedVertex.Faces)
            {
                if (face == removedFace1 || face == removedFace2)
                {
                    continue;
                }
                keepVertex.Faces.Add(face);
                face.Vertices[face.Vertices.IndexOf(removedVertex)] = keepVertex;
            }
            // conversely keepVertex should forget about the edge and the remove faces
            keepVertex.Edges.Remove(edge);
            keepVertex.Faces.Remove(removedFace1);
            keepVertex.Faces.Remove(removedFace2);
            var farVertex = removedFace1 == null ? null : removedFace1.OtherVertex(edge, true);

            if (farVertex != null)
            {
                farVertex.Edges.Remove(removedEdge1);
                farVertex.Faces.Remove(removedFace1);
            }
            farVertex = removedFace2 == null ? null : removedFace2.OtherVertex(edge, true);
            if (farVertex != null)
            {
                farVertex.Edges.Remove(removedEdge2);
                farVertex.Faces.Remove(removedFace2);
            }
            // for the winged edges (removedEdge1 and removedEdge2) that are removed, connected their faces to
            // the new edge
            // first on the "owned side of edge"
            var fromFace = removedEdge1 == null
                ? null
                : removedEdge1.OwnedFace == removedFace1 ? removedEdge1.OtherFace : removedEdge1.OwnedFace;

            if (fromFace != null)
            {
                var index = fromFace.Edges.IndexOf(removedEdge1);
                if (index >= 0 && index < fromFace.Edges.Count)
                {
                    fromFace.Edges[index] = keepEdge1;
                }
            }
            if (keepEdge1 != null && keepEdge1.OwnedFace == removedFace1)
            {
                keepEdge1.OwnedFace = fromFace;
            }
            else if (keepEdge1 != null)
            {
                keepEdge1.OtherFace = fromFace;
            }
            // second on the "other side of edge"
            fromFace = removedEdge2 == null
                ? null
                : removedEdge2.OwnedFace == removedFace2 ? removedEdge2.OtherFace : removedEdge2.OwnedFace;
            if (fromFace != null)
            {
                var index = fromFace.Edges.IndexOf(removedEdge2);
                if (index >= 0 && index < fromFace.Edges.Count)
                {
                    fromFace.Edges[index] = keepEdge2;
                }
            }
            if (keepEdge2 != null && keepEdge2.OwnedFace == removedFace2)
            {
                keepEdge2.OwnedFace = fromFace;
            }
            else if (keepEdge2 != null)
            {
                keepEdge2.OtherFace = fromFace;
            }
            keepVertex.Position = DetermineIntermediateVertexPosition(keepVertex, removedVertex);
            foreach (var e in keepVertex.Edges)
            {
                e.Update();
            }
            foreach (var f in keepVertex.Faces)
            {
                f.Update();
            }
            removedVertexOut = removedVertex;
            removedEdge1Out  = removedEdge1;
            removedEdge2Out  = removedEdge2;
            return(true);
        }
コード例 #27
0
        /// <summary>
        /// Simplifies the model by merging the eliminating edges that are closer together
        /// than double the shortest edge length
        /// </summary>
        /// <param name="ts">The ts.</param>
        public static void SimplifyFlatPatches(this TessellatedSolid ts)
        {
            //   throw new NotImplementedException();
            var edgesToRemove    = new List <Edge>();
            var edgesToAdd       = new List <Edge>();
            var facesToRemove    = new List <PolygonalFace>();
            var facesToAdd       = new List <PolygonalFace>();
            var verticesToRemove = new List <Vertex>();
            var flats            = TVGL.MiscFunctions.FindFlats(ts.Faces);

            if (ts.Primitives == null)
            {
                ts.Primitives = new List <PrimitiveSurface>();
            }
            foreach (var flat in flats)
            {
                if (flat.InnerEdges.Count < flat.Faces.Count)
                {
                    continue;
                }
                var newFaces         = new List <PolygonalFace>();
                var outerEdgeHashSet = new HashSet <Edge>(flat.OuterEdges);
                facesToRemove.AddRange(flat.Faces);
                edgesToRemove.AddRange(flat.InnerEdges);
                var innerVertices = new HashSet <Vertex>(flat.InnerEdges.Select(e => e.To));
                innerVertices.UnionWith(flat.InnerEdges.Select(e => e.From));
                innerVertices.RemoveWhere(v => outerEdgeHashSet.Overlaps(v.Edges));
                verticesToRemove.AddRange(innerVertices);
                var vertexLoops = OrganizeIntoLoop(flat.OuterEdges, flat.Normal);
                List <List <Vertex[]> > triangulatedListofLists = TriangulatePolygon.Run(new[] { vertexLoops }, flat.Normal);
                var triangulatedList  = triangulatedListofLists.SelectMany(tl => tl).ToList();
                var oldEdgeDictionary = flat.OuterEdges.ToDictionary(TessellatedSolid.SetAndGetEdgeChecksum);
                Dictionary <long, Edge> newEdgeDictionary = new Dictionary <long, Edge>();
                foreach (var triangle in triangulatedList)
                {
                    var newFace = new PolygonalFace(triangle, flat.Normal);
                    if (newFace.Area.IsNegligible() && newFace.Normal.Any(double.IsNaN))
                    {
                        continue;
                    }
                    newFaces.Add(newFace);
                    for (var j = 0; j < 3; j++)
                    {
                        var fromVertex = newFace.Vertices[j];
                        var toVertex   = newFace.NextVertexCCW(fromVertex);
                        var checksum   = TessellatedSolid.GetEdgeChecksum(fromVertex, toVertex);
                        if (oldEdgeDictionary.ContainsKey(checksum))
                        {
                            //fix up old outer edge.
                            var edge = oldEdgeDictionary[checksum];
                            if (fromVertex == edge.From)
                            {
                                edge.OwnedFace = newFace;
                            }
                            else
                            {
                                edge.OtherFace = newFace;
                            }
                            newFace.AddEdge(edge);
                            oldEdgeDictionary.Remove(checksum);
                        }
                        else if (newEdgeDictionary.ContainsKey(checksum))
                        {
                            //Finish creating edge.
                            var newEdge = newEdgeDictionary[checksum];
                            newEdge.OtherFace = newFace;
                            newFace.AddEdge(newEdge);
                            newEdgeDictionary.Remove(checksum);
                            edgesToAdd.Add(newEdge);
                        }
                        else
                        {
                            newEdgeDictionary.Add(checksum, new Edge(fromVertex, toVertex, newFace, null, false, checksum));
                        }
                    }
                }
                ts.Primitives.Add(new Flat(newFaces));
            }
            ts.RemoveVertices(verticesToRemove);  //todo: check if the order of these five commands
            ts.RemoveFaces(facesToRemove);        // matters. There may be an ordering that is more efficient
            ts.AddFaces(facesToAdd);
            ts.RemoveEdges(edgesToRemove);
            ts.AddEdges(edgesToAdd);
        }
コード例 #28
0
ファイル: Extrude.cs プロジェクト: opensourcer2/TVGL
        /// <summary>
        /// Create the Polygonal Faces for a new Tesselated Solid by extruding the given loop along the given normal.
        /// Setting midPlane to true, extrudes half forward and half reverse.
        /// </summary>
        /// <param name="loops"></param>
        /// <param name="extrudeDirection"></param>
        /// <param name="distance"></param>
        /// <param name="midPlane"></param>
        /// <returns></returns>
        public static List <PolygonalFace> ReturnFacesFromLoops(IEnumerable <IEnumerable <double[]> > loops, double[] extrudeDirection,
                                                                double distance, bool midPlane = false)
        {
            //This simplifies the cases we have to handle by always extruding in the positive direction
            if (distance < 0)
            {
                distance         = -distance;
                extrudeDirection = extrudeDirection.multiply(-1);
            }

            //First, make sure we are using "clean" loops. (e.g. not connected to any faces or edges)
            var cleanLoops = new List <List <Vertex> >();
            var i          = 0;

            foreach (var loop in loops)
            {
                var cleanLoop = new List <Vertex>();
                foreach (var vertexPosition in loop)
                {
                    //If a midPlane extrusion, move the original vertices backwards by 1/2 the extrude distance.
                    //These vertices will be used as the base for offsetting the paired vertices forward by the
                    //entire extrude distance.
                    if (midPlane)
                    {
                        var midPlaneVertexPosition = vertexPosition.add(extrudeDirection.multiply(-distance / 2), 3);
                        cleanLoop.Add(new Vertex(midPlaneVertexPosition, i));
                    }
                    else
                    {
                        cleanLoop.Add(new Vertex(vertexPosition, i));
                    }
                    i++;
                }
                cleanLoops.Add(cleanLoop);
            }
            var distanceFromOriginAlongDirection = extrudeDirection.dotProduct(cleanLoops.First().First().Position, 3);

            //First, triangulate the loops
            var listOfFaces   = new List <PolygonalFace>();
            var backTransform = new double[, ] {
            };
            var paths         = cleanLoops.Select(loop => MiscFunctions.Get2DProjectionPointsAsLightReorderingIfNecessary(loop.ToArray(), extrudeDirection, out backTransform)).ToList();
            List <PointLight[]> points2D;
            List <Vertex[]>     triangles;

            try
            {
                //Reset the list of triangles
                triangles = new List <Vertex[]>();

                //Do some polygon functions to clean up issues and try again
                //This is important because the Get2DProjections may produce invalid paths and because
                //triangulate will try 3 times before throwing the exception to go to the catch.
                paths = PolygonOperations.Union(paths, true, PolygonFillType.EvenOdd);

                //Since triangulate polygon needs the points to have references to their vertices, we need to add vertex references to each point
                //This also means we need to recreate cleanLoops
                //Also, give the vertices indices.
                cleanLoops = new List <List <Vertex> >();
                points2D   = new List <PointLight[]>();
                var j = 0;
                foreach (var path in paths)
                {
                    var pathAsPoints = path.Select(p => new PointLight(p.X, p.Y, true)).ToArray();
                    var area         = new PolygonLight(path).Area;
                    points2D.Add(pathAsPoints);
                    var cleanLoop = new List <Vertex>();
                    foreach (var point in pathAsPoints)
                    {
                        var position        = new[] { point.X, point.Y, 0.0, 1.0 };
                        var vertexPosition1 = backTransform.multiply(position).Take(3).ToArray();
                        //The point has been located back to its original position. It is not necessarily the correct distance along the cutting plane normal.
                        //So, we must move it to be on the plane
                        //This next line gets a second vertex to use for the point on plane function
                        var vertexPosition2 = vertexPosition1.add(extrudeDirection.multiply(5), 3);
                        var vertex          = MiscFunctions.PointOnPlaneFromIntersectingLine(extrudeDirection,
                                                                                             distanceFromOriginAlongDirection, new Vertex(vertexPosition1),
                                                                                             new Vertex(vertexPosition2));
                        vertex.IndexInList = j;
                        point.References.Add(vertex);
                        cleanLoop.Add(vertex);
                        j++;
                    }
                    cleanLoops.Add(cleanLoop);
                }

                bool[] isPositive       = null;
                var    triangleFaceList = TriangulatePolygon.Run2D(points2D, out _, ref isPositive);
                foreach (var face in triangleFaceList)
                {
                    triangles.AddRange(face);
                }
            }
            catch
            {
                try
                {
                    //Reset the list of triangles
                    triangles = new List <Vertex[]>();

                    //Do some polygon functions to clean up issues and try again
                    paths = PolygonOperations.Union(paths, true, PolygonFillType.EvenOdd);
                    paths = PolygonOperations.OffsetRound(paths, distance / 1000);
                    paths = PolygonOperations.OffsetRound(paths, -distance / 1000);
                    paths = PolygonOperations.Union(paths, true, PolygonFillType.EvenOdd);

                    //Since triangulate polygon needs the points to have references to their vertices, we need to add vertex references to each point
                    //This also means we need to recreate cleanLoops
                    //Also, give the vertices indices.
                    cleanLoops = new List <List <Vertex> >();
                    points2D   = new List <PointLight[]>();
                    var j = 0;
                    foreach (var path in paths)
                    {
                        var pathAsPoints = path.Select(p => new PointLight(p.X, p.Y, true)).ToArray();
                        points2D.Add(pathAsPoints);
                        var cleanLoop = new List <Vertex>();
                        foreach (var point in pathAsPoints)
                        {
                            var position        = new[] { point.X, point.Y, 0.0, 1.0 };
                            var vertexPosition1 = backTransform.multiply(position).Take(3).ToArray();
                            //The point has been located back to its original position. It is not necessarily the correct distance along the cutting plane normal.
                            //So, we must move it to be on the plane
                            //This next line gets a second vertex to use for the point on plane function
                            var vertexPosition2 = vertexPosition1.add(extrudeDirection.multiply(5), 3);
                            var vertex          = MiscFunctions.PointOnPlaneFromIntersectingLine(extrudeDirection,
                                                                                                 distanceFromOriginAlongDirection, new Vertex(vertexPosition1),
                                                                                                 new Vertex(vertexPosition2));
                            vertex.IndexInList = j;
                            point.References.Add(vertex);
                            cleanLoop.Add(vertex);
                            j++;
                        }
                        cleanLoops.Add(cleanLoop);
                    }

                    bool[] isPositive       = null;
                    var    triangleFaceList = TriangulatePolygon.Run2D(points2D, out _, ref isPositive);
                    foreach (var face in triangleFaceList)
                    {
                        triangles.AddRange(face);
                    }
                }
                catch
                {
                    Debug.WriteLine("Tried extrusion twice and failed.");
                    return(null);
                }
            }

            //Second, build up the a set of duplicate vertices
            var vertices = new HashSet <Vertex>();

            foreach (var vertex in cleanLoops.SelectMany(loop => loop))
            {
                vertices.Add(vertex);
            }
            var pairedVertices = new Dictionary <Vertex, Vertex>();

            foreach (var vertex in vertices)
            {
                var newVertex = new Vertex(vertex.Position.add(extrudeDirection.multiply(distance), 3));
                pairedVertices.Add(vertex, newVertex);
            }

            //Third, create the triangles on the two ends
            //var triangleDictionary = new Dictionary<PolygonalFace, PolygonalFace>();
            var topFaces = new List <PolygonalFace>();

            foreach (var triangle in triangles)
            {
                //Create the triangle in plane with the loops
                var v1 = triangle[1].Position.subtract(triangle[0].Position, 3);
                var v2 = triangle[2].Position.subtract(triangle[0].Position, 3);

                //This model reverses the triangle vertex ordering as necessary to line up with the normal.
                var topTriangle = v1.crossProduct(v2).dotProduct(extrudeDirection.multiply(-1), 3) < 0
                    ? new PolygonalFace(triangle.Reverse(), extrudeDirection.multiply(-1), true)
                    : new PolygonalFace(triangle, extrudeDirection.multiply(-1), true);
                topFaces.Add(topTriangle);
                listOfFaces.Add(topTriangle);

                //Create the triangle on the opposite side of the extrusion
                var bottomTriangle = new PolygonalFace(
                    new List <Vertex>
                {
                    pairedVertices[triangle[0]],
                    pairedVertices[triangle[2]],
                    pairedVertices[triangle[1]]
                }, extrudeDirection, false);
                listOfFaces.Add(bottomTriangle);
                //triangleDictionary.Add(topTriangle, bottomTriangle);
            }

            //Fourth, create the triangles on the sides
            //The normals of the faces are dependent on the whether the loops are ordered correctly from the view of the extrude direction
            //This influences which order the vertices are used to create triangles.
            for (var j = 0; j < cleanLoops.Count; j++)
            {
                var loop = cleanLoops[j];

                //Determine if the loop direction is correct by using the top face
                var v1 = loop[0];
                var v2 = loop[1];

                //Find the face with both of these vertices
                PolygonalFace firstFace = null;
                foreach (var face in topFaces)
                {
                    if (face.Vertices[0] == v1 || face.Vertices[1] == v1 || face.Vertices[2] == v1)
                    {
                        if (face.Vertices[0] == v2 || face.Vertices[1] == v2 || face.Vertices[2] == v2)
                        {
                            firstFace = face;
                            break;
                        }
                    }
                }
                if (firstFace == null)
                {
                    throw new Exception("Did not find face with both the vertices");
                }


                if (firstFace.NextVertexCCW(v1) == v2)
                {
                    //Do nothing
                }
                else if (firstFace.NextVertexCCW(v2) == v1)
                {
                    //Reverse the loop
                    loop.Reverse();
                }
                else
                {
                    throw new Exception();
                }

                //The loop is now ordered correctly
                //It does not matter whether the loop is positive or negative, only that it is ordered correctly for the given extrude direction
                for (var k = 0; k < loop.Count; k++)
                {
                    var g = k + 1;
                    if (k == loop.Count - 1)
                    {
                        g = 0;
                    }

                    //Create the new triangles
                    listOfFaces.Add(new PolygonalFace(new List <Vertex>()
                    {
                        loop[k], pairedVertices[loop[k]], pairedVertices[loop[g]]
                    }));
                    listOfFaces.Add(new PolygonalFace(new List <Vertex>()
                    {
                        loop[k], pairedVertices[loop[g]], loop[g]
                    }));
                }
            }

            return(listOfFaces);
        }
コード例 #29
0
        /// <summary>
        /// Returns owned and other face in that order
        /// </summary>
        /// <returns></returns>
        internal static (PolygonalFace, PolygonalFace) GetOwnedAndOtherFace(long edgeChecksum, PolygonalFace face1, PolygonalFace face2)
        {
            var(from, to) = GetVertexIndices(edgeChecksum);
            //We are going to enforce that the edge is defined along the vertices, such that it goes from the smaller
            //vertex index to the larger.
            var v0      = face1.Vertices.First(v => v.IndexInList == from);
            var v1      = face1.Vertices.First(v => v.IndexInList == to);
            var v2      = face1.Vertices.First(v => v.IndexInList != to && v.IndexInList != from);
            var vector1 = v1.Position.subtract(v0.Position);
            var vector2 = v2.Position.subtract(v1.Position);
            var dot     = vector1.crossProduct(vector2).dotProduct(face1.Normal);

            //The owned face(the face in which the from-to direction makes sense
            // - that is, produces the proper cross-product normal).
            return(Math.Sign(dot) > 0 ? (face1, face2) : (face2, face1));
        }
コード例 #30
0
        private static IEnumerable <List <PointLight> > GetSurfacePaths(List <HashSet <PolygonalFace> > surfaces, double[] normal,
                                                                        double minAreaToConsider, TessellatedSolid originalSolid, Dictionary <int, List <PointLight> > projectedFacePolygons)
        {
            originalSolid.HasUniformColor = false;

            var red      = new Color(KnownColors.Red);
            var allPaths = new List <List <PointLight> >();

            foreach (var surface in surfaces)
            {
                //Get the surface inner and outer edges
                var outerEdges = new HashSet <Edge>();
                var innerEdges = new HashSet <Edge>();
                foreach (var face in surface)
                {
                    if (face.Edges.Count != 3)
                    {
                        throw new Exception();
                    }
                    foreach (var edge in face.Edges)
                    {
                        //if (innerEdges.Contains(edge)) continue;
                        if (!outerEdges.Contains(edge))
                        {
                            outerEdges.Add(edge);
                        }
                        else if (outerEdges.Contains(edge))
                        {
                            innerEdges.Add(edge);
                            outerEdges.Remove(edge);
                        }
                        else
                        {
                            throw new Exception();
                        }
                    }
                }

                var surfacePaths  = new List <List <PointLight> >();
                var assignedEdges = new HashSet <Edge>();
                while (outerEdges.Any())
                {
                    //Get the start vertex and edge and save them to the lists
                    var startEdge   = outerEdges.First();
                    var startVertex = startEdge.From;
                    var loop        = new List <Vertex> {
                        startVertex
                    };

                    var nextVertex = startEdge.To;
                    var edgeLoop   = new List <(Edge, Vertex, Vertex)> {
                        (startEdge, startVertex, nextVertex)
                    };
                    assignedEdges.Add(startEdge);

                    //Initialize the current vertex and edge
                    var vertex      = startEdge.From;
                    var currentEdge = startEdge;

                    //Loop until back to the start vertex
                    while (nextVertex.IndexInList != startVertex.IndexInList)
                    {
                        //Get the next edge
                        var nextEdges = nextVertex.Edges.Where(e => !assignedEdges.Contains(e)).Where(e => outerEdges.Contains(e))
                                        .ToList();
                        if (nextEdges.Count == 0)
                        {
                            Debug.WriteLine("Surface paths do not wrap around properly. Artificially closing loop.");
                            break;
                        }
                        if (nextEdges.Count > 1)
                        {
                            //There are multiple edges to go to next. Simply reversing will cause an issue
                            //if the same thing happens along the other direction.
                            //To avoid this, we go in the direction of the current edge's surface face, until we
                            //hit an edge.
                            var minAngle    = 2 * Math.PI;
                            var currentFace = surface.Contains(currentEdge.OtherFace) ? currentEdge.OtherFace : currentEdge.OwnedFace;
                            //currentFace.Color = new Color(KnownColors.White);
                            var otherVertex = currentFace.OtherVertex(currentEdge.To, currentEdge.From);
                            var angle1      = MiscFunctions.ProjectedExteriorAngleBetweenVerticesCCW(vertex, nextVertex, otherVertex, normal);
                            var angle2      = MiscFunctions.ProjectedInteriorAngleBetweenVerticesCCW(vertex, nextVertex, otherVertex, normal);
                            if (angle1 < angle2)
                            {
                                //Use the exterior angle
                                foreach (var edge in nextEdges)
                                {
                                    var furtherVertex = edge.OtherVertex(nextVertex);
                                    var angle         = MiscFunctions.ProjectedExteriorAngleBetweenVerticesCCW(vertex, nextVertex, furtherVertex, normal);
                                    if (!(angle < minAngle))
                                    {
                                        continue;
                                    }
                                    minAngle = angle;
                                    //Update the current edge
                                    currentEdge = edge;
                                }
                            }
                            else
                            {
                                //Use the interior angle
                                foreach (var edge in nextEdges)
                                {
                                    var furtherVertex = edge.OtherVertex(nextVertex);
                                    var angle         = MiscFunctions.ProjectedInteriorAngleBetweenVerticesCCW(vertex, nextVertex, furtherVertex, normal);
                                    if (!(angle < minAngle))
                                    {
                                        continue;
                                    }
                                    minAngle = angle;
                                    //Update the current edge
                                    currentEdge = edge;

                                    PolygonalFace faceInQuestion = null;
                                    if (surface.Contains(edge.OwnedFace) && surface.Contains(edge.OtherFace))
                                    {
                                        //edge.OwnedFace.Color = new Color(KnownColors.Green);
                                        //edge.OtherFace.Color = red;
                                        //Presenter.ShowVertexPathsWithSolid(new List<List<List<Vertex>>> { error2Loops },
                                        //    new List<TessellatedSolid> { originalSolid });
                                    }
                                    else if (surface.Contains(edge.OwnedFace))
                                    {
                                        faceInQuestion = edge.OtherFace;
                                    }
                                    else if (surface.Contains(edge.OtherFace))
                                    {
                                        faceInQuestion = edge.OwnedFace;
                                    }
                                    if (faceInQuestion != null)
                                    {
                                        //faceInQuestion.Color = red;
                                        //var n2 = PolygonalFace.DetermineNormal(faceInQuestion.Vertices, out _);
                                        //Presenter.ShowAndHang(originalSolid);
                                        //Presenter.ShowVertexPathsWithSolid(new List<List<List<Vertex>>> { error2Loops },
                                        //    new List<TessellatedSolid> { originalSolid });
                                    }
                                }
                            }
                            foreach (var edge in nextEdges)
                            {
                                if (currentFace.Edges.Contains(edge))
                                {
                                    if (edge == currentEdge)
                                    {
                                        break;                      //This is what we want
                                    }
                                    //Presenter.ShowVertexPathsWithSolid(new List<List<List<Vertex>>> { error2Loops },
                                    //    new List<TessellatedSolid> { originalSolid });
                                }
                            }
                        }
                        else
                        {
                            //Update the current edge
                            currentEdge = nextEdges.First();
                        }
                        //Update the current vertex
                        vertex = nextVertex;
                        loop.Add(vertex);
                        //Get the next vertex
                        nextVertex = currentEdge.OtherVertex(vertex);
                        edgeLoop.Add((currentEdge, vertex, nextVertex));
                        assignedEdges.Add(currentEdge);
                    }

                    //To determine order:
                    //The vertices should be listed such that their edge vector cross producted with the second point,
                    //toward a third point on the positive face that provided this edge lines up with the normal.
                    //If that is incorrect, then this may be a hole.
                    //All edges should agree on this test.
                    var correct       = 0;
                    var needsReversal = 0;
                    foreach (var edgeTuple in edgeLoop)
                    {
                        var edge = edgeTuple.Item1;
                        outerEdges.Remove(edge);
                        var isOtherFace = surface.Contains(edge.OtherFace);
                        var isOwnedFace = surface.Contains(edge.OwnedFace);
                        if (isOwnedFace == isOtherFace)
                        {
                            throw new Exception("Should be one and only one face for this edge on this surface");
                        }
                        var positiveFaceBelongingToEdge = isOwnedFace ? edge.OwnedFace : edge.OtherFace;
                        var vertex3 = positiveFaceBelongingToEdge.OtherVertex(edge);
                        var v1      = vertex3.Position.subtract(edgeTuple.Item3.Position, 3);         //To point according to our loop
                        var v2      = edgeTuple.Item3.Position.subtract(edgeTuple.Item2.Position, 3); //To minus from
                        var dot     = v2.crossProduct(v1).dotProduct(positiveFaceBelongingToEdge.Normal, 3);
                        if (dot > 0)
                        {
                            correct++;
                        }
                        else
                        {
                            needsReversal++;
                        }
                    }
                    if (needsReversal > correct)
                    {
                        loop.Reverse();
                    }
                    //if(needsReversal*correct != 0) Debug.WriteLine("Reversed Loop Count: " + needsReversal + " Forward Loop Count: " + correct);

                    //Get2DProjections does not project directionally (normal and normal.multiply(-1) return the same transform)
                    //However, the way we are unioning the polygons and eliminating overhand polygons seems to be taking care of this
                    var surfacePath = MiscFunctions.Get2DProjectionPointsAsLight(loop, normal).ToList();
                    var area2D      = MiscFunctions.AreaOfPolygon(surfacePath);
                    if (area2D.IsNegligible(minAreaToConsider))
                    {
                        continue;
                    }

                    //Trust the ordering from the face normals. A self intersecting polygon may have a negative area,
                    //but in-fact be positive once it undergoes a Fill Positive union. Same goes for positive areas.
                    //if (Math.Sign(area2D) != Math.Sign(area3D)) surfacePath.Reverse();
                    surfacePaths.Add(surfacePath);
                }
                if (!surfacePaths.Any())
                {
                    continue;
                }
                allPaths.AddRange(surfacePaths);
            }

            //By unioning the path into non-self intersecting paths,
            //partially covered holes will be reduced to their final non-covered size.
            //This is necessary for the next few checks in determining if it is a hole or an overhang.
            //This union operation is the trickiest union in the silhouette function to reason through.
            //Using positive fill or even/odd perform pretty well, but they union overlapping
            //negative regions. This is undesirable, since we do not want to union a hole
            //with an overlapping region. For this reason, Union Non-Zero is used. It keeps
            //the holes in their proper orientation and does not combine them together.
            var nonSelfIntersectingPaths = PolygonOperations.Union(allPaths, false, PolygonFillType.NonZero);
            var correctedSurfacePath     = EliminateOverhangPolygons(nonSelfIntersectingPaths, projectedFacePolygons);

            //if (allPaths.Sum(p => p.Count) > 10) Presenter.ShowAndHang(nonSelfIntersectingPaths);

            return(correctedSurfacePath);
        }