Beispiel #1
0
        //create a mesh from polyline by kangaroo plankton mesh
        public static Mesh polylineToMesh(Polyline C)
        {
            KPlanktonMesh KM     = new KPlanktonMesh();
            var           points = new Rhino.Collections.Point3dList();

            var faceIndices = new List <int>();

            for (int i = 0; i < C.SegmentCount; i++)
            {
                int nearest = points.ClosestIndex(C.PointAt(i));
                if (nearest == -1)
                {
                    faceIndices.Add(KM.Vertices.Count);
                    KM.Vertices.Add(C.PointAt(i));
                    points.Add(C.PointAt(i));
                }
                else
                {
                    if (points[nearest].DistanceTo(C.PointAt(i)) < 1e-4)
                    {
                        faceIndices.Add(nearest);
                    }
                    else
                    {
                        faceIndices.Add(KM.Vertices.Count);
                        KM.Vertices.Add(C.PointAt(i));
                        points.Add(C.PointAt(i));
                    }
                }
            }
            KM.Faces.AddFace(faceIndices);

            return(KM.ToRhinoMesh());
        }
Beispiel #2
0
 public static Rhino.Commands.Result AddNurbsCurve(Rhino.RhinoDoc doc)
 {
     Rhino.Collections.Point3dList points = new Rhino.Collections.Point3dList(5);
     points.Add(0, 0, 0);
     points.Add(0, 2, 0);
     points.Add(2, 3, 0);
     points.Add(4, 2, 0);
     points.Add(4, 0, 0);
     Rhino.Geometry.NurbsCurve nc = Rhino.Geometry.NurbsCurve.Create(false, 3, points);
     Rhino.Commands.Result     rc = Rhino.Commands.Result.Failure;
     if (nc != null && nc.IsValid)
     {
         if (doc.Objects.AddCurve(nc) != Guid.Empty)
         {
             doc.Views.Redraw();
             rc = Rhino.Commands.Result.Success;
         }
     }
     return(rc);
 }
 public static Rhino.Commands.Result AddNurbsCurve(Rhino.RhinoDoc doc)
 {
     Rhino.Collections.Point3dList points = new Rhino.Collections.Point3dList(5);
     points.Add(0, 0, 0);
     points.Add(0, 2, 0);
     points.Add(2, 3, 0);
     points.Add(4, 2, 0);
     points.Add(4, 0, 0);
     Rhino.Geometry.NurbsCurve nc = Rhino.Geometry.NurbsCurve.Create(false, 3, points);
     Rhino.Commands.Result rc = Rhino.Commands.Result.Failure;
     if (nc != null && nc.IsValid)
     {
       if (doc.Objects.AddCurve(nc) != Guid.Empty)
       {
     doc.Views.Redraw();
     rc = Rhino.Commands.Result.Success;
       }
     }
     return rc;
 }
    /// <summary>
    /// Attempts to create a Rectangle from a Polyline. In order for a polyline to qualify 
    /// as a rectangle, it must have 4 or 5 corner points (i.e. it need not be closed).
    /// <para>This overload also returns deviations.</para>
    /// </summary>
    /// <param name="polyline">Polyline to parse.</param>
    /// <param name="deviation">On success, the deviation will contain the largest deviation between the polyline and the rectangle.</param>
    /// <param name="angleDeviation">On success, the angleDeviation will contain the largest deviation (in radians) between the polyline edges and the rectangle edges.</param>
    /// <returns>A rectangle that is shaped similarly to the polyline or Rectangle3d.Unset 
    /// if the polyline does not represent a rectangle.</returns>
    public static Rectangle3d CreateFromPolyline(IEnumerable<Point3d> polyline, out double deviation, out double angleDeviation)
    {
      if (polyline == null) { throw new ArgumentNullException("polyline"); }

      deviation = 0.0;
      angleDeviation = 0.0;

      Rhino.Collections.Point3dList pts = new Rhino.Collections.Point3dList(5);
      foreach (Point3d pt in polyline)
      {
        pts.Add(pt);
        if (pts.Count > 5) { return Rectangle3d.Unset; }
      }
      if (pts.Count < 4) { return Rectangle3d.Unset; }
      if (pts.Count == 5) { pts.RemoveAt(4); }

      Vector3d AB = pts[1] - pts[0];
      Vector3d DC = pts[2] - pts[3];
      Vector3d AD = pts[3] - pts[0];
      Vector3d BC = pts[2] - pts[1];
      Vector3d x = AB + DC;
      Vector3d y = AD + BC;

      if (x.IsZero && y.IsZero)
      {
        Rectangle3d null_rec = new Rectangle3d(new Plane(pts[0], Vector3d.XAxis, Vector3d.YAxis), 0.0, 0.0);
        ComputeDeviation(null_rec, pts, out deviation, out angleDeviation);
        return null_rec;
      }
      if (x.IsZero) { x.PerpendicularTo(y); }
      if (y.IsZero) { y.PerpendicularTo(x); }

      if (!x.Unitize()) { return Rectangle3d.Unset; }
      if (!y.Unitize()) { return Rectangle3d.Unset; }

      Rectangle3d rc = new Rectangle3d();
      rc.m_plane = new Plane(pts[0], x, y);
      rc.m_x = new Interval(0, 0.5 * (AB.Length + DC.Length));
      rc.m_y = new Interval(0, 0.5 * (AD.Length + BC.Length));

      ComputeDeviation(rc, pts, out deviation, out angleDeviation);
      return rc;
    }
Beispiel #5
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);

            }
        }
Beispiel #6
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);
        }
Beispiel #7
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);
            }
        }
        /// <summary>
        /// Attempts to create a Rectangle from a Polyline. In order for a polyline to qualify
        /// as a rectangle, it must have 4 or 5 corner points (i.e. it need not be closed).
        /// <para>This overload also returns deviations.</para>
        /// </summary>
        /// <param name="polyline">Polyline to parse.</param>
        /// <param name="deviation">On success, the deviation will contain the largest deviation between the polyline and the rectangle.</param>
        /// <param name="angleDeviation">On success, the angleDeviation will contain the largest deviation (in radians) between the polyline edges and the rectangle edges.</param>
        /// <returns>A rectangle that is shaped similarly to the polyline or Rectangle3d.Unset
        /// if the polyline does not represent a rectangle.</returns>
        public static Rectangle3d CreateFromPolyline(IEnumerable <Point3d> polyline, out double deviation, out double angleDeviation)
        {
            if (polyline == null)
            {
                throw new ArgumentNullException("polyline");
            }

            deviation      = 0.0;
            angleDeviation = 0.0;

            Rhino.Collections.Point3dList pts = new Rhino.Collections.Point3dList(5);
            foreach (Point3d pt in polyline)
            {
                pts.Add(pt);
                if (pts.Count > 5)
                {
                    return(Rectangle3d.Unset);
                }
            }
            if (pts.Count < 4)
            {
                return(Rectangle3d.Unset);
            }
            if (pts.Count == 5)
            {
                pts.RemoveAt(4);
            }

            Vector3d AB = pts[1] - pts[0];
            Vector3d DC = pts[2] - pts[3];
            Vector3d AD = pts[3] - pts[0];
            Vector3d BC = pts[2] - pts[1];
            Vector3d x  = AB + DC;
            Vector3d y  = AD + BC;

            if (x.IsZero && y.IsZero)
            {
                Rectangle3d null_rec = new Rectangle3d(new Plane(pts[0], Vector3d.XAxis, Vector3d.YAxis), 0.0, 0.0);
                ComputeDeviation(null_rec, pts, out deviation, out angleDeviation);
                return(null_rec);
            }
            if (x.IsZero)
            {
                x.PerpendicularTo(y);
            }
            if (y.IsZero)
            {
                y.PerpendicularTo(x);
            }

            if (!x.Unitize())
            {
                return(Rectangle3d.Unset);
            }
            if (!y.Unitize())
            {
                return(Rectangle3d.Unset);
            }

            Rectangle3d rc = new Rectangle3d();

            rc.m_plane = new Plane(pts[0], x, y);
            rc.m_x     = new Interval(0, 0.5 * (AB.Length + DC.Length));
            rc.m_y     = new Interval(0, 0.5 * (AD.Length + BC.Length));

            ComputeDeviation(rc, pts, out deviation, out angleDeviation);
            return(rc);
        }
Beispiel #9
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]] = 0;
                    //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 (StrutIndices.Count ==1) 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();

                        double KnuckleOffset = ND * 0.5;
                        EndMesh.Vertices.Add(Nodes[NodeIdx] + (Knuckles[0] * KnuckleOffset));

                        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);
                    ExoTools.NormaliseMesh(ref StrutMesh);
                    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();

            Mesh[] OutMeshes = Hulls.SplitDisjointPieces();
            for (int SplitMesh = 0; SplitMesh < OutMeshes.Length; SplitMesh++) { ExoTools.NormaliseMesh(ref OutMeshes[SplitMesh]); }

            //ExoTools.NormaliseMesh(ref Hulls);

            DA.SetDataList(0, OutMeshes);
        }