Exemplo n.º 1
0
        /// <summary>
        /// Generates a convex hull mesh for a set of points. Also removes all faces that lie on the ExoMesh plates.
        /// </summary>
        /// <param name="nodeIndex">Index of node being hulled.</param>
        /// <param name="sides">Number of sides per strut.</param>
        /// <param name="tol">The tolerance (RhinoDoc.ActiveDoc.ModelAbsoluteTolerance is a good bet).</param>
        /// <param name="cleanPlates">If true, the plate faces will be removed from the hull, so that the sleeves can be directly attached.</param>
        /// <remarks>
        /// If a plate point is coplanar with another plate, the hull may be impossible to clean.
        /// This is because the hulling process may remove the coplanar point and create a new face.
        /// </remarks>
        public Mesh MakeConvexHull(int nodeIndex, int sides, double tol, bool cleanPlates)
        {
            Mesh    hullMesh = new Mesh();
            ExoHull node     = this.Hulls[nodeIndex];
            double  radius   = node.AvgRadius;

            double planeTolerance = tol * radius / 25;

            // Collect all hull points (i.e. all plate points at the node)
            List <Point3d> pts = new List <Point3d>();

            foreach (int pIndex in node.PlateIndices)
            {
                pts.AddRange(this.Plates[pIndex].Vtc);
            }

            // 1. Create initial tetrahedron.
            // Form triangle from 3 first points (lie on same plate, thus, same plane)
            hullMesh.Vertices.Add(pts[0]);
            hullMesh.Vertices.Add(pts[1]);
            hullMesh.Vertices.Add(pts[2]);
            Plane planeStart = new Plane(pts[0], pts[1], pts[2]);
            // Form tetrahedron with a 4th point which does not lie on the same plane
            int nextIndex = sides + 1;

            while (Math.Abs(planeStart.DistanceTo(pts[nextIndex])) < planeTolerance)
            {
                nextIndex++;
            }
            hullMesh.Vertices.Add(pts[nextIndex]);
            // Stitch faces of tetrahedron
            hullMesh.Faces.AddFace(0, 2, 1);
            hullMesh.Faces.AddFace(0, 3, 2);
            hullMesh.Faces.AddFace(0, 1, 3);
            hullMesh.Faces.AddFace(1, 2, 3);

            // 2. Begin the incremental hulling process
            // Remove points already checked
            pts.RemoveAt(nextIndex);
            pts.RemoveRange(0, 3);

            // Loop through the remaining points
            for (int i = 0; i < pts.Count; i++)
            {
                MeshTools.NormaliseMesh(ref hullMesh);

                // Find visible faces
                List <int> seenFaces = new List <int>();
                for (int faceIndex = 0; faceIndex < hullMesh.Faces.Count; faceIndex++)
                {
                    Vector3d testVect  = pts[i] - hullMesh.Faces.GetFaceCenter(faceIndex);
                    double   angle     = Vector3d.VectorAngle(hullMesh.FaceNormals[faceIndex], testVect);
                    Plane    planeTest = new Plane(hullMesh.Faces.GetFaceCenter(faceIndex), hullMesh.FaceNormals[faceIndex]);
                    if (angle < Math.PI * 0.5 || Math.Abs(planeTest.DistanceTo(pts[i])) < planeTolerance)
                    {
                        seenFaces.Add(faceIndex);
                    }
                }

                // Remove visible faces
                hullMesh.Faces.DeleteFaces(seenFaces);
                // Add current point
                hullMesh.Vertices.Add(pts[i]);

                List <MeshFace> addFaces = new List <MeshFace>();
                // Close open hull based on new vertex
                for (int edgeIndex = 0; edgeIndex < hullMesh.TopologyEdges.Count; edgeIndex++)
                {
                    if (!hullMesh.TopologyEdges.IsSwappableEdge(edgeIndex))
                    {
                        IndexPair V  = hullMesh.TopologyEdges.GetTopologyVertices(edgeIndex);
                        int       I1 = hullMesh.TopologyVertices.MeshVertexIndices(V.I)[0];
                        int       I2 = hullMesh.TopologyVertices.MeshVertexIndices(V.J)[0];
                        addFaces.Add(new MeshFace(I1, I2, hullMesh.Vertices.Count - 1));
                    }
                }
                hullMesh.Faces.AddFaces(addFaces);
            }

            MeshTools.NormaliseMesh(ref hullMesh);


            // 3. If requested, delete the hull faces that lie on the plates (so sleeves can connect directly to the hulls)
            if (cleanPlates)
            {
                List <int> deleteFaces = new List <int>();
                foreach (int plateIndx in node.PlateIndices)
                {
                    List <Point3f> plateVtc = MeshTools.Point3dToPoint3f(this.Plates[plateIndx].Vtc);
                    // Recall that strut plates have 'sides+1' vertices.
                    // If the plate has only 'sides' vertices, it is an extra plate (for acute nodes), so we should keep it
                    if (plateVtc.Count < sides + 1)
                    {
                        continue;
                    }

                    for (int j = 0; j < hullMesh.Faces.Count; j++)
                    {
                        Point3f ptA, ptB, ptC, ptD;
                        hullMesh.Faces.GetFaceVertices(j, out ptA, out ptB, out ptC, out ptD);

                        // Check if the mesh face has vertices that belong to a single plate, if so we need to remove the face
                        int matches = 0;
                        foreach (Point3f testPt in plateVtc)
                        {
                            if (testPt.EpsilonEquals(ptA, (float)tol) || testPt.EpsilonEquals(ptB, (float)tol) || testPt.EpsilonEquals(ptC, (float)tol))
                            {
                                matches++;
                            }
                        }
                        // If all three face vertices are plate vertices, we should remove the face
                        if (matches == 3)
                        {
                            deleteFaces.Add(j);
                        }
                    }
                }
                // Remove the faces. Reverse the list so that it is in decreasing order.
                deleteFaces.Reverse();
                foreach (int faceIndx in deleteFaces)
                {
                    hullMesh.Faces.RemoveAt(faceIndx);
                }
            }
            return(hullMesh);
        }