Ejemplo n.º 1
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            //declare and set primary variables

            List <Curve>  L  = new List <Curve>();
            int           S  = 0;
            List <double> Rs = new List <double>();
            List <double> Re = new List <double>();
            double        D  = 0;
            double        ND = 0;
            bool          O  = false;

            if (!DA.GetDataList(0, L))
            {
                return;
            }
            if (!DA.GetData(1, ref S))
            {
                return;
            }
            if (!DA.GetDataList(2, Rs))
            {
                return;
            }
            if (!DA.GetDataList(3, Re))
            {
                return;
            }
            if (!DA.GetData(4, ref ND))
            {
                return;
            }
            if (!DA.GetData(5, ref D))
            {
                return;
            }
            if (!DA.GetData(6, ref O))
            {
                return;
            }

            if (L == null || L.Count == 0)
            {
                return;
            }
            if (S < 3)
            {
                return;
            }
            if (Rs == null || Rs.Count == 0 || Rs.Contains(0))
            {
                return;
            }
            if (Re == null || Re.Count == 0 || Rs.Contains(0))
            {
                return;
            }
            if (D == 0)
            {
                return;
            }

            //declare node and strut lists and reference lookups

            double Sides = (double)S;

            List <Point3d>     Nodes      = new List <Point3d>();
            List <List <int> > NodeStruts = new List <List <int> >();

            List <Curve>  Struts     = new List <Curve>();
            List <int>    StrutNodes = new List <int>();
            List <double> StrutRadii = new List <double>();

            double tol = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance;

            //set the index counter for matching start and end radii from input list
            int IdxL = 0;

            //register unique nodes and struts, with reference lookups
            //each full strut is broken into two half-struts, with the even-indexed
            //element being the start point, and the odd-indexed element being the end point

            //initialise first node
            Nodes.Add(L[0].PointAtStart);
            NodeStruts.Add(new List <int>());

            Rhino.Collections.Point3dList NodeLookup = new Rhino.Collections.Point3dList(Nodes);

            foreach (Curve StartL in L)
            {
                double StrutStartRadius = 0;
                if (Rs.Count - 1 > IdxL)
                {
                    StrutStartRadius = Rs[IdxL];
                }
                else
                {
                    StrutStartRadius = Rs.Last();
                }

                double StrutEndRadius = 0;
                if (Re.Count - 1 > IdxL)
                {
                    StrutEndRadius = Re[IdxL];
                }
                else
                {
                    StrutEndRadius = Re.Last();
                }

                Point3d StrutCenter = new Point3d((StartL.PointAtStart + StartL.PointAtEnd) / 2);

                int StartTestIdx = NodeLookup.ClosestIndex(StartL.PointAtStart);
                if (Nodes[StartTestIdx].DistanceTo(StartL.PointAtStart) < tol)
                {
                    NodeStruts[StartTestIdx].Add(Struts.Count);
                    StrutNodes.Add(StartTestIdx);
                }
                else
                {
                    StrutNodes.Add(Nodes.Count);
                    Nodes.Add(StartL.PointAtStart);
                    NodeLookup.Add(StartL.PointAtStart);
                    NodeStruts.Add(new List <int>());
                    NodeStruts.Last().Add(Struts.Count());
                }
                Struts.Add(new LineCurve(StartL.PointAtStart, StrutCenter));
                StrutRadii.Add(StrutStartRadius);


                int EndTestIdx = NodeLookup.ClosestIndex(StartL.PointAtEnd);
                if (Nodes[EndTestIdx].DistanceTo(StartL.PointAtEnd) < tol)
                {
                    NodeStruts[EndTestIdx].Add(Struts.Count);
                    StrutNodes.Add(EndTestIdx);
                }
                else
                {
                    StrutNodes.Add(Nodes.Count);
                    Nodes.Add(StartL.PointAtEnd);
                    NodeLookup.Add(StartL.PointAtEnd);
                    NodeStruts.Add(new List <int>());
                    NodeStruts.Last().Add(Struts.Count);
                }
                Struts.Add(new LineCurve(StartL.PointAtEnd, StrutCenter));
                StrutRadii.Add(StrutEndRadius);

                IdxL += 1;
            }


            Plane[]  StrutPlanes  = new Plane[Struts.Count];        //base plane for each strut
            double[] PlaneOffsets = new double[Struts.Count];       //distance for each base plane to be set from node
            Point3d[,] StrutHullVtc = new Point3d[Struts.Count, S]; //two-dimensional array for vertices along each strut for executing hull
            bool[] StrutSolo = new bool[Struts.Count];              //tag for struts that don't share a node with other struts (ends)

            Mesh Hulls = new Mesh();                                //main output mesh

            //cycle through each node to generate hulls
            for (int NodeIdx = 0; NodeIdx < Nodes.Count; NodeIdx++)
            {
                List <int> StrutIndices = NodeStruts[NodeIdx];
                double     MinOffset    = 0;
                double     MaxRadius    = 0;

                //orientation & size drivers for knuckle vertices
                List <Vector3d> Knuckles   = new List <Vector3d>();
                double          KnuckleMin = 0;

                //compare all unique combinations of struts in a given node to calculate plane offsets for
                //hulling operations and for adjusting vertices to potential non-convex hulls
                for (int I1 = 0; I1 < StrutIndices.Count - 1; I1++)
                {
                    for (int I2 = I1 + 1; I2 < StrutIndices.Count; I2++)
                    {
                        //identify minimum offset distances for calculating hulls by comparing each outgoing strut to each other
                        double R1         = StrutRadii[StrutIndices[I1]] / Math.Cos(Math.PI / S);
                        double R2         = StrutRadii[StrutIndices[I2]] / Math.Cos(Math.PI / S);
                        double Radius     = Math.Max(R1, R2);
                        double Theta      = Vector3d.VectorAngle(Struts[StrutIndices[I1]].TangentAtStart, Struts[StrutIndices[I2]].TangentAtStart);
                        double TestOffset = Radius * Math.Cos(Theta * 0.5) / Math.Sin(Theta * 0.5);
                        if (TestOffset > MinOffset)
                        {
                            MinOffset = TestOffset;
                        }
                        if (MaxRadius < Radius)
                        {
                            MaxRadius = Radius;
                        }
                        //set offsets for shrinking hull vertices into a non-convex hull based on desired node offsets (ND) and adjacent struts
                        double Offset1 = 0;
                        double Offset2 = 0;

                        //calculates final offsets for potential shrinking of nodes into non-convex hull
                        ExoTools.OffsetCalculator(Theta, R1, R2, ref Offset1, ref Offset2);

                        if (PlaneOffsets[StrutIndices[I1]] < Offset1)
                        {
                            PlaneOffsets[StrutIndices[I1]] = Math.Max(Offset1, ND);
                        }
                        if (PlaneOffsets[StrutIndices[I2]] < Offset2)
                        {
                            PlaneOffsets[StrutIndices[I2]] = Math.Max(Offset2, ND);
                        }

                        //set offsets for knuckle to be the size of the smallest outgoing strut radius
                        double KnuckleMinSet = Math.Min(StrutRadii[StrutIndices[I1]], StrutRadii[StrutIndices[I2]]);
                        if (I1 == 0 || KnuckleMin > KnuckleMinSet)
                        {
                            KnuckleMin = KnuckleMinSet;
                        }
                    }
                }

                //ensures that if a two struts are linear to one another in a two-strut node that there is an offset for hulling
                if (MinOffset < RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)
                {
                    MinOffset = MaxRadius * 0.5;
                }

                //direction for dealing with struts at ends
                if (StrutIndices.Count == 1)
                {
                    PlaneOffsets[StrutIndices[0]] = ND;
                    if (O)
                    {
                        StrutSolo[StrutIndices[0]] = true;
                    }
                }

                //build base planes, offset them for hulling, and array hull vertices for each strut
                for (int I1 = 0; I1 < StrutIndices.Count; I1++)
                {
                    //build base planes
                    Curve Strut = Struts[StrutIndices[I1]];
                    Plane StrutPlane;

                    //sets the strut plane
                    if (StrutIndices[I1] % 2 == 0)
                    {
                        Strut.PerpendicularFrameAt(0, out StrutPlane);
                    }
                    else
                    {
                        Curve LookupStrut = Struts[StrutIndices[I1] - 1];
                        LookupStrut.PerpendicularFrameAt(0, out StrutPlane);
                    }

                    //offset planes for hulling
                    StrutPlane.Origin = Strut.PointAtStart + Strut.TangentAtStart * MinOffset;
                    double StrutRadius = StrutRadii[StrutIndices[I1]];

                    //add strut tangent to list of knuckle orientation vectors
                    Knuckles.Add(-Strut.TangentAtStart);

                    //add hulling vertices
                    for (int HV = 0; HV < S; HV++)
                    {
                        StrutHullVtc[StrutIndices[I1], HV] = StrutPlane.PointAt(Math.Cos((HV / Sides) * Math.PI * 2) * StrutRadius, Math.Sin((HV / Sides) * Math.PI * 2) * StrutRadius);
                    }


                    double OffsetMult = PlaneOffsets[StrutIndices[I1]];
                    if (ND > OffsetMult)
                    {
                        OffsetMult = ND;
                    }
                    if (StrutIndices[I1] % 2 != 0)
                    {
                        StrutPlane.Rotate(Math.PI, StrutPlane.YAxis, StrutPlane.Origin);
                    }

                    if (StrutSolo[StrutIndices[I1]])
                    {
                        OffsetMult = 0;
                    }

                    StrutPlanes[StrutIndices[I1]]        = StrutPlane;
                    StrutPlanes[StrutIndices[I1]].Origin = Strut.PointAtStart + Strut.TangentAtStart * OffsetMult;
                }

                //collect all of the hull points from each strut in a given node for hulling, including knuckle points

                if (StrutIndices.Count > 1)
                {
                    List <Point3d> HullPts      = new List <Point3d>();
                    List <int>     PlaneIndices = new List <int>();

                    double KnuckleOffset = MinOffset * 0.5;

                    for (int HV = 0; HV < S; HV++)
                    {
                        for (int I1 = 0; I1 < StrutIndices.Count; I1++)
                        {
                            HullPts.Add(StrutHullVtc[StrutIndices[I1], HV]);
                            PlaneIndices.Add(StrutIndices[I1]);
                            if (HV == 0)
                            {
                                HullPts.Add(Nodes[NodeIdx] + (Knuckles[I1] * KnuckleOffset));
                                PlaneIndices.Add(-1);
                            }
                        }
                        double Angle = ((double)HV / S) * (Math.PI * 2);
                    }

                    Rhino.Collections.Point3dList LookupPts = new Rhino.Collections.Point3dList(HullPts);

                    //execute the hulling operation
                    Mesh HullMesh = new Mesh();
                    ExoTools.Hull(HullPts, ref HullMesh);
                    ExoTools.NormaliseMesh(ref HullMesh);

                    Point3d[]  HullVertices = HullMesh.Vertices.ToPoint3dArray();
                    List <int> FaceVertices = new List <int>();

                    //relocate vertices to potentially non-convex configurations
                    for (int HullVtx = 0; HullVtx < HullVertices.Length; HullVtx++)
                    {
                        int CloseIdx = LookupPts.ClosestIndex(HullVertices[HullVtx]);
                        if (PlaneIndices[CloseIdx] > -1)
                        {
                            double OffsetMult = 0;
                            if (ND > PlaneOffsets[PlaneIndices[CloseIdx]])
                            {
                                OffsetMult = ND - MinOffset;
                            }
                            else
                            {
                                OffsetMult = PlaneOffsets[PlaneIndices[CloseIdx]] - MinOffset;
                            }

                            HullVertices[HullVtx]     += StrutPlanes[PlaneIndices[CloseIdx]].ZAxis * OffsetMult;
                            HullMesh.Vertices[HullVtx] = new Point3f((float)HullVertices[HullVtx].X, (float)HullVertices[HullVtx].Y, (float)HullVertices[HullVtx].Z);
                            FaceVertices.Add(PlaneIndices[CloseIdx]);
                        }
                        else
                        {
                            Vector3d KnuckleVector = new Vector3d(HullVertices[HullVtx] - Nodes[NodeIdx]);
                            KnuckleVector.Unitize();
                            Point3d KnucklePt = Nodes[NodeIdx] + (KnuckleVector * KnuckleMin);
                            HullMesh.Vertices[HullVtx] = new Point3f((float)KnucklePt.X, (float)KnucklePt.Y, (float)KnucklePt.Z);
                            FaceVertices.Add(PlaneIndices[CloseIdx]);
                        }
                    }

                    //delete all faces whose vertices are associated with the same plane
                    List <int> DeleteFaces = new List <int>();

                    for (int FaceIdx = 0; FaceIdx < HullMesh.Faces.Count; FaceIdx++)
                    {
                        if ((FaceVertices[HullMesh.Faces[FaceIdx].A] != -1) && (FaceVertices[HullMesh.Faces[FaceIdx].A] == FaceVertices[HullMesh.Faces[FaceIdx].B]) &&
                            (FaceVertices[HullMesh.Faces[FaceIdx].B] == FaceVertices[HullMesh.Faces[FaceIdx].C]))
                        {
                            DeleteFaces.Add(FaceIdx);
                        }
                    }
                    HullMesh.Faces.DeleteFaces(DeleteFaces);
                    ExoTools.NormaliseMesh(ref HullMesh);
                    Hulls.Append(HullMesh);
                }
                else if (!O)
                {
                    Mesh EndMesh = new Mesh();
                    EndMesh.Vertices.Add(StrutPlanes[StrutIndices[0]].Origin);
                    for (int HullVtx = 0; HullVtx < S; HullVtx++)
                    {
                        EndMesh.Vertices.Add(StrutHullVtc[StrutIndices[0], HullVtx]);
                        int StartVtx = HullVtx + 1;
                        int EndVtx   = HullVtx + 2;
                        if (HullVtx == S - 1)
                        {
                            EndVtx = 1;
                        }
                        EndMesh.Faces.AddFace(0, StartVtx, EndVtx);
                    }
                    Hulls.Append(EndMesh);
                }
            }

            //add stocking meshes between struts
            Rhino.Collections.Point3dList MatchPts = new Rhino.Collections.Point3dList(Hulls.Vertices.ToPoint3dArray());
            Mesh StrutMeshes = new Mesh();

            //if a strut is overwhelmed by its nodes then output a sphere centered on the failing strut
            Mesh FailStruts = new Mesh();

            for (int I1 = 0; I1 <= Struts.Count; I1++)
            {
                if (I1 % 2 != 0)
                {
                    if (StrutPlanes[I1 - 1].Origin.DistanceTo(Struts[I1 - 1].PointAtStart) + StrutPlanes[I1].Origin.DistanceTo(Struts[I1].PointAtStart) > Struts[I1 - 1].GetLength() * 2)
                    {
                        Plane    FailPlane    = new Plane(Struts[I1 - 1].PointAtStart, Struts[I1 - 1].TangentAtStart);
                        Cylinder FailCylinder = new Cylinder(new Circle(FailPlane, (StrutRadii[I1] + StrutRadii[I1 - 1]) / 2), Struts[I1 - 1].GetLength() * 2);
                        FailStruts.Append(Mesh.CreateFromCylinder(FailCylinder, 5, 10));
                    }

                    Mesh   StrutMesh   = new Mesh();
                    double StrutLength = StrutPlanes[I1 - 1].Origin.DistanceTo(StrutPlanes[I1].Origin);

                    //calculate the number of segments between nodes
                    int    Segments = (int)(Math.Round((StrutLength * 0.5) / D, 0) * 2) + 2;
                    double PlnZ     = StrutLength / Segments;

                    bool Match = true;
                    if (O && StrutSolo[I1 - 1])
                    {
                        Match = false;
                    }

                    //build up vertices
                    ExoTools.VertexAdd(ref StrutMesh, StrutPlanes[I1 - 1], (double)S, StrutRadii[I1 - 1], Match, MatchPts, Hulls);
                    double StrutIncrement = (StrutRadii[I1] - StrutRadii[I1 - 1]) / Segments;

                    for (int PlnIdx = 1; PlnIdx <= Segments; PlnIdx++)
                    {
                        Plane PlnSet = new Plane(StrutPlanes[I1 - 1]);
                        PlnSet.Rotate((PlnIdx % 2) * -(Math.PI / (double)S), PlnSet.ZAxis);
                        PlnSet.Origin = PlnSet.PointAt(0, 0, PlnIdx * PlnZ);
                        bool Affix = false;
                        if (PlnIdx == Segments && (!StrutSolo[I1] || (StrutSolo[I1] && !O)))
                        {
                            Affix = true;
                        }
                        ExoTools.VertexAdd(ref StrutMesh, PlnSet, (double)S, StrutRadii[I1 - 1] + (StrutIncrement * PlnIdx), Affix, MatchPts, Hulls);
                    }

                    //build up faces
                    ExoTools.StockingStitch(ref StrutMesh, Segments, S);
                    Hulls.Append(StrutMesh);
                }
            }

            if (FailStruts.Vertices.Count > 0)
            {
                DA.SetData(0, FailStruts);
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "One or more struts is engulfed by its nodes");
                return;
            }

            Hulls.Faces.CullDegenerateFaces();
            Hulls.Vertices.CullUnused();
            ExoTools.NormaliseMesh(ref Hulls);

            DA.SetData(0, Hulls);
        }
Ejemplo n.º 2
0
        public static void Main()
        {
            List <Curve> L = new List <Curve>();

            int           S  = 0;
            List <double> Rs = new List <double>();
            List <double> Re = new List <double>();
            double        D  = 0;
            double        ND = 0;
            int           RP = 0;
            bool          O  = false;

            double Sides = (double)S;
            double Tol   = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance;

            List <ExoNode>  ExoNodes  = new List <ExoNode>();
            List <ExoStrut> ExoStruts = new List <ExoStrut>();

            //ExoNodes.Add(new ExoNode());
            Rhino.Collections.Point3dList NodeLookup = new Rhino.Collections.Point3dList();
            int IdxL = 0;

            ExoNodes.Add(new ExoNode(L[0].PointAtStart));
            NodeLookup.Add(ExoNodes[0].Point3d);

            //cycle through each input curve to find unique nodes and assign struts to each node
            foreach (Curve StartL in L)
            {
                //strut as linecurve, and
                LineCurve StartLC = new LineCurve(StartL.PointAtStart, StartL.PointAtEnd);
                Plane     StartPlane;
                StartLC.PerpendicularFrameAt(0, out StartPlane);

                //create paired struts for each input curve
                for (int I = 0; I <= 1; I++)
                {
                    Point3d TestPoint;
                    double  StrutRadius;
                    bool    IsEnd;

                    //set variables based on whether the strut is the start or end strut
                    if (I == 0)
                    {
                        IsEnd     = false;
                        TestPoint = StartL.PointAtStart;
                        if (Rs.Count - 1 > IdxL)
                        {
                            StrutRadius = Rs[IdxL];
                        }
                        else
                        {
                            StrutRadius = Rs.Last();
                        }
                    }
                    else
                    {
                        IsEnd     = true;
                        TestPoint = StartL.PointAtEnd;
                        if (Re.Count - 1 > IdxL)
                        {
                            StrutRadius = Re[IdxL];
                        }
                        else
                        {
                            StrutRadius = Re.Last();
                        }
                    }

                    //register new nodes
                    int TestIndex = NodeLookup.ClosestIndex(TestPoint);
                    int NodeIndex = -1;
                    if (ExoNodes[TestIndex].Point3d.DistanceTo(TestPoint) < Tol)
                    {
                        NodeIndex = TestIndex;
                        ExoNodes[TestIndex].StrutIndices.Add(ExoStruts.Count);
                    }
                    else
                    {
                        NodeIndex = ExoNodes.Count;
                        ExoNodes.Add(new ExoNode(TestPoint));
                        ExoNodes.Last().StrutIndices.Add(ExoStruts.Count);
                    }

                    int LastStrut = ExoNodes[NodeIndex].StrutIndices.Last();
                    //register new struts
                    ExoStruts.Add(new ExoStrut(IsEnd, StrutRadius, StrutRadius / Math.Cos(Math.PI / Sides),
                                               StartLC, StartPlane));

                    //test each new strut in a given node against all of the other struts in a given node to calculate both local
                    //and global vertex offsets, both for hulling operation and for relocating vertices post-hull
                    if (ExoNodes[NodeIndex].StrutIndices.Count > 1)
                    {
                        foreach (int StrutIndex in ExoNodes[NodeIndex].StrutIndices)
                        {
                            if (StrutIndex != LastStrut)
                            {
                                double Radius     = Math.Max(ExoStruts[LastStrut].HullRadius, ExoStruts[StrutIndex].HullRadius);
                                double Theta      = Vector3d.VectorAngle(ExoStruts[LastStrut].Normal, ExoStruts[StrutIndex].Normal);
                                double TestOffset = Radius * Math.Cos(Theta * 0.5) / Math.Sin(Theta * 0.5);
                                if (TestOffset > ExoNodes[NodeIndex].HullOffset)
                                {
                                    ExoNodes[NodeIndex].HullOffset = TestOffset;
                                }
                                if (ExoNodes[NodeIndex].MaxRadius < Radius)
                                {
                                    ExoNodes[NodeIndex].MaxRadius = Radius;
                                }

                                double Offset1 = 0;
                                double Offset2 = 0;

                                ExoTools.OffsetCalculator(Theta, ExoStruts[LastStrut].HullRadius,
                                                          ExoStruts[StrutIndex].HullRadius, ref Offset1, ref Offset2);

                                Offset1 = Math.Max(ND, Offset1);
                                Offset2 = Math.Max(ND, Offset1);

                                if (ExoStruts[LastStrut].FixOffset < Offset1)
                                {
                                    ExoStruts[LastStrut].FixOffset = Offset1;
                                }
                                if (ExoStruts[StrutIndex].FixOffset < Offset1)
                                {
                                    ExoStruts[StrutIndex].FixOffset = Offset2;
                                }

                                double KnuckleMinSet = Math.Min(ExoStruts[LastStrut].Radius, ExoStruts[StrutIndex].Radius);
                                if (ExoNodes[NodeIndex].KnuckleMin <Tol || ExoNodes[NodeIndex].KnuckleMin> KnuckleMinSet)
                                {
                                    ExoNodes[NodeIndex].KnuckleMin = KnuckleMinSet;
                                }
                            }
                        }
                    }
                }
                IdxL += 1;
            }

            foreach (ExoNode HullNode in ExoNodes)
            {
                if (HullNode.StrutIndices.Count == 2)
                {
                    if (Vector3d.VectorAngle(ExoStruts[HullNode.StrutIndices[0]].Normal, ExoStruts[HullNode.StrutIndices[1]].Normal) - Math.PI < RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)
                    {
                        HullNode.HullOffset = HullNode.MaxRadius * 0.5;
                    }
                }

                for (int HV = 0; HV < S; HV++)
                {
                    foreach (int StrutIdx in HullNode.StrutIndices)
                    {
                        if (HV == 0)
                        {
                            ExoStruts[StrutIdx].HullPlane.Origin = ExoStruts[StrutIdx].HullPlane.Origin + (ExoStruts[StrutIdx].Normal * HullNode.HullOffset);
                            HullNode.HullPoints.Add(HullNode.Point3d + (-ExoStruts[StrutIdx].Normal * (HullNode.HullOffset * 0.5)));
                            HullNode.HullVertexStrut.Add(-1);
                        }
                        HullNode.HullPoints.Add(ExoStruts[StrutIdx].HullPlane.PointAt(Math.Cos((HV / Sides) * Math.PI * 2) * ExoStruts[StrutIdx].Radius, Math.Sin((HV / Sides) * Math.PI * 2) * ExoStruts[StrutIdx].Radius));
                        HullNode.HullVertexStrut.Add(StrutIdx);
                    }
                }

                HullNode.HullVertexLookup.AddRange(HullNode.HullPoints);
            }
        }