//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()); }
/// <summary> /// Attempts to fit a sphere to a collection of points. /// </summary> /// <param name="points">Points to fit. The collection must contain at least two points.</param> /// <returns>The Sphere that best approximates the points or Sphere.Unset on failure.</returns> public static Sphere FitSphereToPoints(System.Collections.Generic.IEnumerable<Point3d> points) { if (points == null) { throw new ArgumentNullException("points"); } Rhino.Collections.Point3dList pts = new Rhino.Collections.Point3dList(points); if (pts.Count < 2) { return Sphere.Unset; } Plane plane; if (Plane.FitPlaneToPoints(points, out plane) == PlaneFitResult.Failure) { return Sphere.Unset; } Point3d meanP = new Point3d(0, 0, 0); for (int i = 0; i < pts.Count; i++) { meanP += pts[i]; } meanP /= pts.Count; Point3d center = meanP; double radius = -1; for (int k = 0; k < 2048; k++) { double meanL = 0.0; Vector3d meanD = new Vector3d(0, 0, 0); Point3d current = center; for (int i = 0; i < pts.Count; i++) { Vector3d diff = pts[i] - center; double length = diff.Length; if (length > RhinoMath.SqrtEpsilon) { meanL += length; meanD -= (diff / length); } } meanL /= pts.Count; meanD /= pts.Count; center = meanP + (meanD * meanL); radius = meanL; if (center.DistanceTo(current) < RhinoMath.SqrtEpsilon) { break; } } plane.Origin = center; return new Sphere(plane, radius); }
/// <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; }
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; }
private static void ComputeDeviation(Rectangle3d rec, Rhino.Collections.Point3dList pts, out double dev, out double angdev) { dev = double.MaxValue; for (int i = 0; i < 4; i++) { dev = Math.Min(dev, rec.Corner(i).DistanceTo(pts[i])); } angdev = double.MaxValue; for (int i = 0; i < 4; i++) { int j = (i == 3) ? 0 : i + 1; Vector3d re = rec.Corner(i) - rec.Corner(j); Vector3d pe = pts[i] - pts[j]; double ad = Vector3d.VectorAngle(re, pe); if (RhinoMath.IsValidDouble(ad)) { angdev = Math.Min(angdev, ad); } } }
/// <summary> /// Use ///private void RunScript(List<Point3d> pts, double D, ref object A, ref object B){ ///Point3d VP = new Point3d(0, 0, 1000000); ///A = PointCloudNormals(pts, VP, D); ///B = pts.FindAll(VT => VT.DistanceTo(pts[10]) < D);} /// </summary> /// <param name="points"></param> /// <param name="VP"></param> /// <param name="D"></param> /// <returns></returns> public static Vector3d[] PointCloudNormalsByViewPoint(List <Point3d> points, Point3d VP, double D) { Vector3d[] Normals = new Vector3d[points.Count]; Rhino.Collections.Point3dList pts = new Rhino.Collections.Point3dList(points); double Dev = 0.01; double squaredD = D * D; int i = 0; foreach (Point3d point in pts) { dynamic nei = pts.FindAll(V => V.DistanceToSquared(point) < squaredD); Plane NP = Plane.Unset; Plane.FitPlaneToPoints(nei, out NP, out Dev); int sign = (NP.Normal * (VP - point) > 0) ? 1 : -1; Normals[i++] = (sign * NP.Normal); } return(Normals); }
/// <summary> /// Attempts to fit a sphere to a collection of points. /// </summary> /// <param name="points">Points to fit. The collection must contain at least two points.</param> /// <returns>The Sphere that best approximates the points or Sphere.Unset on failure.</returns> /// <since>5.0</since> public static Sphere FitSphereToPoints(System.Collections.Generic.IEnumerable <Point3d> points) { if (points == null) { throw new ArgumentNullException("points"); } Rhino.Collections.Point3dList pts = new Rhino.Collections.Point3dList(points); if (pts.Count < 2) { return(Sphere.Unset); } Plane plane; if (Plane.FitPlaneToPoints(points, out plane) == PlaneFitResult.Failure) { return(Sphere.Unset); } Point3d meanP = new Point3d(0, 0, 0); for (int i = 0; i < pts.Count; i++) { meanP += pts[i]; } meanP /= pts.Count; Point3d center = meanP; double radius = -1; for (int k = 0; k < 2048; k++) { double meanL = 0.0; Vector3d meanD = new Vector3d(0, 0, 0); Point3d current = center; for (int i = 0; i < pts.Count; i++) { Vector3d diff = pts[i] - center; double length = diff.Length; if (length > RhinoMath.SqrtEpsilon) { meanL += length; meanD -= (diff / length); } } meanL /= pts.Count; meanD /= pts.Count; center = meanP + (meanD * meanL); radius = meanL; if (center.DistanceTo(current) < RhinoMath.SqrtEpsilon) { break; } } plane.Origin = center; return(new Sphere(plane, radius)); }
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); } }
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); }
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); } }
//public static Point3d[] RaySurfaces(Ray3d ray, IEnumerable<Surface> surfaces, int maxReflections) //{ // if (maxReflections < 1 || maxReflections > 1000) // throw new ArgumentException("maxReflections must be between 1-1000"); // Rhino.Runtime.INTERNAL_GeometryArray srfs = new Rhino.Runtime.INTERNAL_GeometryArray(surfaces); // IntPtr pSrfs = srfs.ConstPointer(); // Point3d[] rc = null; // using (Rhino.Runtime.InteropWrappers.SimpleArrayPoint3d points = new Rhino.Runtime.InteropWrappers.SimpleArrayPoint3d()) // { // IntPtr pPoints = points.NonConstPointer(); // int count = UnsafeNativeMethods.ON_RayShooter_MultiSurface(ray.Position, ray.Direction, pSrfs, pPoints, maxReflections); // if (count > 0) rc = points.ToArray(); // } // srfs.Dispose(); // return rc; //} #endregion #if RHINO_SDK /// <summary> /// Projects points onto meshes. /// </summary> /// <param name="meshes">the meshes to project on to.</param> /// <param name="points">the points to project.</param> /// <param name="direction">the direction to project.</param> /// <param name="tolerance"> /// Projection tolerances used for culling close points and for line-mesh intersection. /// </param> /// <returns> /// Array of projected points, or null in case of any error or invalid input. /// </returns> public static Point3d[] ProjectPointsToMeshes(IEnumerable<Mesh> meshes, IEnumerable<Point3d> points, Vector3d direction, double tolerance) { Point3d[] rc = null; if (meshes != null && points != null) { Rhino.Runtime.InteropWrappers.SimpleArrayMeshPointer mesh_array = new Rhino.Runtime.InteropWrappers.SimpleArrayMeshPointer(); foreach (Mesh mesh in meshes) mesh_array.Add(mesh, true); Rhino.Collections.Point3dList inputpoints = new Rhino.Collections.Point3dList(points); if (inputpoints.Count > 0) { IntPtr pConstMeshArray = mesh_array.ConstPointer(); using (Rhino.Runtime.InteropWrappers.SimpleArrayPoint3d output = new Rhino.Runtime.InteropWrappers.SimpleArrayPoint3d()) { IntPtr pOutput = output.NonConstPointer(); if (UnsafeNativeMethods.RHC_RhinoProjectPointsToMeshes(pConstMeshArray, direction, tolerance, inputpoints.Count, inputpoints.m_items, pOutput)) rc = output.ToArray(); } } } return rc; }
/// <summary> /// Projects points onto breps. /// </summary> /// <param name="breps">The breps projection targets.</param> /// <param name="points">The points to project.</param> /// <param name="direction">The direction to project.</param> /// <param name="tolerance">The tolerance used for intersections.</param> /// <returns> /// Array of projected points, or null in case of any error or invalid input. /// </returns> /// <example> /// <code source='examples\vbnet\ex_projectpointstobreps.vb' lang='vbnet'/> /// <code source='examples\cs\ex_projectpointstobreps.cs' lang='cs'/> /// <code source='examples\py\ex_projectpointstobreps.py' lang='py'/> /// </example> public static Point3d[] ProjectPointsToBreps(IEnumerable<Brep> breps, IEnumerable<Point3d> points, Vector3d direction, double tolerance) { Point3d[] rc = null; if (breps != null && points != null) { Rhino.Runtime.InteropWrappers.SimpleArrayBrepPointer brep_array = new Rhino.Runtime.InteropWrappers.SimpleArrayBrepPointer(); foreach (Brep brep in breps) brep_array.Add(brep, true); Rhino.Collections.Point3dList inputpoints = new Rhino.Collections.Point3dList(points); if (inputpoints.Count > 0) { IntPtr pConstBrepArray = brep_array.ConstPointer(); using (Runtime.InteropWrappers.SimpleArrayPoint3d output = new Rhino.Runtime.InteropWrappers.SimpleArrayPoint3d()) { IntPtr pOutput = output.NonConstPointer(); if (UnsafeNativeMethods.RHC_RhinoProjectPointsToBreps(pConstBrepArray, direction, tolerance, inputpoints.Count, inputpoints.m_items, pOutput)) rc = output.ToArray(); } } } 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); }
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); }
public static void VertexAdd(ref Mesh Msh, Plane Pln, double S, double R, bool Affix, Rhino.Collections.Point3dList Pts, Mesh AffixMesh) { for (int I = 1; I <= S; I++) { double Angle = ((double)I / S) * 2.0 * Math.PI; if (Affix) { Msh.Vertices.Add(AffixMesh.Vertices[Pts.ClosestIndex(Pln.PointAt(Math.Cos(Angle) * R, Math.Sin(Angle) * R, 0))]); } else { Msh.Vertices.Add(Pln.PointAt(Math.Cos(Angle) * R, Math.Sin(Angle) * R, 0)); } } return; }