protected virtual void precompute_shells() { int nLayers = Slices.Count; LayerShells = new List <ShellsFillPolygon> [nLayers]; gParallel.ForEach(Interval1i.Range(nLayers), (layeri) => { PlanarSlice slice = Slices[layeri]; LayerShells[layeri] = new List <ShellsFillPolygon>(); List <GeneralPolygon2d> solids = slice.Solids; foreach (GeneralPolygon2d shape in solids) { ShellsFillPolygon shells_gen = new ShellsFillPolygon(shape); shells_gen.PathSpacing = Settings.SolidFillPathSpacingMM(); shells_gen.ToolWidth = Settings.Machine.NozzleDiamMM; shells_gen.Layers = Settings.Shells; shells_gen.InsetInnerPolygons = false; shells_gen.Compute(); LayerShells[layeri].Add(shells_gen); if (slice.Tags.Has(shape)) { ShellTags.Add(shells_gen, slice.Tags.Get(shape)); } } }); }
protected override void add_cavity_polygons(PlanarSlice slice, List <GeneralPolygon2d> polygons, PrintMeshOptions options) { slice.AddCavityPolygons(polygons); if (options.Extended != null && options.Extended is ExtendedPrintMeshOptions) { ExtendedPrintMeshOptions ext = options.Extended as ExtendedPrintMeshOptions; if (slice is PlanarSlicePro) { PlanarSlicePro sp = slice as PlanarSlicePro; if (ext.ClearanceXY != 0) { foreach (var poly in polygons) { sp.Cavity_Clearances.Add(poly, ext.ClearanceXY); } } if (ext.OffsetXY != 0) { foreach (var poly in polygons) { sp.Cavity_Offsets.Add(poly, ext.OffsetXY); } } } } }
/// <summary> /// Add explicit support points for any small floating polygons. /// These can be used at toolpathing time to ensure support for /// such areas, which otherwise might be lost (or insufficiently /// supported) by the standard techniques to detect support regions. /// </summary> public void AddMinZTipSupportPoints(double tipDiamThresh = 2.0, int nExtraLayers = 0) { List <Vector3d> tips = new List <Vector3d>(); SpinLock tiplock = new SpinLock(); int N = Slices.Count; gParallel.ForEach(Interval1i.FromToInclusive(1, N - 1), (li) => { PlanarSlice slice = Slices[li]; PlanarSlice prev = Slices[li - 1]; foreach (GeneralPolygon2d poly in slice.InputSolids) { AxisAlignedBox2d bounds = poly.Bounds; if (bounds.MaxDim > tipDiamThresh) { continue; } Vector2d c = bounds.Center; bool contained = false; foreach (var poly2 in prev.InputSolids) { if (poly2.Contains(c)) { contained = true; break; } } if (contained) { continue; } bool entered = false; tiplock.Enter(ref entered); tips.Add(new Vector3d(c.x, c.y, li)); tiplock.Exit(); } }); foreach (var tip in tips) { int layer_i = (int)tip.z; int add_to = Math.Min(N - 1, layer_i + nExtraLayers); for (int i = layer_i; i < add_to; ++i) { Slices[i].InputSupportPoints.Add(tip.xy); } } }
/// <summary> /// Format is: /// [num_slices] /// [slice0_z] /// [num_polys_in_slice_0] /// [x0 y0 x1 y1 x2 y3 ...] /// [x0 y0 x1 y1 ... ] /// [slice1_z] /// [num_polys_in_slice_1] /// ... /// </summary> public void ReadSimpleSliceFormat(TextReader reader) { PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.SortPolygons; options.TrustOrientations = false; char[] splitchars = new char[] { ' ' }; int nSlices = int.Parse(reader.ReadLine()); PlanarComplex[] layer_complexes = new PlanarComplex[nSlices]; for (int si = 0; si < nSlices; ++si) { PlanarSlice slice = new PlanarSlice(); slice.Z = double.Parse(reader.ReadLine()); PlanarComplex complex = new PlanarComplex(); int nPolys = int.Parse(reader.ReadLine()); for (int pi = 0; pi < nPolys; pi++) { string[] stringValues = reader.ReadLine().Split(splitchars, StringSplitOptions.RemoveEmptyEntries); double[] values = Array.ConvertAll(stringValues, Double.Parse); Polygon2d poly = new Polygon2d(values); if (poly.VertexCount < 3) { continue; } complex.Add(poly); } layer_complexes[si] = complex; //// this could be done in separate thread... //var solidInfo = complex.FindSolidRegions(options); //slice.InputSolids = solidInfo.Polygons; //slice.Resolve(); Slices.Add(slice); } gParallel.ForEach(Interval1i.Range(nSlices), (si) => { var solidInfo = layer_complexes[si].FindSolidRegions(options); Slices[si].InputSolids = solidInfo.Polygons; Slices[si].Resolve(); }); }
protected virtual void add_solid_polygons(PlanarSlice slice, List <GeneralPolygon2d> polygons, PrintMeshOptions options) { slice.AddPolygons(polygons); if (options.ClearanceXY != 0) { foreach (var poly in polygons) { slice.Clearances.Add(poly, options.ClearanceXY); } } if (options.OffsetXY != 0) { foreach (var poly in polygons) { slice.Offsets.Add(poly, options.OffsetXY); } } }
public DMesh3 Make3DTubes(Interval1i layer_range, double merge_tol, double tube_radius) { Polygon2d tube_profile = Polygon2d.MakeCircle(tube_radius, 8); Frame3f frame = Frame3f.Identity; DMesh3 full_mesh = new DMesh3(); foreach (int layer_i in layer_range) { PlanarSlice slice = Slices[layer_i]; frame.Origin = new Vector3f(0, 0, slice.Z); foreach (GeneralPolygon2d gpoly in slice.Solids) { List <Polygon2d> polys = new List <Polygon2d>() { gpoly.Outer }; polys.AddRange(gpoly.Holes); foreach (Polygon2d poly in polys) { Polygon2d simpPoly = new Polygon2d(poly); simpPoly.Simplify(merge_tol, 0.01, true); if (simpPoly.VertexCount < 3) { Util.gBreakToDebugger(); } TubeGenerator tubegen = new TubeGenerator(simpPoly, frame, tube_profile) { NoSharedVertices = true }; DMesh3 tubeMesh = tubegen.Generate().MakeDMesh(); MeshEditor.Append(full_mesh, tubeMesh); } } } return(full_mesh); }
protected virtual void add_crop_region_polygons(PlanarSlice slice, List <GeneralPolygon2d> polygons, PrintMeshOptions options) { slice.AddCropRegions(polygons); }
protected virtual void add_support_polygons(PlanarSlice slice, List <GeneralPolygon2d> polygons, PrintMeshOptions options) { slice.AddSupportPolygons(polygons); }
/// <summary> /// Slice the meshes and return the slice stack. /// </summary> public PlanarSliceStack Compute() { if (Meshes.Count == 0) { return(new PlanarSliceStack()); } Interval1d zrange = Interval1d.Empty; foreach (var meshinfo in Meshes) { zrange.Contain(meshinfo.bounds.Min.z); zrange.Contain(meshinfo.bounds.Max.z); } if (SetMinZValue != double.MinValue) { zrange.a = SetMinZValue; } // construct layers List <PlanarSlice> slice_list = new List <PlanarSlice>(); double cur_layer_z = zrange.a; int layer_i = 0; while (cur_layer_z < zrange.b) { double layer_height = get_layer_height(layer_i); double z = cur_layer_z; Interval1d zspan = new Interval1d(z, z + layer_height); if (SliceLocation == SliceLocations.EpsilonBase) { z += 0.01 * layer_height; } else if (SliceLocation == SliceLocations.MidLine) { z += 0.5 * layer_height; } PlanarSlice slice = SliceFactoryF(zspan, z, layer_i); slice.EmbeddedPathWidth = OpenPathDefaultWidthMM; slice_list.Add(slice); layer_i++; cur_layer_z += layer_height; } int NH = slice_list.Count; if (NH > MaxLayerCount) { throw new Exception("MeshPlanarSlicer.Compute: exceeded layer limit. Increase .MaxLayerCount."); } PlanarSlice[] slices = slice_list.ToArray(); // determine if we have crop objects bool have_crop_objects = false; foreach (var mesh in Meshes) { if (mesh.options.IsCropRegion) { have_crop_objects = true; } } // assume Resolve() takes 2x as long as meshes... TotalCompute = (Meshes.Count * NH) + (2 * NH); Progress = 0; // compute slices separately for each mesh for (int mi = 0; mi < Meshes.Count; ++mi) { if (Cancelled()) { break; } DMesh3 mesh = Meshes[mi].mesh; PrintMeshOptions mesh_options = Meshes[mi].options; // [TODO] should we hang on to this spatial? or should it be part of assembly? DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); AxisAlignedBox3d bounds = Meshes[mi].bounds; bool is_cavity = mesh_options.IsCavity; bool is_crop = mesh_options.IsCropRegion; bool is_support = mesh_options.IsSupport; bool is_closed = (mesh_options.IsOpen) ? false : mesh.IsClosed(); var useOpenMode = (mesh_options.OpenPathMode == OpenPathsModes.Default) ? DefaultOpenPathMode : mesh_options.OpenPathMode; // each layer is independent so we can do in parallel gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } double z = slices[i].Z; if (z < bounds.Min.z || z > bounds.Max.z) { return; } // compute cut Polygon2d[] polys; PolyLine2d[] paths; compute_plane_curves(mesh, spatial, z, is_closed, out polys, out paths); // if we didn't hit anything, try again with jittered plane // [TODO] this could be better... if ((is_closed && polys.Length == 0) || (is_closed == false && polys.Length == 0 && paths.Length == 0)) { double jitterz = slices[i].LayerZSpan.Interpolate(0.75); compute_plane_curves(mesh, spatial, jitterz, is_closed, out polys, out paths); } if (is_closed) { // construct planar complex and "solids" // (ie outer polys and nested holes) PlanarComplex complex = new PlanarComplex(); foreach (Polygon2d poly in polys) { complex.Add(poly); } PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.Default; options.WantCurveSolids = false; options.SimplifyDeviationTolerance = 0.001; options.TrustOrientations = true; options.AllowOverlappingHoles = true; PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(options); List <GeneralPolygon2d> solid_polygons = ApplyValidRegions(solids.Polygons); if (is_support) { add_support_polygons(slices[i], solid_polygons, mesh_options); } else if (is_cavity) { add_cavity_polygons(slices[i], solid_polygons, mesh_options); } else if (is_crop) { add_crop_region_polygons(slices[i], solid_polygons, mesh_options); } else { add_solid_polygons(slices[i], solid_polygons, mesh_options); } } else if (useOpenMode != OpenPathsModes.Ignored) { // [TODO] // - does not really handle clipped polygons properly, there will be an extra break somewhere... List <PolyLine2d> all_paths = new List <PolyLine2d>(paths); foreach (Polygon2d poly in polys) { all_paths.Add(new PolyLine2d(poly, true)); } List <PolyLine2d> open_polylines = ApplyValidRegions(all_paths); foreach (PolyLine2d pline in open_polylines) { if (useOpenMode == OpenPathsModes.Embedded) { slices[i].AddEmbeddedPath(pline); } else { slices[i].AddClippedPath(pline); } } } Interlocked.Increment(ref Progress); }); // end of parallel.foreach } // end mesh iter // resolve planar intersections, etc gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } if (have_crop_objects && slices[i].InputCropRegions.Count == 0) { // don't resolve, we have fully cropped this layer } else { slices[i].Resolve(); } Interlocked.Add(ref Progress, 2); }); // discard spurious empty slices int last = slices.Length - 1; while (slices[last].IsEmpty && last > 0) { last--; } int first = 0; if (DiscardEmptyBaseSlices || have_crop_objects) { while (slices[first].IsEmpty && first < slices.Length) { first++; } } PlanarSliceStack stack = SliceStackFactoryF(); for (int k = first; k <= last; ++k) { stack.Add(slices[k]); } if (SupportMinZTips) { stack.AddMinZTipSupportPoints(MinZTipMaxDiam, MinZTipExtraLayers); } return(stack); }
protected virtual void add_cavity_polygons(PlanarSlice slice, List <GeneralPolygon2d> polygons, PrintMeshOptions options) { slice.AddCavityPolygons(polygons); }
/// <summary> /// Slice the meshes and return the slice stack. /// </summary> public PlanarSliceStack Compute() { if (Meshes.Count == 0) { return(new PlanarSliceStack()); } Interval1d zrange = Interval1d.Empty; foreach (var meshinfo in Meshes) { zrange.Contain(meshinfo.bounds.Min.z); zrange.Contain(meshinfo.bounds.Max.z); } if (SetMinZValue != double.MinValue) { zrange.a = SetMinZValue; } int nLayers = (int)(zrange.Length / LayerHeightMM); if (nLayers > MaxLayerCount) { throw new Exception("MeshPlanarSlicer.Compute: exceeded layer limit. Increase .MaxLayerCount."); } // make list of slice heights (could be irregular) List <double> heights = new List <double>(); for (int i = 0; i < nLayers + 1; ++i) { double t = zrange.a + (double)i * LayerHeightMM; if (SliceLocation == SliceLocations.EpsilonBase) { t += 0.01 * LayerHeightMM; } else if (SliceLocation == SliceLocations.MidLine) { t += 0.5 * LayerHeightMM; } heights.Add(t); } int NH = heights.Count; // process each *slice* in parallel PlanarSlice[] slices = new PlanarSlice[NH]; for (int i = 0; i < NH; ++i) { slices[i] = SliceFactoryF(heights[i], i); slices[i].EmbeddedPathWidth = OpenPathDefaultWidthMM; } // assume Resolve() takes 2x as long as meshes... TotalCompute = (Meshes.Count * NH) + (2 * NH); Progress = 0; // compute slices separately for each mesh for (int mi = 0; mi < Meshes.Count; ++mi) { if (Cancelled()) { break; } DMesh3 mesh = Meshes[mi].mesh; PrintMeshOptions mesh_options = Meshes[mi].options; // [TODO] should we hang on to this spatial? or should it be part of assembly? DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); AxisAlignedBox3d bounds = Meshes[mi].bounds; bool is_cavity = mesh_options.IsCavity; bool is_crop = mesh_options.IsCropRegion; bool is_support = mesh_options.IsSupport; bool is_closed = (mesh_options.IsOpen) ? false : mesh.IsClosed(); var useOpenMode = (mesh_options.OpenPathMode == PrintMeshOptions.OpenPathsModes.Default) ? DefaultOpenPathMode : mesh_options.OpenPathMode; // each layer is independent so we can do in parallel gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } double z = heights[i]; if (z < bounds.Min.z || z > bounds.Max.z) { return; } // compute cut Polygon2d[] polys; PolyLine2d[] paths; compute_plane_curves(mesh, spatial, z, is_closed, out polys, out paths); // if we didn't hit anything, try again with jittered plane // [TODO] this could be better... if ((is_closed && polys.Length == 0) || (is_closed == false && polys.Length == 0 && paths.Length == 0)) { compute_plane_curves(mesh, spatial, z + LayerHeightMM * 0.25, is_closed, out polys, out paths); } if (is_closed) { // construct planar complex and "solids" // (ie outer polys and nested holes) PlanarComplex complex = new PlanarComplex(); foreach (Polygon2d poly in polys) { complex.Add(poly); } PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.Default; options.WantCurveSolids = false; options.SimplifyDeviationTolerance = 0.001; options.TrustOrientations = true; options.AllowOverlappingHoles = true; PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(options); if (is_support) { add_support_polygons(slices[i], solids.Polygons, mesh_options); } else if (is_cavity) { add_cavity_polygons(slices[i], solids.Polygons, mesh_options); } else if (is_crop) { add_crop_region_polygons(slices[i], solids.Polygons, mesh_options); } else { add_solid_polygons(slices[i], solids.Polygons, mesh_options); } } else if (useOpenMode != PrintMeshOptions.OpenPathsModes.Ignored) { foreach (PolyLine2d pline in paths) { if (useOpenMode == PrintMeshOptions.OpenPathsModes.Embedded) { slices[i].AddEmbeddedPath(pline); } else { slices[i].AddClippedPath(pline); } } // [TODO] // - does not really handle clipped polygons properly, there will be an extra break somewhere... foreach (Polygon2d poly in polys) { PolyLine2d pline = new PolyLine2d(poly, true); if (useOpenMode == PrintMeshOptions.OpenPathsModes.Embedded) { slices[i].AddEmbeddedPath(pline); } else { slices[i].AddClippedPath(pline); } } } Interlocked.Increment(ref Progress); }); // end of parallel.foreach } // end mesh iter // resolve planar intersections, etc gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } slices[i].Resolve(); Interlocked.Add(ref Progress, 2); }); // discard spurious empty slices int last = slices.Length - 1; while (slices[last].IsEmpty && last > 0) { last--; } int first = 0; if (DiscardEmptyBaseSlices) { while (slices[first].IsEmpty && first < slices.Length) { first++; } } PlanarSliceStack stack = SliceStackFactoryF(); for (int k = first; k <= last; ++k) { stack.Add(slices[k]); } if (SupportMinZTips) { stack.AddMinZTipSupportPoints(MinZTipMaxDiam, MinZTipExtraLayers); } return(stack); }
protected PlanarSlice ComputeSolidSliceAtZ(double z, bool bCavitySolids) { PlanarSlice temp = new PlanarSlice(); for (int mi = 0; mi < Meshes.Count; ++mi) { if (Cancelled()) { break; } DMesh3 mesh = Meshes[mi].mesh; PrintMeshOptions mesh_options = Meshes[mi].options; // [TODO] should we hang on to this spatial? or should it be part of assembly? DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); AxisAlignedBox3d bounds = Meshes[mi].bounds; bool is_cavity = mesh_options.IsCavity; bool is_crop = mesh_options.IsCropRegion; bool is_support = mesh_options.IsSupport; bool is_closed = (mesh_options.IsOpen) ? false : mesh.IsClosed(); var useOpenMode = (mesh_options.OpenPathMode == PrintMeshOptions.OpenPathsModes.Default) ? DefaultOpenPathMode : mesh_options.OpenPathMode; if (is_crop || is_support) { throw new Exception("Not supported!"); } if (bCavitySolids && is_cavity == false) { continue; } // compute cut Polygon2d[] polys; PolyLine2d[] paths; ComputeSlicePlaneCurves(mesh, spatial, z, is_closed, out polys, out paths); if (is_closed) { // construct planar complex and "solids" // (ie outer polys and nested holes) PlanarComplex complex = new PlanarComplex(); foreach (Polygon2d poly in polys) { complex.Add(poly); } PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.Default; options.WantCurveSolids = false; options.SimplifyDeviationTolerance = 0.001; options.TrustOrientations = true; options.AllowOverlappingHoles = true; PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(options); List <GeneralPolygon2d> solid_polygons = ApplyValidRegions(solids.Polygons); if (is_cavity && bCavitySolids == false) { add_cavity_polygons(temp, solid_polygons, mesh_options); } else { add_solid_polygons(temp, solid_polygons, mesh_options); } } } return(temp); }
/// <summary> /// Slice the meshes and return the slice stack. /// </summary> public Result Compute() { Result result = new Result(); if (Meshes.Count == 0) { return(result); } // find Z interval we want to slice in Interval1d zrange = Interval1d.Empty; foreach (var meshinfo in Meshes) { zrange.Contain(meshinfo.bounds.Min.z); zrange.Contain(meshinfo.bounds.Max.z); } if (SetMinZValue != double.MinValue) { zrange.a = SetMinZValue; } result.TopZ = Math.Round(zrange.b, PrecisionDigits); result.BaseZ = Math.Round(zrange.a, PrecisionDigits); // [TODO] might be able to make better decisions if we took flat regions // into account when constructing initial Z-heights? if we have large flat // region just below Zstep, might make sense to do two smaller Z-steps so we // can exactly hit it?? // construct list of clearing Z-heights List <double> clearingZLayers = new List <double>(); double cur_layer_z = zrange.b; int layer_i = 0; while (cur_layer_z > zrange.a) { double layer_height = get_layer_height(layer_i); cur_layer_z -= layer_height; double z = Math.Round(cur_layer_z, PrecisionDigits); clearingZLayers.Add(z); layer_i++; } if (clearingZLayers.Last() < result.BaseZ) { clearingZLayers[clearingZLayers.Count - 1] = result.BaseZ; } if (clearingZLayers.Last() == clearingZLayers[clearingZLayers.Count - 2]) { clearingZLayers.RemoveAt(clearingZLayers.Count - 1); } // construct layer slices from Z-heights List <PlanarSlice> clearing_slice_list = new List <PlanarSlice>(); layer_i = 0; for (int i = 0; i < clearingZLayers.Count; ++i) { double layer_height = (i == clearingZLayers.Count - 1) ? (result.TopZ - clearingZLayers[i]) : (clearingZLayers[i + 1] - clearingZLayers[i]); double z = clearingZLayers[i]; Interval1d zspan = new Interval1d(z, z + layer_height); if (SliceLocation == SliceLocations.EpsilonBase) { z += 0.001; } PlanarSlice slice = SliceFactoryF(zspan, z, layer_i); clearing_slice_list.Add(slice); layer_i++; } int NH = clearing_slice_list.Count; if (NH > MaxLayerCount) { throw new Exception("MeshPlanarSlicer.Compute: exceeded layer limit. Increase .MaxLayerCount."); } PlanarSlice[] clearing_slices = clearing_slice_list.ToArray(); // assume Resolve() takes 2x as long as meshes... TotalCompute = (Meshes.Count * NH) + (2 * NH); Progress = 0; // compute slices separately for each mesh for (int mi = 0; mi < Meshes.Count; ++mi) { if (Cancelled()) { break; } DMesh3 mesh = Meshes[mi].mesh; PrintMeshOptions mesh_options = Meshes[mi].options; // [TODO] should we hang on to this spatial? or should it be part of assembly? DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); AxisAlignedBox3d bounds = Meshes[mi].bounds; bool is_cavity = mesh_options.IsCavity; bool is_crop = mesh_options.IsCropRegion; bool is_support = mesh_options.IsSupport; bool is_closed = (mesh_options.IsOpen) ? false : mesh.IsClosed(); var useOpenMode = (mesh_options.OpenPathMode == PrintMeshOptions.OpenPathsModes.Default) ? DefaultOpenPathMode : mesh_options.OpenPathMode; if (is_crop || is_support) { throw new Exception("Not supported!"); } // each layer is independent so we can do in parallel gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } double z = clearing_slices[i].Z; if (z < bounds.Min.z || z > bounds.Max.z) { return; } // compute cut Polygon2d[] polys; PolyLine2d[] paths; ComputeSlicePlaneCurves(mesh, spatial, z, is_closed, out polys, out paths); if (is_closed) { // construct planar complex and "solids" // (ie outer polys and nested holes) PlanarComplex complex = new PlanarComplex(); foreach (Polygon2d poly in polys) { complex.Add(poly); } PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.Default; options.WantCurveSolids = false; options.SimplifyDeviationTolerance = 0.001; options.TrustOrientations = true; options.AllowOverlappingHoles = true; PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(options); List <GeneralPolygon2d> solid_polygons = ApplyValidRegions(solids.Polygons); if (is_cavity) { add_cavity_polygons(clearing_slices[i], solid_polygons, mesh_options); } else { if (ExpandStockAmount > 0) { solid_polygons = ClipperUtil.MiterOffset(solid_polygons, ExpandStockAmount); } add_solid_polygons(clearing_slices[i], solid_polygons, mesh_options); } } Interlocked.Increment(ref Progress); }); // end of parallel.foreach } // end mesh iter // resolve planar intersections, etc gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } clearing_slices[i].Resolve(); Interlocked.Add(ref Progress, 2); }); // add to clearing stack result.Clearing = SliceStackFactoryF(); for (int k = 0; k < clearing_slices.Length; ++k) { result.Clearing.Add(clearing_slices[k]); } /* * Horizontal planar regions finishing pass. * First we find all planar horizontal Z-regions big enough to mill. * Then we add slices at the Z's we haven't touched yet. * * Cannot just 'fill' planar regions because we will miss edges that might * be millable. So we grow region and then intersect with full-slice millable area. */ // find set of horizontal flat regions Dictionary <double, List <PlanarRegion> > flat_regions = FindPlanarZRegions(ToolDiameter); if (flat_regions.Count == 0) { goto done_slicing; } // if we have already milled this exact Z-height in clearing pass, then we can skip it List <double> doneZ = new List <double>(); foreach (double z in flat_regions.Keys) { if (clearingZLayers.Contains(z)) { doneZ.Add(z); } } foreach (var z in doneZ) { flat_regions.Remove(z); } // create slice for each layer PlanarSlice[] horz_slices = new PlanarSlice[flat_regions.Count]; List <double> flatZ = new List <double>(flat_regions.Keys); flatZ.Sort(); for (int k = 0; k < horz_slices.Length; ++k) { double z = flatZ[k]; Interval1d zspan = new Interval1d(z, z + LayerHeightMM); horz_slices[k] = SliceFactoryF(zspan, z, k); // compute full millable region slightly above this slice. PlanarSlice clip_slice = ComputeSolidSliceAtZ(z + 0.0001, false); clip_slice.Resolve(); // extract planar polys List <Polygon2d> polys = GetPlanarPolys(flat_regions[z]); PlanarComplex complex = new PlanarComplex(); foreach (Polygon2d poly in polys) { complex.Add(poly); } // convert to planar solids PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.SortPolygons; options.SimplifyDeviationTolerance = 0.001; options.TrustOrientations = true; options.AllowOverlappingHoles = true; PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(options); List <GeneralPolygon2d> solid_polygons = ApplyValidRegions(solids.Polygons); // If planar solid has holes, then when we do inset later, we might lose // too-thin parts. Shrink the holes to avoid this case. //FilterHoles(solid_polygons, 0.55 * ToolDiameter); // ok now we need to expand region and intersect with full region. solid_polygons = ClipperUtil.MiterOffset(solid_polygons, ToolDiameter * 0.5, 0.0001); solid_polygons = ClipperUtil.Intersection(solid_polygons, clip_slice.Solids, 0.0001); // Same idea as above, but if we do after, we keep more of the hole and // hence do less extra clearing. // Also this could then be done at the slicer level instead of here... // (possibly this entire thing should be done at slicer level, except we need clip_slice!) FilterHoles(solid_polygons, 1.1 * ToolDiameter); add_solid_polygons(horz_slices[k], solid_polygons, PrintMeshOptions.Default()); } // resolve planar intersections, etc int NF = horz_slices.Length; gParallel.ForEach(Interval1i.Range(NF), (i) => { if (Cancelled()) { return; } horz_slices[i].Resolve(); Interlocked.Add(ref Progress, 2); }); // add to clearing stack result.HorizontalFinish = SliceStackFactoryF(); for (int k = 0; k < horz_slices.Length; ++k) { result.HorizontalFinish.Add(horz_slices[k]); } done_slicing: return(result); }
public void Add(PlanarSlice slice) { Slices.Add(slice); }