public Column(Mesh mesh, double diameter, double height, Interval region) { double width = Util.GetWidth(diameter, height); double offset = width * 0.5; const double fillSize = 400; const double shortenDist = 10; string _text = null; Progress("Contouring"); var contours = Contouring(mesh); Progress("Cleaning"); var cleanContours = Cleaning(contours); Progress("FixCantilevers"); cleanContours = FixCantilevers(cleanContours); Progress("Toolpathing"); var toolpath = Toolpathing(cleanContours); Progress("Piping"); var pipes = Piping(toolpath); Progress("Skin"); var skin = new Mesh(); for (int i = 0; i < pipes.Length; i++) { var pipe = pipes[i].Last(); skin.Append(pipe); } Progress("end"); Contours = contours; Layers = toolpath; //new Polyline[0][]; Pipes = pipes; //new Mesh[0][]; Skin = skin; Polyline[] Contouring(Mesh m) { Polyline[] outPolylines; var contour = Cache.Read <Curve>("contours")?.ToPolyline(); if (contour is null) { outPolylines = Slicer.Create(m, height, region) .Select(c => c.MaxBy(p => p.Length).First()) .ToArray(); Cache.Write("contours", new PolylineCurve(outPolylines[0])); } else { outPolylines = Enumerable.Repeat(contour, 1).ToArray(); } return(outPolylines); } Polyline[] Cleaning(Polyline[] inPolylines) { var outPolylines = new Polyline[inPolylines.Length]; Parallel.ForEach(Partitioner.Create(0, inPolylines.Length), range => { for (int i = range.Item1; i < range.Item2; i++) { var polyline = inPolylines[i]; polyline = Shorten(polyline, shortenDist); //polyline = BallPivot.Create(polyline, width * 0.2); // simplfy outPolylines[i] = polyline; } }); Polyline Shorten(Polyline contour, double resolution) { var maxDist = resolution * 1; var maxDistSq = maxDist * maxDist; var pivot = BallPivot.Create(contour, resolution); var outCurve = new Polyline(contour.Where(p => pivot.ClosestPoint(p).DistanceToSquared(p) < maxDistSq)); if (!outCurve.IsClosed) { outCurve.Add(outCurve[0]); } return(outCurve); } return(outPolylines); } Mesh[][] Piping(Polyline[][] inPolylines) { var outMeshes = new Mesh[inPolylines.Length][]; if (inPolylines.Length == 0) { return(outMeshes); } Parallel.ForEach(Partitioner.Create(0, inPolylines.Length), range => { for (int i = range.Item1; i < range.Item2; i++) { //var pls = inPolylines[i].Select(p => p.ToPolyline(0.01, PI * 0.01, 1, double.MaxValue).ToPolyline()); outMeshes[i] = inPolylines[i].Select(p => MeshPipe.MeshFlatPolyline(p, width, height, width * 0.5, 5)).ToArray(); } }); return(outMeshes); } Polyline[][] Toolpathing(Polyline[] inPolylines) { var outPolylines = new Polyline[inPolylines.Length][]; Parallel.ForEach(Partitioner.Create(0, inPolylines.Length), range => { for (int i = range.Item1; i < range.Item2; i++) { // var pl = inPolylines[i]; var outerSkin = inPolylines[i]; //var outerSkin = Geometry.Region.Offset(pl, -offset); var innerSkin = Geometry.Region.Offset(outerSkin, -offset * 2); var outPolyline = new Polyline[] { outerSkin, innerSkin }; outPolylines[i] = outPolyline.Select(p => Clean(p, offset)).ToArray(); } }); // Progress("Filling"); var layers = PolygonFill.Square(outPolylines.Select(l => l[1]).ToArray(), fillSize, offset); for (int i = 0; i < inPolylines.Length; i++) { outPolylines[i] = MoreEnumerable.Append(layers[i], outPolylines[i][0]).ToArray(); } return(outPolylines); Polyline Clean(Polyline contour, double resolution) { var clean = new Polyline(contour); var removed = clean.ReduceSegments(resolution * 0.02); removed = clean.CollapseShortSegments(2); //var nurbs = Curve.CreateInterpolatedCurve(clean, 3); //Document.Debug.Bake(nurbs, Color.Red); //var curve = nurbs.ToArcsAndLines(resolution * 0.1, PI * 0.1, width * 0.5, double.MaxValue); // Document.Debug.Bake(curve, Color.Blue); //var pl = curve.ToPolyline(0.01, PI * 0.01, 0.1, double.MaxValue); //Document.Debug.Bake(pl, Color.Green); return(clean); } } Polyline[] FixCantilevers(Polyline[] polylines) { Parallel.ForEach(Partitioner.Create(0, polylines.Length), range => { for (int i = range.Item1; i < range.Item2; i++) { var contour = Geometry.Region.Offset(polylines[i], -offset); var removed = contour.ReduceSegments(width * 0.02); removed = contour.CollapseShortSegments(2); polylines[i] = contour; } }); double maxSeparation = width * 0.75; double maxSeparationSq = maxSeparation * maxSeparation; for (int i = 1; i < polylines.Length; i++) { var contour = polylines[i]; var prev = polylines[i - 1]; for (int j = 0; j < contour.Count; j++) { var p = contour[j]; var closest = prev.ClosestPointFast(p); var v = closest - p; v.Z = 0; var lengthSq = v.SquareLength; if (lengthSq > maxSeparationSq) { var length = Sqrt(lengthSq); var move = v * ((length - maxSeparation) / length); p += move; contour[j] = p; } } } return(polylines); } void Progress(string text) { if (text != "Contouring") { Rhino.RhinoApp.WriteLine($"{ _text}"); } StatusBar.HideProgressMeter(); if (text == "end") { return; } _text = text; StatusBar.ShowProgressMeter(0, 4, $"{text}...", true, true); if (text != "Contouring") { StatusBar.UpdateProgressMeter(1, true); } } }