コード例 #1
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);
        }
コード例 #2
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);
        }
コード例 #3
0
        private static IEnumerable <Tuple <Edge, List <PolygonalFace> > > CreateMissingEdgesAndFaces(
            List <Tuple <List <Edge>, double[]> > loops,
            out List <PolygonalFace> newFaces, out List <Edge> remainingEdges)
        {
            var completedEdges = new List <Tuple <Edge, List <PolygonalFace> > >();

            newFaces       = new List <PolygonalFace>();
            remainingEdges = new List <Edge>();
            foreach (var tuple in loops)
            {
                var edges  = tuple.Item1;
                var normal = tuple.Item2;
                //if a simple triangle, create a new face from vertices
                if (edges.Count == 3)
                {
                    var newFace = new PolygonalFace(edges.Select(e => e.To), normal);
                    foreach (var edge in edges)
                    {
                        completedEdges.Add(new Tuple <Edge, List <PolygonalFace> >(edge,
                                                                                   new List <PolygonalFace> {
                            edge.OwnedFace, newFace
                        }));
                    }
                    newFaces.Add(newFace);
                }
                //Else, use the triangulate function
                else
                {
                    Dictionary <long, Edge> edgeDic = new Dictionary <long, Edge>();
                    foreach (var edge in edges)
                    {
                        var checksum = GetEdgeChecksum(edge.From, edge.To);
                        if (!edgeDic.ContainsKey(checksum))
                        {
                            edgeDic.Add(checksum, edge);
                        }
                    }
                    List <List <Vertex[]> > triangleFaceList = null;
                    try
                    {
                        triangleFaceList = TriangulatePolygon.Run(new List <List <Vertex> >
                        {
                            edges.Select(e => e.To).ToList()
                        }, normal);
                    }
                    catch
                    {
                        continue;
                    }
                    var triangles = triangleFaceList.SelectMany(tl => tl).ToList();
                    if (triangles.Any())
                    {
                        Message.output("loop successfully repaired with " + triangles.Count, 5);
                        foreach (var triangle in triangles)
                        {
                            var newFace = new PolygonalFace(triangle, 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   = GetEdgeChecksum(fromVertex, toVertex);
                                if (edgeDic.ContainsKey(checksum))
                                {
                                    //Finish creating edge.
                                    var edge = edgeDic[checksum];
                                    completedEdges.Add(new Tuple <Edge, List <PolygonalFace> >(edge,
                                                                                               new List <PolygonalFace> {
                                        edge.OwnedFace, newFace
                                    }));
                                    edgeDic.Remove(checksum);
                                }
                                else
                                {
                                    edgeDic.Add(checksum, new Edge(fromVertex, toVertex, newFace, null, false, checksum));
                                }
                            }
                        }
                    }
                    else
                    {
                        remainingEdges.AddRange(edges);
                    }
                }
            }
            return(completedEdges);
        }