public static DGraph2 perturb_fill_2(DGraph2 graphIn, GeneralPolygon2d bounds, double waveWidth, double stepSize) { DGraph2Util.Curves curves = DGraph2Util.ExtractCurves(graphIn); Polygon2d poly = curves.Loops[0]; GeneralPolygon2dBoxTree gpTree = new GeneralPolygon2dBoxTree(bounds); Polygon2dBoxTree outerTree = new Polygon2dBoxTree(bounds.Outer); Polygon2dBoxTree innerTree = new Polygon2dBoxTree(bounds.Holes[0]); DGraph2 graph = new DGraph2(); graph.EnableVertexColors(Vector3f.Zero); graph.AppendPolygon(poly); DGraph2Resampler resampler = new DGraph2Resampler(graph); resampler.CollapseToMinEdgeLength(waveWidth); if (graph.VertexCount % 2 != 0) { // TODO smallest edge Index2i ev = graph.GetEdgeV(graph.EdgeIndices().First()); DGraph2.EdgeCollapseInfo cinfo; graph.CollapseEdge(ev.a, ev.b, out cinfo); } // move to borders int startv = graph.VertexIndices().First(); int eid = graph.VtxEdgesItr(startv).First(); int curv = startv; bool outer = true; do { Polygon2dBoxTree use_tree = (outer) ? outerTree : innerTree; outer = !outer; graph.SetVertex(curv, use_tree.NearestPoint(graph.GetVertex(curv))); Index2i next = DGraph2Util.NextEdgeAndVtx(eid, curv, graph); eid = next.a; curv = next.b; } while (curv != startv); return(graph); }
public static void TestFill() { Window window = new Window("TestFill"); window.SetDefaultSize(600, 600); window.SetPosition(WindowPosition.Center); DebugViewCanvas view = new DebugViewCanvas(); GeneralPolygon2d poly = new GeneralPolygon2d( Polygon2d.MakeCircle(20, 32)); Polygon2d hole = Polygon2d.MakeCircle(15, 32); hole.Reverse(); hole.Translate(2 * Vector2d.AxisX); poly.AddHole(hole); view.AddPolygon(poly, Colorf.Black); double spacing = 0.5; double[] offsets = new double[] { 5 }; foreach (double offset in offsets) { DGraph2 graph = TopoOffset2d.QuickCompute(poly, offset, spacing); DGraph2Util.Curves c = DGraph2Util.ExtractCurves(graph); //view.AddGraph(graph, Colorf.Red); //DGraph2 perturbGraph = perturb_fill(graph, poly, 5.0f, spacing); DGraph2 perturbGraph = perturb_fill_2(graph, poly, 1.0f, spacing); //DGraph2Util.Curves c2 = DGraph2Util.ExtractCurves(perturbGraph); view.AddGraph(perturbGraph, Colorf.Orange); } window.Add(view); window.ShowAll(); Active = view; }
public static DGraph2 perturb_fill(DGraph2 graphIn, GeneralPolygon2d bounds, double waveWidth, double stepSize) { DGraph2Util.Curves curves = DGraph2Util.ExtractCurves(graphIn); Polygon2d poly = curves.Loops[0]; GeneralPolygon2dBoxTree gpTree = new GeneralPolygon2dBoxTree(bounds); Polygon2dBoxTree outerTree = new Polygon2dBoxTree(bounds.Outer); Polygon2dBoxTree innerTree = new Polygon2dBoxTree(bounds.Holes[0]); DGraph2 graph = new DGraph2(); graph.EnableVertexColors(Vector3f.Zero); double len = poly.Perimeter; int waves = (int)(len / waveWidth); double lenScale = len / (MathUtil.TwoPI * waves); double accum_len = 0; int prev_vid = -1, start_vid = -1; int N = poly.VertexCount; for (int k = 0; k < N; ++k) { double t = accum_len / lenScale; t = Math.Cos(t); //Vector2d normal = poly.GetNormal(k); Vector2d normal = poly[k].Normalized; int vid = graph.AppendVertex(poly[k], new Vector3f(t, normal.x, normal.y)); if (prev_vid != -1) { graph.AppendEdge(prev_vid, vid); accum_len += graph.GetVertex(prev_vid).Distance(graph.GetVertex(vid)); } else { start_vid = vid; } prev_vid = vid; } graph.AppendEdge(prev_vid, start_vid); Vector2d[] newPos = new Vector2d[graph.MaxVertexID]; for (int k = 0; k < 10; ++k) { smooth_pass(graph, 0.5f, newPos); } for (int k = 0; k < 20; ++k) { foreach (int vid in graph.VertexIndices()) { Vector2d v = graph.GetVertex(vid); Vector3f c = graph.GetVertexColor(vid); float t = c.x; Vector2d n = new Vector2d(c.y, c.z); if (k == 0 || Math.Abs(t) > 0.9) { v += t * stepSize * n; if (!bounds.Contains(v)) { v = gpTree.NearestPoint(v); } } newPos[vid] = v; } foreach (int vid in graph.VertexIndices()) { graph.SetVertex(vid, newPos[vid]); } for (int j = 0; j < 5; ++j) { smooth_pass(graph, 0.1f, newPos); } } return(graph); }
public static void TestDGraph2() { Window window = new Window("TestDGraph2"); window.SetDefaultSize(600, 600); window.SetPosition(WindowPosition.Center); DebugViewCanvas view = new DebugViewCanvas(); GeneralPolygon2d poly = new GeneralPolygon2d( Polygon2d.MakeCircle(10, 32)); //Polygon2d hole = Polygon2d.MakeCircle(9, 32); //hole.Reverse(); //poly.AddHole(hole); Polygon2d hole = Polygon2d.MakeCircle(5, 32); hole.Translate(new Vector2d(2, 0)); hole.Reverse(); poly.AddHole(hole); Polygon2d hole2 = Polygon2d.MakeCircle(1, 32); hole2.Translate(-6 * Vector2d.AxisX); hole2.Reverse(); poly.AddHole(hole2); Polygon2d hole3 = Polygon2d.MakeCircle(1, 32); hole3.Translate(-6 * Vector2d.One); hole3.Reverse(); poly.AddHole(hole3); Polygon2d hole4 = Polygon2d.MakeCircle(1, 32); hole4.Translate(7 * Vector2d.AxisY); hole4.Reverse(); poly.AddHole(hole4); view.AddPolygon(poly, Colorf.Black); double spacing = 0.2; //double[] offsets = new double[] { 0.5, 1, 1.5, 2, 2.5 }; double[] offsets = new double[] { 0.2, 0.6 }; TopoOffset2d o = new TopoOffset2d(poly) { PointSpacing = spacing }; foreach (double offset in offsets) { o.Offset = offset; DGraph2 graph = o.Compute(); DGraph2Util.Curves c = DGraph2Util.ExtractCurves(graph); view.AddGraph(graph, Colorf.Red); } window.Add(view); window.ShowAll(); }
/// <summary> /// Convert the input polygons to a set of paths. /// If FilterSelfOverlaps=true, then the paths will be clipped against /// themselves, in an attempt to avoid over-printing. /// </summary> public virtual FillCurveSet2d ShellPolysToPaths(List <GeneralPolygon2d> shell_polys, int nShell) { FillCurveSet2d paths = new FillCurveSet2d(); FillTypeFlags flags = FillTypeFlags.PerimeterShell; if (nShell == 0 && ShellType == ShellTypes.ExternalPerimeters) { flags = FillTypeFlags.OutermostShell; } else if (ShellType == ShellTypes.InternalShell) { flags = FillTypeFlags.InteriorShell; } else if (ShellType == ShellTypes.BridgeShell) { flags = FillTypeFlags.BridgeSupport; } if (FilterSelfOverlaps == false) { foreach (GeneralPolygon2d shell in shell_polys) { paths.Append(shell, flags); } return(paths); } int outer_shell_edgegroup = 100; foreach (GeneralPolygon2d shell in shell_polys) { PathOverlapRepair repair = new PathOverlapRepair(); repair.OverlapRadius = SelfOverlapTolerance; repair.Add(shell, outer_shell_edgegroup); // Ideally want to presreve outermost shell of external perimeters. // However in many cases internal holes are 'too close' to outer border. // So we will still apply to those, but use edge filter to preserve outermost loop. // [TODO] could we be smarter about this somehow? if (PreserveOuterShells && nShell == 0 && ShellType == ShellTypes.ExternalPerimeters) { repair.PreserveEdgeFilterF = (eid) => { return(repair.Graph.GetEdgeGroup(eid) == outer_shell_edgegroup); } } ; repair.Compute(); DGraph2Util.Curves c = DGraph2Util.ExtractCurves(repair.GetResultGraph()); foreach (var polygon in c.Loops) { paths.Append(polygon, flags); } foreach (var polyline in c.Paths) { if (polyline.ArcLength < DiscardTinyPerimterLengthMM) { continue; } if (polyline.Bounds.MaxDim < DiscardTinyPerimterLengthMM) { continue; } paths.Append(new FillPolyline2d(polyline) { TypeFlags = flags }); } } return(paths); }
/// <summary> /// Convert the input polygons to a set of paths. /// If FilterSelfOverlaps=true, then the paths will be clipped against /// themselves, in an attempt to avoid over-printing. /// </summary> public virtual FillCurveSet2d ShellPolysToPaths(List <GeneralPolygon2d> shell_polys, int nShell) { FillCurveSet2d paths = new FillCurveSet2d(); IFillType currentFillType = nShell == 0 ? firstShellFillType ?? fillType : fillType; if (FilterSelfOverlaps == false) { foreach (var shell in shell_polys) { paths.Append(new FillLoop <FillSegment>(shell.Outer.Vertices) { FillType = currentFillType, PerimeterOrder = nShell }); foreach (var hole in shell.Holes) { paths.Append(new FillLoop <FillSegment>(hole.Vertices) { FillType = currentFillType, PerimeterOrder = nShell, IsHoleShell = true });; } } return(paths); } int outer_shell_edgegroup = 100; foreach (GeneralPolygon2d shell in shell_polys) { PathOverlapRepair repair = new PathOverlapRepair(); repair.OverlapRadius = SelfOverlapTolerance; repair.Add(shell, outer_shell_edgegroup); // Ideally want to presreve outermost shell of external perimeters. // However in many cases internal holes are 'too close' to outer border. // So we will still apply to those, but use edge filter to preserve outermost loop. // [TODO] could we be smarter about this somehow? if (PreserveOuterShells && nShell == 0) { repair.PreserveEdgeFilterF = (eid) => { return(repair.Graph.GetEdgeGroup(eid) == outer_shell_edgegroup); } } ; repair.Compute(); DGraph2Util.Curves c = DGraph2Util.ExtractCurves(repair.GetResultGraph()); #region Borrow nesting calculations from PlanarSlice to enforce winding direction PlanarComplex complex = new PlanarComplex(); foreach (Polygon2d poly in c.Loops) { complex.Add(poly); } PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.Default; options.WantCurveSolids = false; options.SimplifyDeviationTolerance = 0.001; options.TrustOrientations = false; options.AllowOverlappingHoles = false; PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(options); foreach (var polygon in solids.Polygons) { polygon.EnforceCounterClockwise(); paths.Append(new FillLoop <FillSegment>(polygon.Outer.Vertices) { FillType = currentFillType, PerimeterOrder = nShell }); foreach (var hole in polygon.Holes) { paths.Append(new FillLoop <FillSegment>(hole.Vertices) { FillType = currentFillType, PerimeterOrder = nShell, IsHoleShell = true, }); } } #endregion Borrow nesting calculations from PlanarSlice to enforce winding direction foreach (var polyline in c.Paths) { if (polyline.ArcLength < DiscardTinyPerimeterLengthMM) { continue; } if (polyline.Bounds.MaxDim < DiscardTinyPerimeterLengthMM) { continue; } paths.Append(new FillCurve <FillSegment>(polyline) { FillType = currentFillType, PerimeterOrder = nShell }); } } return(paths); }