protected List <List <Segment2d> > ComputeSegments(GeneralPolygon2d poly, SegmentSet2d polyCache) { List <List <Segment2d> > PerRaySpans = new List <List <Segment2d> >(); double angleRad = AngleDeg * MathUtil.Deg2Rad; Vector2d dir = new Vector2d(Math.Cos(angleRad), Math.Sin(angleRad)); // compute projection span along axis Vector2d axis = dir.Perp; Interval1d axisInterval = Interval1d.Empty; Interval1d dirInterval = Interval1d.Empty; foreach (Vector2d v in poly.Outer.Vertices) { dirInterval.Contain(v.Dot(dir)); axisInterval.Contain(v.Dot(axis)); } // [TODO] also check holes? or assume they are contained? dirInterval.a -= 10 * ToolWidth; dirInterval.b += 10 * ToolWidth; double extent = dirInterval.Length; axisInterval.a += ToolWidth * 0.1 + PathShift; axisInterval.b -= ToolWidth * 0.1; if (axisInterval.b < axisInterval.a) { return(PerRaySpans); // [RMS] is this right? I guess so. interval is too small to fill? } Vector2d startCorner = axisInterval.a * axis + dirInterval.a * dir; double range = axisInterval.Length; int N = (int)(range / PathSpacing); for (int ti = 0; ti <= N; ++ti) { double t = (double)ti / (double)N; Vector2d o = startCorner + (t * range) * axis; Segment2d ray = new Segment2d(o, o + extent * dir); List <Segment2d> spans = compute_polygon_ray_spans(poly, ray, startCorner, axis, t, polyCache); PerRaySpans.Add(spans); } return(PerRaySpans); }
/// <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); }
/// <summary> /// shoot parallel set of 2D rays at input polygon, and find portions /// of rays that are inside the polygon (we call these "spans"). These /// are inserted into the polygon, resulting in a non-manifold 2D graph. /// </summary> protected DGraph2 ComputeSpanGraph(GeneralPolygon2d poly) { double angleRad = AngleDeg * MathUtil.Deg2Rad; Vector2d dir = new Vector2d(Math.Cos(angleRad), Math.Sin(angleRad)); // compute projection span along axis Vector2d axis = dir.Perp; Interval1d axisInterval = Interval1d.Empty; Interval1d dirInterval = Interval1d.Empty; foreach (Vector2d v in poly.Outer.Vertices) { dirInterval.Contain(v.Dot(dir)); axisInterval.Contain(v.Dot(axis)); } // [TODO] also check holes? or assume they are contained? should be // classified as outside by winding check anyway... // construct interval we will step along to shoot parallel rays dirInterval.a -= 10 * ToolWidth; dirInterval.b += 10 * ToolWidth; double extent = dirInterval.Length; // nudge in a very tiny amount so that if poly is a rectangle, first // line is not directly on boundary axisInterval.a += ToolWidth * 0.01; axisInterval.b -= ToolWidth * 0.01; axisInterval.a -= PathShift; if (axisInterval.b < axisInterval.a) { return(null); // [RMS] is this right? I guess so. interval is too small to fill? } // If we are doing a dense fill, we want to pack as tightly as possible. // But if we are doing a sparse fill, then we want layers to stack. // So in that case, snap the interval to increments of the spacing // (does this work?) bool bIsSparse = (PathSpacing > ToolWidth * 2); if (bIsSparse) { // snap axisInterval.a to grid so that layers are aligned double snapped_a = Snapping.SnapToIncrement(axisInterval.a, PathSpacing); if (snapped_a > axisInterval.a) { snapped_a -= PathSpacing; } axisInterval.a = snapped_a; } Vector2d startCorner = axisInterval.a * axis + dirInterval.a * dir; double range = axisInterval.Length; int N = (int)(range / PathSpacing) + 1; // nudge spacing so that we exactly fill the available space double use_spacing = PathSpacing; if (bIsSparse == false && AdjustSpacingToMaximizeFill) { int nn = (int)(range / use_spacing); use_spacing = range / (double)nn; N = (int)(range / use_spacing) + 1; } DGraph2 graph = new DGraph2(); graph.AppendPolygon(poly); GraphSplitter2d splitter = new GraphSplitter2d(graph); splitter.InsideTestF = poly.Contains; // insert sequential rays for (int ti = 0; ti <= N; ++ti) { Vector2d o = startCorner + (double)ti * use_spacing * axis; Line2d ray = new Line2d(o, dir); splitter.InsertLine(ray, ti); } return(graph); }
public override DeformInfo Apply(Frame3f vNextPos) { Interval1d edgeRangeSqr = Interval1d.Empty; int N = Curve.VertexCount; if (N > NewV.Size) { NewV.resize(N); } if (N > ModifiedV.Length) { ModifiedV = new BitArray(2 * N); } // clear modified ModifiedV.SetAll(false); bool bSmooth = (SmoothAlpha > 0 && SmoothIterations > 0); double r2 = Radius * Radius; // deform pass if (DeformF != null) { for (int i = 0; i < N; ++i) { Vector3D v = Curve[i]; double d2 = (v - vPreviousPos.Origin).LengthSquared; if (d2 < r2) { double t = WeightFunc(Math.Sqrt(d2), Radius); Vector3D vNew = DeformF(i, t); if (bSmooth == false) { if (i > 0) { edgeRangeSqr.Contain(vNew.DistanceSquared(Curve[i - 1])); } if (i < N - 1) { edgeRangeSqr.Contain(vNew.DistanceSquared(Curve[i + 1])); } } NewV[i] = vNew; ModifiedV[i] = true; } } } else { // anything? } // smooth pass if (bSmooth) { for (int j = 0; j < SmoothIterations; ++j) { int iStart = (Curve.Closed) ? 0 : 1; int iEnd = (Curve.Closed) ? N : N - 1; for (int i = iStart; i < iEnd; ++i) { Vector3D v = (ModifiedV[i]) ? NewV[i] : Curve[i]; double d2 = (v - vPreviousPos.Origin).LengthSquared; if (ModifiedV[i] || d2 < r2) // always smooth any modified verts { double a = SmoothAlpha * WeightFunc(Math.Sqrt(d2), Radius); int iPrev = (i == 0) ? N - 1 : i - 1; int iNext = (i + 1) % N; Vector3D vPrev = (ModifiedV[iPrev]) ? NewV[iPrev] : Curve[iPrev]; Vector3D vNext = (ModifiedV[iNext]) ? NewV[iNext] : Curve[iNext]; Vector3D c = (vPrev + vNext) * 0.5f; NewV[i] = (1 - a) * v + (a) * c; ModifiedV[i] = true; if (i > 0) { edgeRangeSqr.Contain(NewV[i].DistanceSquared(Curve[i - 1])); } if (i < N - 1) { edgeRangeSqr.Contain(NewV[i].DistanceSquared(Curve[i + 1])); } } } } } // bake for (int i = 0; i < N; ++i) { if (ModifiedV[i]) { Curve[i] = NewV[i]; } } return(new DeformInfo() { minEdgeLenSqr = edgeRangeSqr.a, maxEdgeLenSqr = edgeRangeSqr.b }); }
/// <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); }
/// <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 ObserveValue(float value) { interval.Contain(value); RangeMin = (float)interval.a; RangeMax = (float)interval.b; }