protected virtual List <GeneralPolygon2d> make_solid(GeneralPolygon2d poly, bool bIsSupportSolid) { // always do a self-union of outer polygon. This allows Clipper to clean up many // problems in input polygons, eg self-intersections and other junk GeneralPolygon2d gouter = new GeneralPolygon2d(poly.Outer); List <GeneralPolygon2d> resolvedSolid = ClipperUtil.Union(gouter, gouter, MIN_AREA); // solid may contain overlapping holes. We need to resolve these before continuing, // otherwise those overlapping regions will be filled by Clipper even/odd rules foreach (Polygon2d hole in poly.Holes) { GeneralPolygon2d holePoly = new GeneralPolygon2d(hole); resolvedSolid = ClipperUtil.PolygonBoolean(resolvedSolid, holePoly, ClipperUtil.BooleanOp.Difference, MIN_AREA); } if (bIsSupportSolid == false && Thickened != null) { // Subtract away any clearance solids foreach (var pair in Thickened) { if (pair.Key != poly) { resolvedSolid = ClipperUtil.Difference(resolvedSolid, pair.Value); } } } return(filter_solids(resolvedSolid)); }
public bool Compute() { AxisAlignedBox2d bounds = Polygon.Bounds; ScaleGridIndexer2 index = new ScaleGridIndexer2() { CellSize = TileSize }; Vector2i min = index.ToGrid(bounds.Min) - Vector2i.One; Vector2i max = index.ToGrid(bounds.Max); List <Tile> Tiles = new List <Tile>(); for (int y = min.y; y <= max.y; ++y) { for (int x = min.x; x <= max.x; ++x) { Tile t = new Tile(); t.index = new Vector2i(x, y); Vector2d tile_min = index.FromGrid(t.index); Vector2d tile_max = index.FromGrid(t.index + Vector2i.One); AxisAlignedBox2d tile_box = new AxisAlignedBox2d(tile_min, tile_max); tile_box.Expand(TileOverlap); t.poly = new Polygon2d(new Vector2d[] { tile_box.GetCorner(0), tile_box.GetCorner(1), tile_box.GetCorner(2), tile_box.GetCorner(3) }); Tiles.Add(t); } } gParallel.ForEach(Tiles, (tile) => { tile.regions = ClipperUtil.PolygonBoolean(Polygon, new GeneralPolygon2d(tile.poly), ClipperUtil.BooleanOp.Intersection); }); List <ICurvesFillPolygon> all_fills = new List <ICurvesFillPolygon>(); foreach (Tile t in Tiles) { if (t.regions.Count == 0) { continue; } t.fills = new ICurvesFillPolygon[t.regions.Count]; for (int k = 0; k < t.regions.Count; ++k) { t.fills[k] = TileFillGeneratorF(t.regions[k], t.index); if (t.fills[k] != null) { all_fills.Add(t.fills[k]); } } } gParallel.ForEach(all_fills, (fill) => { fill.Compute(); }); FillCurves = new List <FillCurveSet2d>(); foreach (ICurvesFillPolygon fill in all_fills) { List <FillCurveSet2d> result = fill.GetFillCurves(); if (result != null && result.Count > 0) { FillCurves.AddRange(result); } } return(true); }
protected virtual List <GeneralPolygon2d> combine_solids(List <GeneralPolygon2d> all_solids, List <GeneralPolygon2d> new_solids) { return(ClipperUtil.PolygonBoolean(all_solids, new_solids, ClipperUtil.BooleanOp.Union, MIN_AREA)); }
/// <summary> /// Convert assembly of polygons, polylines, etc, into a set of printable solids and paths /// </summary> public virtual void Resolve() { // combine solids, process largest-to-smallest if (InputSolids.Count > 0) { GeneralPolygon2d[] solids = InputSolids.ToArray(); solids = process_input_polys_before_sort(solids); // sort by decreasing weight double[] weights = new double[solids.Length]; for (int i = 0; i < solids.Length; ++i) { weights[i] = sorting_weight(solids[i]); } Array.Sort(weights, solids); Array.Reverse(solids); solids = process_input_polys_after_sort(solids); Solids = new List <GeneralPolygon2d>(); for (int k = 0; k < solids.Length; ++k) { // convert this polygon into the solid we want to use List <GeneralPolygon2d> resolvedSolid = make_solid(solids[k], false); // now union in with accumulated solids if (Solids.Count == 0) { Solids.AddRange(resolvedSolid); } else { Solids = combine_solids(Solids, resolvedSolid); } } } // subtract input cavities foreach (var cavity in InputCavities) { Solids = remove_cavity(Solids, cavity); } // subtract thickened embedded paths from solids if (EmbeddedPaths.Count > 0 && EmbeddedPathWidth == 0) { throw new Exception("PlanarSlice.Resolve: must set embedded path width!"); } foreach (var path in EmbeddedPaths) { Polygon2d thick_path = make_thickened_path(path, EmbeddedPathWidth); Solids = ClipperUtil.Difference(Solids, new GeneralPolygon2d(thick_path), MIN_AREA); Paths.Add(path); } // cleanup Solids = filter_solids(Solids); // subtract solids from clipped paths foreach (var path in ClippedPaths) { List <PolyLine2d> clipped = ClipperUtil.ClipAgainstPolygon(Solids, path); foreach (var cp in clipped) { Paths.Add(cp); } } // combine support solids, while also subtracting print solids and thickened paths if (InputSupportSolids.Count > 0) { // make assembly of path solids // [TODO] do we need to boolean these? List <GeneralPolygon2d> path_solids = null; if (Paths.Count > 0) { path_solids = new List <GeneralPolygon2d>(); foreach (var path in Paths) { path_solids.Add(new GeneralPolygon2d(make_thickened_path(path, EmbeddedPathWidth))); } } foreach (var solid in InputSupportSolids) { // convert this polygon into the solid we want to use List <GeneralPolygon2d> resolved = make_solid(solid, true); // now subtract print solids resolved = ClipperUtil.PolygonBoolean(resolved, Solids, ClipperUtil.BooleanOp.Difference, MIN_AREA); // now subtract paths if (path_solids != null) { resolved = ClipperUtil.PolygonBoolean(resolved, path_solids, ClipperUtil.BooleanOp.Difference, MIN_AREA); } // now union in with accumulated support solids if (SupportSolids.Count == 0) { SupportSolids.AddRange(resolved); } else { SupportSolids = ClipperUtil.PolygonBoolean(SupportSolids, resolved, ClipperUtil.BooleanOp.Union, MIN_AREA); } } SupportSolids = filter_solids(SupportSolids); } // apply crop regions if (InputCropRegions.Count > 0) { // combine crop regions var CropRegions = make_solid(InputCropRegions[0], false); for (int k = 1; k < InputCropRegions.Count; ++k) { CropRegions = combine_solids(CropRegions, make_solid(InputCropRegions[k], false)); } Solids = ClipperUtil.Intersection(CropRegions, Solids, MIN_AREA); Solids = filter_solids(Solids); List <PolyLine2d> cropped_paths = new List <PolyLine2d>(); foreach (var path in Paths) { cropped_paths.AddRange(ClipperUtil.ClipAgainstPolygon(CropRegions, path, true)); } // TODO: filter paths SupportSolids = ClipperUtil.Intersection(CropRegions, SupportSolids, MIN_AREA); SupportSolids = filter_solids(SupportSolids); } }