Пример #1
0
        /// <summary>
        /// Fallback to deal with very tiny polygons that disappear when insetting.
        /// This happens at Z-minima-tips, which can be a problem because it may leave
        /// gaps between layers. For tips we draw a tiny circle.
        /// For elongated shapes we...?? currently do something dumb.
        /// Probably should use robust thinning!
        /// </summary>
        public virtual void HandleTinyPolygon()
        {
            //(InsetFromInputPolygon) ?
            //ClipperUtil.ComputeOffsetPolygon(Polygon, -ToolWidth / 2, true) :
            AxisAlignedBox2d bounds = Polygon.Bounds;

            if (bounds.MaxDim < ToolWidth)
            {
                GeneralPolygon2d min_poly = new GeneralPolygon2d(Polygon2d.MakeCircle(ToolWidth / 4, 6));
                min_poly.Outer.Translate(bounds.Center);
                FillCurveSet2d paths = ShellPolysToPaths(new List <GeneralPolygon2d>()
                {
                    min_poly
                }, 0);
                Shells.Add(paths);
            }
            else
            {
                FillCurveSet2d paths = ShellPolysToPaths(new List <GeneralPolygon2d>()
                {
                    Polygon
                }, 0);
                Shells.Add(paths);
            }

            InnerPolygons = new List <GeneralPolygon2d>();
        }
Пример #2
0
        public static void TestOffsetAnimation()
        {
            Window window = new Window("TestFill");

            window.SetDefaultSize(600, 600);
            window.SetPosition(WindowPosition.Center);

            DMesh3            mesh  = StandardMeshReader.ReadMesh("c:\\scratch\\remesh.obj");
            MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh);
            DCurve3           curve = loops[0].ToCurve();
            Polygon2d         poly  = new Polygon2d();

            foreach (Vector3d v in curve.Vertices)
            {
                poly.AppendVertex(v.xy);
            }
            Outer = new GeneralPolygon2d(poly);

            DebugViewCanvas view = new DebugViewCanvas();

            view.AddPolygon(Outer, Colorf.Black);


            DGraph2 graph = TopoOffset2d.QuickCompute(Outer, AnimOffset, AnimSpacing);

            view.AddGraph(graph, Colorf.Red);

            window.Add(view);
            window.ShowAll();

            Active = view;
        }
Пример #3
0
        protected FillCurveSet2d ComputeFillPaths(GeneralPolygon2d poly, SegmentSet2d polyCache)
        {
            List <List <Segment2d> > StepSpans = ComputeSegments(poly, polyCache);
            int N = StepSpans.Count;

            //double hard_max_dist = 5 * PathSpacing;

            // [TODO] need a pathfinder here, that can chain segments efficiently

            // (for now just do dumb things?)

            FillCurveSet2d paths = new FillCurveSet2d();

            //FillPolyline2d cur = new FillPolyline2d();

            foreach (var seglist in StepSpans)
            {
                foreach (Segment2d seg in seglist)
                {
                    FillPolyline2d fill_seg = new FillPolyline2d()
                    {
                        TypeFlags = FillTypeFlags.SolidInfill
                    };
                    fill_seg.AppendVertex(seg.P0);
                    fill_seg.AppendVertex(seg.P1);
                    paths.Append(fill_seg);
                }
            }

            return(paths);
        }
Пример #4
0
 public static List <GeneralPolygon2d> RoundOffset(GeneralPolygon2d poly, double fOffset, double minArea = -1)
 {
     return(ComputeOffsetPolygon(new List <GeneralPolygon2d>()
     {
         poly
     }, fOffset, false, minArea));
 }
Пример #5
0
        /// <summary>
        /// fill poly w/ adjacent straight line segments, connected by connectors
        /// </summary>
        protected FillCurveSet2d ComputeFillPaths(GeneralPolygon2d poly)
        {
            FillCurveSet2d paths = new FillCurveSet2d();

            // smooth the input poly a little bit, this simplifies the filling
            // (simplify after?)
            if (SimplifyBeforeFilling)
            {
                poly = SimplifyInputPolygon(poly);
            }

            // compute 2D non-manifold graph consisting of original polygon and
            // inserted line segments
            DGraph2 spanGraph = ComputeSpanGraph(poly);

            if (spanGraph == null || spanGraph.VertexCount == poly.VertexCount)
            {
                return(paths);
            }

            DGraph2 pathGraph = BuildPathGraph(spanGraph);

            // filter out self-overlaps from graph
            if (FilterSelfOverlaps)
            {
                pathGraph = FilterPathGraphSelfOverlaps(pathGraph);
            }

            return(WalkPathGraph(pathGraph));
        }
Пример #6
0
        // Compute step-forward at cur_pos. We find the closest point on the poly,
        // and step away from that, unless we go to far, then we step back.
        protected Vector2d compute_offset_step(Vector2d cur_pos, GeneralPolygon2d poly, double fTargetOffset, double stepSize, out double err)
        {
            int    iHole, iSeg; double segT;
            double distSqr =
                poly_tree.DistanceSquared(cur_pos, out iHole, out iSeg, out segT);

            double   dist   = Math.Sqrt(distSqr);
            Vector2d normal = poly.GetNormal(iSeg, segT, iHole);

            // flip for negative offsets
            if (fTargetOffset < 0)
            {
                fTargetOffset = -fTargetOffset;
                normal        = -normal;
            }

            double step = stepSize;

            if (dist > fTargetOffset)
            {
                step = Math.Max(fTargetOffset - dist, -step);
            }
            else
            {
                step = Math.Min(fTargetOffset - dist, step);
            }
            err = Math.Abs(fTargetOffset - dist);

            Vector2d new_pos = cur_pos - step * normal;

            return(new_pos);
        }
Пример #7
0
        public void AddPolygonsTest()
        {
            PlanarSlice slice = new PlanarSlice();
            Polygon2d   p2d   = new Polygon2d(new List <Vector2d>()
            {
                new Vector2d(1, 0),
                new Vector2d(0, 1),
                new Vector2d(1, 1)
            });
            GeneralPolygon2d poly = new GeneralPolygon2d(p2d);
            Polygon2d        p2d2 = new Polygon2d(new List <Vector2d>()
            {
                new Vector2d(-1, 0),
                new Vector2d(0, -1),
                new Vector2d(-1, -1)
            });
            GeneralPolygon2d poly2 = new GeneralPolygon2d(p2d2);

            slice.AddPolygons(new List <GeneralPolygon2d>()
            {
                poly, poly2
            });

            Assert.AreEqual(slice.InputSolids[0], poly);
            Assert.AreEqual(slice.InputSolids[1], poly2);
        }
Пример #8
0
        // [RMS] only using this for hit-testing to make sure no connectors cross polygon border...
        // [TODO] replace with GeneralPolygon2dBoxTree (currently does not have intersection test!)
        //SegmentSet2d BoundaryPolygonCache;

        public ParallelLinesFillPolygon(GeneralPolygon2d poly, IFillType fillType, bool exportToSVG = false)
        {
            Polygon          = poly;
            FillCurves       = new List <FillCurveSet2d>();
            FillType         = fillType;
            this.exportToSVG = exportToSVG;
        }
Пример #9
0
        protected FillCurveSet2d ComputeFillPaths(GeneralPolygon2d poly, SegmentSet2d polyCache)
        {
            var stepSpans = ComputeSegments(poly, polyCache);

            // [TODO] need a pathfinder here, that can chain segments efficiently
            // (for now just do dumb things?)

            FillCurveSet2d paths = new FillCurveSet2d();

            foreach (var seglist in stepSpans)
            {
                foreach (Segment2d seg in seglist)
                {
                    // Discard paths that are too short
                    if (seg.Length < MinPathLengthMM)
                    {
                        continue;
                    }

                    var fill_seg = new FillCurve <FillSegment>()
                    {
                        FillType = FillType,
                    };
                    fill_seg.BeginCurve(seg.P0);
                    fill_seg.AddToCurve(seg.P1, new FillSegment());
                    paths.Append(fill_seg);
                }
            }

            return(paths);
        }
Пример #10
0
        public static SKPath ToSKPath(GeneralPolygon2d g, Func <Vector2d, SKPoint> mapF)
        {
            SKPath p = new SKPath();

            int N = g.Outer.VertexCount;

            p.MoveTo(mapF(g.Outer[0]));
            for (int i = 1; i < N; i++)
            {
                p.LineTo(mapF(g.Outer[i]));
            }
            p.Close();

            foreach (Polygon2d h in g.Holes)
            {
                int hN = h.VertexCount;
                p.MoveTo(mapF(h[0]));
                for (int i = 1; i < hN; ++i)
                {
                    p.LineTo(mapF(h[i]));
                }
                p.Close();
            }

            return(p);
        }
Пример #11
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);
        }
Пример #12
0
 public static List <GeneralPolygon2d> PolygonBoolean(GeneralPolygon2d poly1, List <GeneralPolygon2d> poly2, BooleanOp opType, double minArea = -1)
 {
     return(PolygonBoolean(new List <GeneralPolygon2d>()
     {
         poly1
     }, poly2, opType, minArea));
 }
Пример #13
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);
        }
Пример #14
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));
        }
Пример #15
0
        // approximately check thickness of poly. For each segment, offset by check_offset*seg_normal,
        // then find distance to nearset point on poly. If distance_sqr is < mindist_sqr,
        // then we are below thin-tolerance.
        //
        // Currently returns true/false test, which is stupid...
        //
        // Will definitely fail on: squares (w/ short seg near edge), thin narrow bits... ??
        bool is_too_thin(GeneralPolygon2d poly, double check_offset, double mindist_sqr)
        {
            Debug.Assert(mindist_sqr < 0.95 * check_offset * check_offset);

            bool failed = false;
            Action <Segment2d> seg_checkF = (seg) => {
                if (failed)
                {
                    return;
                }
                if (seg.Length < 0.01)                  // not robust if segment is too short
                {
                    return;
                }
                Vector2d n = -seg.Direction.Perp;
                Vector2d pt = seg.Center + check_offset * n;
                int      iHole, iSeg; double segT;
                double   dist_sqr = poly.DistanceSquared(pt, out iHole, out iSeg, out segT);
                if (dist_sqr < mindist_sqr)
                {
                    failed = true;
                }
            };

            gParallel.ForEach(poly.AllSegmentsItr(), seg_checkF);
            return(failed);
        }
Пример #16
0
        public void TrimSegment2d_SegmentCollinear()
        {
            // Checks that when the segment is coincident with segments of the polygon,
            // segments that lie on polygon edges are not included, as per the intended
            // behavior.

            // Arrange
            var square = new GeneralPolygon2d(new Polygon2d(new double[] {
                0, 0,
                3, 0,
                3, 1,
                2, 1,
                2, 1.3,
                2, 1.6,
                2, 2,
                3, 2,
                3, 3,
                0, 3,
            }));
            var seg = new Segment2d(new Vector2d(2, 0.5), new Vector2d(2, 2.5));

            // Act
            var result = square.TrimSegment2d(seg);

            // Assert
            Assert.AreEqual(2, result.Count);
            AssertExtensions.AreEqual(new Vector2d(2, 0.5), result[0].P0);
            AssertExtensions.AreEqual(new Vector2d(2, 1.0), result[0].P1);
            AssertExtensions.AreEqual(new Vector2d(2, 2.0), result[1].P0);
            AssertExtensions.AreEqual(new Vector2d(2, 2.5), result[1].P1);
        }
Пример #17
0
        public static bool IsOutside(this GeneralPolygon2d poly, Segment2d seg)
        {
            bool isOutside = true;

            if (poly.Outer.IsMember(seg, out isOutside))
            {
                if (isOutside)
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            foreach (Polygon2d hole in poly.Holes)
            {
                if (hole.IsMember(seg, out isOutside))
                {
                    if (isOutside)
                    {
                        return(true);
                    }
                    else
                    {
                        return(false);
                    }
                }
            }
            return(false);
        }
Пример #18
0
        /// <summary>
        /// Check for significant topology/area changes in polygon inset.
        /// Currently returns true if:
        ///   1) inset has diff # of polygons or holes in single poly
        ///   2) area change is significantly larger than what we expect
        ///      (inset-band area estimated as perimeter_len * k * inset_dist)
        /// </summary>
        protected virtual bool check_large_topology_change(
            GeneralPolygon2d input, List <GeneralPolygon2d> inset, double fInsetDist)
        {
            if (inset.Count != 1 || inset[0].Holes.Count != input.Holes.Count)
            {
                return(true);
            }

            double orig_area       = Math.Abs(input.Area);
            double perim           = input.Perimeter;
            double perim_band_area = perim * fInsetDist;
            double est_inset_area  = orig_area - 1.5 * perim_band_area;

            double inset_area = 0;

            foreach (var poly in inset)
            {
                inset_area += Math.Abs(poly.Area);
            }

            if (inset_area < est_inset_area)
            {
                return(true);
            }

            return(false);
        }
Пример #19
0
 public SegmentSet2d(GeneralPolygon2d poly)
 {
     Segments = new List <Segment2d>(poly.Outer.SegmentItr());
     foreach (var hole in poly.Holes)
     {
         Segments.AddRange(hole.SegmentItr());
     }
 }
Пример #20
0
 public void AddPolygon(GeneralPolygon2d poly)
 {
     if (poly.Outer.IsClockwise)
     {
         poly.Reverse();
     }
     InputSolids.Add(poly);
 }
Пример #21
0
 public void AddCropRegion(GeneralPolygon2d poly)
 {
     if (poly.Outer.IsClockwise)
     {
         poly.Reverse();
     }
     InputCropRegions.Add(poly);
 }
Пример #22
0
 public void AddCavityPolygon(GeneralPolygon2d poly)
 {
     if (poly.Outer.IsClockwise)
     {
         poly.Reverse();
     }
     InputCavities.Add(poly);
 }
Пример #23
0
 /// <summary>
 /// use during resolve() processing to transfer tags/metadata to child polygons
 /// created by processing ops
 /// </summary>
 protected virtual void transfer_tags(GeneralPolygon2d oldPoly, GeneralPolygon2d newPoly)
 {
     if (Tags.Has(oldPoly))
     {
         int t = Tags.Get(oldPoly);
         Tags.Add(newPoly, t);
     }
 }
Пример #24
0
 public void Add(GeneralPolygon2d path, int outer_gid = -1, int hole_gid = -1)
 {
     Graph.AppendPolygon(path.Outer, outer_gid);
     foreach (Polygon2d hole in path.Holes)
     {
         Graph.AppendPolygon(hole, hole_gid);
     }
 }
Пример #25
0
        private GeneralPolygon2d SimplifyInputPolygon(GeneralPolygon2d poly)
        {
            GeneralPolygon2d smoothed = poly.Duplicate();

            CurveUtils2.LaplacianSmoothConstrained(smoothed, 0.5, 5, ToolWidth / 2, true, false);
            poly = smoothed;
            return(poly);
        }
Пример #26
0
 public static void Store(GeneralPolygon2d polygon, BinaryWriter writer)
 {
     Store(polygon.Outer, writer);
     writer.Write(polygon.Holes.Count);
     for (int i = 0; i < polygon.Holes.Count; ++i)
     {
         Store(polygon.Holes[i], writer);
     }
 }
Пример #27
0
 public TopoOffset2d(GeneralPolygon2d poly, double fOffset, double fPointSpacing, bool bAutoCompute = true)
 {
     Polygon      = poly;
     Offset       = fOffset;
     PointSpacing = fPointSpacing;
     if (bAutoCompute)
     {
         Compute();
     }
 }
Пример #28
0
        /// <summary>
        /// Fill polygon with solid fill strategy.
        /// If bIsInfillAdjacent, then we optionally add one or more shells around the solid
        /// fill, to give the solid fill something to stick to (imagine dense linear fill adjacent
        /// to sparse infill area - when the extruder zigs, most of the time there is nothing
        /// for the filament to attach to, so it pulls back. ugly!)
        /// </summary>
        protected virtual void fill_solid_region(int layer_i, GeneralPolygon2d solid_poly,
                                                 IFillPathScheduler2d scheduler,
                                                 bool bIsInfillAdjacent = false)
        {
            List <GeneralPolygon2d> fillPolys = new List <GeneralPolygon2d>()
            {
                solid_poly
            };

            // if we are on an infill layer, and this shell has some infill region,
            // then we are going to draw contours around solid fill so it has
            // something to stick to
            // [TODO] should only be doing this if solid-fill is adjecent to infill region.
            //   But how to determine this? not easly because we don't know which polys
            //   came from where. Would need to do loop above per-polygon
            if (bIsInfillAdjacent && Settings.Part.InteriorSolidRegionShells > 0)
            {
                ShellsFillPolygon interior_shells = new ShellsFillPolygon(solid_poly, Settings.FillTypeFactory.Solid());
                interior_shells.PathSpacing            = Settings.SolidFillPathSpacingMM();
                interior_shells.ToolWidth              = Settings.Machine.NozzleDiamMM;
                interior_shells.Layers                 = Settings.Part.InteriorSolidRegionShells;
                interior_shells.PreserveOuterShells    = true;
                interior_shells.InsetFromInputPolygonX = 0;
                interior_shells.Compute();
                scheduler.AppendCurveSets(interior_shells.Shells);
                fillPolys = interior_shells.InnerPolygons;
            }

            // now actually fill solid regions
            foreach (GeneralPolygon2d fillPoly in fillPolys)
            {
                var solidFillSpacing        = Settings.SolidFillPathSpacingMM();
                TiledFillPolygon tiled_fill = new TiledFillPolygon(fillPoly)
                {
                    TileSize    = 13.1 * solidFillSpacing,
                    TileOverlap = 0.3 * solidFillSpacing
                };
                tiled_fill.TileFillGeneratorF = (tilePoly, index) =>
                {
                    int odd = ((index.x + index.y) % 2 == 0) ? 1 : 0;
                    RasterFillPolygon solid_gen = new RasterFillPolygon(tilePoly, Settings.FillTypeFactory.Solid())
                    {
                        InsetFromInputPolygon = false,
                        PathSpacing           = solidFillSpacing,
                        ToolWidth             = Settings.Machine.NozzleDiamMM,
                        AngleDeg = LayerFillAngleF(layer_i + odd)
                    };
                    return(solid_gen);
                };

                tiled_fill.Compute();
                scheduler.AppendCurveSets(tiled_fill.FillCurves);
            }
        }
        static SKPath MakePaths(GeneralPolygon2d poly, Func <Vector2d, SKPoint> mapF)
        {
            SKPath p = new SKPath();

            add_poly(p, poly.Outer, mapF);
            foreach (var h in poly.Holes)
            {
                add_poly(p, h, mapF);
            }
            return(p);
        }
Пример #30
0
        public static CPolygonList ConvertToClipper(GeneralPolygon2d polys, double nIntScale)
        {
            List <CPolygon> clipper_polys = new List <CPolygon>();

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