예제 #1
0
        /// <summary>
        /// Compute inset from input polygon. If requested, will check for
        /// topological changes and try smaller insets, and if that fails,
        /// will just return input polygon.
        /// </summary>
        protected virtual List <GeneralPolygon2d> ComputeInitialInsetPolygon(
            bool bForcePreserveTopology)
        {
            double fInset = ToolWidth * InsetFromInputPolygonX;
            List <GeneralPolygon2d> insetPolys =
                ClipperUtil.MiterOffset(Polygon, -fInset);

            if (bForcePreserveTopology == false)
            {
                return(insetPolys);
            }

            if (check_large_topology_change(Polygon, insetPolys, fInset))
            {
                fInset    /= 2;
                insetPolys = ClipperUtil.MiterOffset(Polygon, -fInset);
                if (check_large_topology_change(Polygon, insetPolys, fInset))
                {
                    insetPolys = new List <GeneralPolygon2d>()
                    {
                        Polygon
                    }
                }
                ;
            }

            return(insetPolys);
        }
예제 #2
0
        public List <FillPolyline2d> thin_offset(GeneralPolygon2d p)
        {
            List <FillPolyline2d> result = new List <FillPolyline2d>();

            // to support non-hole thin offsets we need to return polylines
            if (p.Holes.Count == 0)
            {
                return(result);
            }

            // compute desired offset from outer polygon
            GeneralPolygon2d        outer   = new GeneralPolygon2d(p.Outer);
            List <GeneralPolygon2d> offsets =
                ClipperUtil.ComputeOffsetPolygon(outer, -ToolWidth, true);

            if (offsets == null || offsets.Count == 0)
            {
                return(result);
            }

            double clip_dist = ToolWidth * ToolWidthClipMultiplier;

            foreach (GeneralPolygon2d offset_poly in offsets)
            {
                List <FillPolyline2d> clipped = clip_to_band(offset_poly.Outer, p, clip_dist);
                result.AddRange(clipped);
            }

            return(result);
        }
예제 #3
0
        public static List <GeneralPolygon2d> ComputeOffsetPolygon(GeneralPolygon2d poly, double fOffset, bool bMiter = false)
        {
            double nIntScale = GetIntScale(poly);

            CPolygonList clipper_polys = new CPolygonList();

            clipper_polys.Add(ClipperUtil.ConvertToClipper(poly.Outer, nIntScale));
            foreach (Polygon2d hole in poly.Holes)
            {
                clipper_polys.Add(ClipperUtil.ConvertToClipper(hole, nIntScale));
            }

            CPolygonList dilate_solution = new CPolygonList();

            try {
                ClipperOffset co = new ClipperOffset();
                if (bMiter)
                {
                    co.AddPaths(clipper_polys, JoinType.jtMiter, EndType.etClosedPolygon);
                }
                else
                {
                    co.AddPaths(clipper_polys, JoinType.jtRound, EndType.etClosedPolygon);
                }
                co.Execute(ref dilate_solution, fOffset * nIntScale);
            } catch /*( Exception e )*/ {
                //System.Diagnostics.Debug.WriteLine("ClipperUtil.ComputeOffsetPolygon: Clipper threw exception: " + e.Message);
                return(new List <GeneralPolygon2d>());
            }

            List <GeneralPolygon2d> polys = ClipperUtil.ConvertFromClipper(dilate_solution, nIntScale);

            return(polys);
        }
예제 #4
0
        public bool Compute()
        {
            if (InsetFromInputPolygon)
            {
                //BoundaryPolygonCache = new SegmentSet2d(Polygon);
                List <GeneralPolygon2d> current = ClipperUtil.ComputeOffsetPolygon(Polygon, -ToolWidth / 2, true);
                foreach (GeneralPolygon2d poly in current)
                {
                    FillCurveSet2d fillPaths = ComputeFillPaths(poly);
                    if (fillPaths != null)
                    {
                        FillCurves.Add(fillPaths);
                    }
                }
            }
            else
            {
                List <GeneralPolygon2d> boundary = ClipperUtil.ComputeOffsetPolygon(Polygon, ToolWidth / 2, true);
                //BoundaryPolygonCache = new SegmentSet2d(boundary);
                FillCurveSet2d fillPaths = ComputeFillPaths(Polygon);
                if (fillPaths != null)
                {
                    FillCurves.Add(fillPaths);
                }
            }


            return(true);
        }
예제 #5
0
        /// <summary>
        /// Shrink holes inside polys such that if we do offset/2, the hole and
        /// outer offsets will (probably) not collide. Two options:
        ///   1) contract and then dilate each hole. This doesn't handle long skinny holes?
        ///   2) shrink outer by offset and then intersect with holes
        /// Currently using (2). This is better, right?
        /// </summary>
        protected void FilterHoles(List <GeneralPolygon2d> polys, double offset)
        {
            foreach (var poly in polys)
            {
                if (poly.Holes.Count == 0)
                {
                    continue;
                }

                List <GeneralPolygon2d> outer_inset = ClipperUtil.MiterOffset(
                    new GeneralPolygon2d(poly.Outer), -offset);

                List <GeneralPolygon2d> hole_polys = new List <GeneralPolygon2d>();
                foreach (var hole in poly.Holes)
                {
                    hole.Reverse();
                    hole_polys.Add(new GeneralPolygon2d(hole));
                }

                //List<GeneralPolygon2d> contracted = ClipperUtil.MiterOffset(hole_polys, -offset, 0.01);
                //List<GeneralPolygon2d> dilated = ClipperUtil.MiterOffset(hole_polys, offset, 0.01);

                List <GeneralPolygon2d> dilated = ClipperUtil.Intersection(hole_polys, outer_inset, 0.01);

                poly.ClearHoles();

                List <Polygon2d> new_holes = new List <Polygon2d>();
                foreach (var dpoly in dilated)
                {
                    dpoly.Outer.Reverse();
                    poly.AddHole(dpoly.Outer, false, false);
                }
            }
        }
예제 #6
0
        public bool Compute()
        {
            if (InsetFromInputPolygon)
            {
                double inset   = ToolWidth * InsetFromInputPolygonX;
                var    current = ClipperUtil.ComputeOffsetPolygon(Polygon, -inset, true);
                foreach (var poly in current)
                {
                    FillCurveSet2d fillPaths = ComputeFillPaths(poly);
                    if (fillPaths != null)
                    {
                        FillCurves.Add(fillPaths);
                    }
                }
            }
            else
            {
                FillCurveSet2d fillPaths = ComputeFillPaths(Polygon);
                if (fillPaths != null)
                {
                    FillCurves.Add(fillPaths);
                }
            }

            return(true);
        }
예제 #7
0
        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));
        }
예제 #8
0
        /*
         *  functions for subclasses to override to customize behavior
         */

        protected virtual GeneralPolygon2d[] process_input_polys_before_sort(GeneralPolygon2d[] polys)
        {
            if (Offsets.Count == 0)
            {
                return(polys);
            }
            List <GeneralPolygon2d> newPolys = new List <GeneralPolygon2d>();
            bool modified = false;

            foreach (var poly in polys)
            {
                double offset;
                if (Offsets.TryGetValue(poly, out offset) && Math.Abs(offset) > MathUtil.ZeroTolerancef)
                {
                    List <GeneralPolygon2d> offsetPolys = ClipperUtil.MiterOffset(poly, offset);
                    foreach (var newpoly in offsetPolys)
                    {
                        transfer_tags(poly, newpoly);
                        newPolys.Add(newpoly);
                    }
                    modified = true;
                }
                else
                {
                    newPolys.Add(poly);
                }
            }
            if (modified == false)
            {
                return(polys);
            }
            return(newPolys.ToArray());
        }
예제 #9
0
 protected virtual List <GeneralPolygon2d> ApplyValidRegions(List <GeneralPolygon2d> polygonsIn)
 {
     if (ValidRegions == null || ValidRegions.Count == 0)
     {
         return(polygonsIn);
     }
     return(ClipperUtil.Intersection(polygonsIn, ValidRegions));
 }
예제 #10
0
        protected virtual List <PolyLine2d> ApplyValidRegions(List <PolyLine2d> plinesIn)
        {
            if (ValidRegions == null || ValidRegions.Count == 0)
            {
                return(plinesIn);
            }
            List <PolyLine2d> clipped = new List <PolyLine2d>();

            foreach (var pline in plinesIn)
            {
                clipped.AddRange(ClipperUtil.ClipAgainstPolygon(ValidRegions, pline, true));
            }
            return(clipped);
        }
예제 #11
0
        protected virtual GeneralPolygon2d[] process_input_polys_after_sort(GeneralPolygon2d[] solids)
        {
            // construct thickened solids
            Thickened = new Dictionary <GeneralPolygon2d, List <GeneralPolygon2d> >();
            for (int k = 0; k < solids.Length; ++k)
            {
                double clearance;
                if (Clearances.TryGetValue(solids[k], out clearance) && clearance > 0)
                {
                    Thickened.Add(solids[k], ClipperUtil.MiterOffset(solids[k], clearance));
                }
            }

            return(solids);
        }
예제 #12
0
        private LayerCache build_cache(PrintLayerData layerData)
        {
            LayerCache cache = new LayerCache();

            cache.SupportAreas      = ClipperUtil.MiterOffset(layerData.SupportAreas, layerData.Settings.Machine.NozzleDiamMM);
            cache.SupportAreaBounds = new AxisAlignedBox2d[cache.SupportAreas.Count];
            cache.AllSupportBounds  = AxisAlignedBox2d.Empty;
            for (int i = 0; i < cache.SupportAreas.Count; ++i)
            {
                cache.SupportAreaBounds[i] = cache.SupportAreas[i].Bounds;
                cache.AllSupportBounds.Contain(cache.SupportAreaBounds[i]);
            }

            return(cache);
        }
예제 #13
0
        virtual protected void cache_brim_polys()
        {
            combined_solid   = new List <GeneralPolygon2d>();
            combined_support = new List <GeneralPolygon2d>();
            subtract         = new List <GeneralPolygon2d>();

            Frame3f cutPlane = new Frame3f((float)layer_height * 0.5f * Vector3f.AxisY, Vector3f.AxisY);

            foreach (var so in CC.Objects.PrintMeshes)
            {
                if (so.Settings.ObjectType == PrintMeshSettings.ObjectTypes.Ignored)
                {
                    continue;
                }
                SOSectionPlane section = new SOSectionPlane(so);
                section.UpdateSection(cutPlane, CoordSpace.SceneCoords);
                List <GeneralPolygon2d> solids = section.GetSolids();

                // [TODO] should subtract holes explicitly here, like we do in slicer?

                if (so.Settings.ObjectType == PrintMeshSettings.ObjectTypes.Cavity)
                {
                    subtract = ClipperUtil.Union(solids, subtract);
                }
                else if (so.Settings.ObjectType == PrintMeshSettings.ObjectTypes.Support)
                {
                    combined_support = ClipperUtil.Union(solids, combined_support);
                }
                else if (so.Settings.ObjectType == PrintMeshSettings.ObjectTypes.Solid)
                {
                    combined_solid = ClipperUtil.Union(solids, combined_solid);
                }
            }
            if (subtract.Count > 0)
            {
                combined_solid = ClipperUtil.Difference(combined_solid, subtract);
            }

            combined_all = ClipperUtil.Union(combined_solid, combined_support);
            combined_all = CurveUtils2.FilterDegenerate(combined_all, 0.001);

            foreach (var poly in combined_all)
            {
                poly.Simplify(path_width * 0.02);
            }
        }
예제 #14
0
        protected override List <GeneralPolygon2d> make_solid(GeneralPolygon2d poly, bool bIsSupportSolid)
        {
            List <GeneralPolygon2d> solid = base.make_solid(poly, bIsSupportSolid);

            if (bIsSupportSolid == false && Thickened != null)
            {
                // subtract clearance solids
                foreach (var pair in Thickened)
                {
                    if (pair.Key != poly)
                    {
                        solid = ClipperUtil.Difference(solid, pair.Value);
                    }
                }
            }
            return(solid);
        }
예제 #15
0
        protected virtual List <GeneralPolygon2d> remove_cavity(List <GeneralPolygon2d> solids, GeneralPolygon2d cavity)
        {
            double offset = 0;

            if (Cavity_Clearances.ContainsKey(cavity))
            {
                offset = Cavity_Clearances[cavity];
            }
            if (Cavity_Offsets.ContainsKey(cavity))
            {
                offset += Cavity_Offsets[cavity];
            }
            if (Math.Abs(offset) > 0.0001)
            {
                var offset_cavities = ClipperUtil.MiterOffset(cavity, offset, MIN_AREA);
                return(ClipperUtil.Difference(solids, offset_cavities, MIN_AREA));
            }
            else
            {
                return(ClipperUtil.Difference(solids, cavity, MIN_AREA));
            }
        }
예제 #16
0
        public bool Compute()
        {
            if (InsetFromInputPolygon)
            {
                BoundaryPolygonCache = new SegmentSet2d(Polygon);
                List <GeneralPolygon2d> current = ClipperUtil.ComputeOffsetPolygon(Polygon, -ToolWidth / 2, true);
                foreach (GeneralPolygon2d poly in current)
                {
                    SegmentSet2d polyCache = new SegmentSet2d(poly);
                    Paths.Add(ComputeFillPaths(poly, polyCache));
                }
            }
            else
            {
                List <GeneralPolygon2d> boundary = ClipperUtil.ComputeOffsetPolygon(Polygon, ToolWidth / 2, true);
                BoundaryPolygonCache = new SegmentSet2d(boundary);

                SegmentSet2d polyCache = new SegmentSet2d(Polygon);
                Paths.Add(ComputeFillPaths(Polygon, polyCache));
            }

            return(true);
        }
예제 #17
0
        public static List <GeneralPolygon2d> ComputeOffsetPolygon(Polygon2d poly, double fOffset, bool bSharp = false)
        {
            double nIntScale = GetIntScale(poly.Vertices);

            List <IntPoint> clipper_poly  = ClipperUtil.ConvertToClipper(poly, nIntScale);
            CPolygonList    clipper_polys = new CPolygonList()
            {
                clipper_poly
            };

            CPolygonList dilate_solution = new CPolygonList();

            try {
                ClipperOffset co = new ClipperOffset();
                if (bSharp)
                {
                    co.AddPaths(clipper_polys, JoinType.jtMiter, EndType.etClosedPolygon);
                }
                else
                {
                    co.AddPaths(clipper_polys, JoinType.jtRound, EndType.etClosedPolygon);
                }
                co.Execute(ref dilate_solution, fOffset * nIntScale);
            } catch /*( Exception e )*/ {
                //System.Diagnostics.Debug.WriteLine("ClipperUtil.ComputeOffsetPolygon: Clipper threw exception: " + e.Message);
                return(new List <GeneralPolygon2d>());
            }

            if (dilate_solution.Count == 0)
            {
                return(new List <GeneralPolygon2d>());
            }

            List <GeneralPolygon2d> polys = ClipperUtil.ConvertFromClipper(dilate_solution, nIntScale);

            return(polys);
        }
예제 #18
0
 protected virtual List <GeneralPolygon2d> remove_cavity(List <GeneralPolygon2d> solids, GeneralPolygon2d cavity)
 {
     return(ClipperUtil.Difference(solids, cavity, MIN_AREA));
 }
예제 #19
0
        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);
        }
예제 #20
0
        /// <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);
        }
예제 #21
0
        protected virtual void fill_bridge_region_decompose(GeneralPolygon2d poly, IFillPathScheduler2d scheduler, PrintLayerData layer_data)
        {
            poly.Simplify(0.1, 0.01, true);
            TriangulatedPolygonGenerator generator = new TriangulatedPolygonGenerator()
            {
                Polygon = poly, Subdivisions = 16
            };
            DMesh3 mesh = generator.Generate().MakeDMesh();
            //Util.WriteDebugMesh(mesh, "/Users/rms/scratch/bridgemesh.obj");


            //List<Polygon2d> polys = decompose_mesh_recursive(mesh);
            List <Polygon2d> polys = decompose_cluster_up(mesh);

            Util.WriteDebugMesh(mesh, "/Users/rms/scratch/bridgemesh_reduce.obj");

            double spacing = Settings.BridgeFillPathSpacingMM();

            foreach (Polygon2d polypart in polys)
            {
                Box2d    box   = polypart.MinimalBoundingBox(0.00001);
                Vector2d axis  = (box.Extent.x > box.Extent.y) ? box.AxisY : box.AxisX;
                double   angle = Math.Atan2(axis.y, axis.x) * MathUtil.Rad2Deg;

                GeneralPolygon2d gp = new GeneralPolygon2d(polypart);

                ShellsFillPolygon shells_fill = new ShellsFillPolygon(gp);
                shells_fill.PathSpacing            = Settings.SolidFillPathSpacingMM();
                shells_fill.ToolWidth              = Settings.Machine.NozzleDiamMM;
                shells_fill.Layers                 = 1;
                shells_fill.InsetFromInputPolygonX = 0.25;
                shells_fill.ShellType              = ShellsFillPolygon.ShellTypes.BridgeShell;
                shells_fill.FilterSelfOverlaps     = false;
                shells_fill.Compute();
                scheduler.AppendCurveSets(shells_fill.GetFillCurves());
                var fillPolys = shells_fill.InnerPolygons;

                double offset = Settings.Machine.NozzleDiamMM * Settings.SolidFillBorderOverlapX;
                fillPolys = ClipperUtil.MiterOffset(fillPolys, offset);

                foreach (var fp in fillPolys)
                {
                    BridgeLinesFillPolygon fill_gen = new BridgeLinesFillPolygon(fp)
                    {
                        InsetFromInputPolygon = false,
                        PathSpacing           = spacing,
                        ToolWidth             = Settings.Machine.NozzleDiamMM,
                        AngleDeg = angle,
                    };
                    fill_gen.Compute();
                    scheduler.AppendCurveSets(fill_gen.GetFillCurves());
                }
            }

            // fit bbox to try to find fill angle that has shortest spans
            //Box2d box = poly.Outer.MinimalBoundingBox(0.00001);
            //Vector2d axis = (box.Extent.x > box.Extent.y) ? box.AxisY : box.AxisX;
            //double angle = Math.Atan2(axis.y, axis.x) * MathUtil.Rad2Deg;

            // [RMS] should we do something like this?
            //if (Settings.SolidFillBorderOverlapX > 0) {
            //	double offset = Settings.Machine.NozzleDiamMM * Settings.SolidFillBorderOverlapX;
            //	fillPolys = ClipperUtil.MiterOffset(fillPolys, offset);
            //}
        }
예제 #22
0
        public bool Compute()
        {
            bool   enable_thin_check     = false;
            double thin_check_offset     = ToolWidth * 0.45;
            double thin_check_thresh_sqr = ToolWidth * 0.3;

            thin_check_thresh_sqr *= thin_check_thresh_sqr;

            // first shell is either polygon, or inset from that polygon
            List <GeneralPolygon2d> current = null;

            if (InsetFromInputPolygonX != 0)
            {
                current = ComputeInitialInsetPolygon(PreserveInputInsetTopology);
            }
            else
            {
                current = new List <GeneralPolygon2d>()
                {
                    Polygon
                };
            }
            List <GeneralPolygon2d> current_prev = null;

            if (current.Count == 0)
            {
                HandleTinyPolygon();
                return(true);
            }

            // convert previous layer to shell, and then compute next layer
            List <GeneralPolygon2d> failedShells     = new List <GeneralPolygon2d>();
            List <GeneralPolygon2d> nextShellTooThin = new List <GeneralPolygon2d>();

            for (int i = 0; i < Layers; ++i)
            {
                FillCurveSet2d paths = ShellPolysToPaths(current, i);
                Shells.Add(paths);

                List <GeneralPolygon2d> all_next = new List <GeneralPolygon2d>();
                foreach (GeneralPolygon2d gpoly in current)
                {
                    List <GeneralPolygon2d> offsets =
                        ClipperUtil.ComputeOffsetPolygon(gpoly, -PathSpacing, true);

                    List <GeneralPolygon2d> filtered = new List <GeneralPolygon2d>();
                    foreach (var v in offsets)
                    {
                        bool bTooSmall = (v.Perimeter < DiscardTinyPerimterLengthMM ||
                                          v.Area < DiscardTinyPolygonAreaMM2);
                        if (bTooSmall)
                        {
                            continue;
                        }

                        if (enable_thin_check && is_too_thin(v, thin_check_offset, thin_check_thresh_sqr))
                        {
                            nextShellTooThin.Add(v);
                        }
                        else
                        {
                            filtered.Add(v);
                        }
                    }

                    if (filtered.Count == 0)
                    {
                        failedShells.Add(gpoly);
                    }
                    else
                    {
                        all_next.AddRange(filtered);
                    }
                }

                current_prev = current;
                current      = all_next;
            }


            // failedShells have no space for internal contours. But
            // we might be able to fit a single line...
            //foreach (GeneralPolygon2d gpoly in failedShells) {
            //	if (gpoly.Perimeter < DiscardTinyPerimterLengthMM ||
            //		 gpoly.Area < DiscardTinyPolygonAreaMM2)
            //		continue;

            //	List<FillPolyline2d> thin_shells = thin_offset(gpoly);
            //	Shells[Shells.Count - 1].Append(thin_shells);
            //}


            // remaining inner polygons
            if (InsetInnerPolygons)
            {
                InnerPolygons = current;
                InnerPolygons.AddRange(nextShellTooThin);
            }
            else
            {
                InnerPolygons = current_prev;
                InnerPolygons.AddRange(nextShellTooThin);
            }
            return(true);
        }
예제 #23
0
 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));
 }
예제 #24
0
        /// <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);
            }
        }
예제 #25
0
        virtual public void PreRender()
        {
            if (in_shutdown())
            {
                return;
            }

            if (parameters_dirty)
            {
                // offset
                List <GeneralPolygon2d> offset = ClipperUtil.RoundOffset(combined_all, offset_distance);
                // aggressively simplify after round offset...
                foreach (var poly in offset)
                {
                    poly.Simplify(path_width);
                }

                // subtract initial and add tiny gap so these don't get merged by slicer
                if (SubtractSolids)
                {
                    offset = ClipperUtil.Difference(offset, combined_solid);
                    offset = ClipperUtil.MiterOffset(offset, -path_width * 0.1);
                }

                offset = CurveUtils2.FilterDegenerate(offset, 0.001);
                foreach (var poly in offset)
                {
                    poly.Simplify(path_width * 0.02);
                }

                DMesh3     mesh   = new DMesh3();
                MeshEditor editor = new MeshEditor(mesh);
                foreach (var poly in offset)
                {
                    TriangulatedPolygonGenerator polygen = new TriangulatedPolygonGenerator()
                    {
                        Polygon = poly
                    };
                    editor.AppendMesh(polygen.Generate().MakeDMesh());
                }
                MeshTransforms.ConvertZUpToYUp(mesh);

                if (mesh.TriangleCount > 0)
                {
                    MeshExtrudeMesh extrude = new MeshExtrudeMesh(mesh);
                    extrude.ExtrudedPositionF = (v, n, vid) => {
                        return(v + Layers * layer_height * Vector3d.AxisY);
                    };
                    extrude.Extrude();
                    MeshTransforms.Translate(mesh, -mesh.CachedBounds.Min.y * Vector3d.AxisY);
                }

                PreviewSO.ReplaceMesh(mesh, true);

                //Vector3d translate = scene_bounds.Point(1, -1, 1);
                //translate.x += spiral.Bounds.Width + PathWidth;
                //Frame3f sceneF = Frame3f.Identity.Translated((Vector3f)translate);
                //PreviewSO.SetLocalFrame(sceneF, CoordSpace.SceneCoords);

                parameters_dirty = false;
            }
        }