public static void test_splitter() { Polygon2d poly = Polygon2d.MakeCircle(1000, 16); Polygon2d hole = Polygon2d.MakeCircle(500, 32); hole.Reverse(); GeneralPolygon2d gpoly = new GeneralPolygon2d(poly); gpoly.AddHole(hole); //Polygon2d poly = Polygon2d.MakeRectangle(Vector2d.Zero, 1000, 1000); DGraph2 graph = new DGraph2(); graph.AppendPolygon(gpoly); System.Console.WriteLine("Stats before: verts {0} edges {1} ", graph.VertexCount, graph.EdgeCount); GraphSplitter2d splitter = new GraphSplitter2d(graph); splitter.InsideTestF = gpoly.Contains; for (int k = 0; k < poly.VertexCount; ++k) { Line2d line = new Line2d(poly[k], Vector2d.AxisY); splitter.InsertLine(line); } System.Console.WriteLine("Stats after 1: verts {0} edges {1} ", graph.VertexCount, graph.EdgeCount); for (int k = 0; k < poly.VertexCount; ++k) { Line2d line = new Line2d(poly[k], Vector2d.AxisX); splitter.InsertLine(line); } for (int k = 0; k < poly.VertexCount; ++k) { Line2d line = new Line2d(poly[k], Vector2d.One.Normalized); splitter.InsertLine(line); } for (int k = 0; k < poly.VertexCount; ++k) { Line2d line = new Line2d(poly[k], new Vector2d(1, -1).Normalized); splitter.InsertLine(line); } System.Console.WriteLine("Stats after: verts {0} edges {1} ", graph.VertexCount, graph.EdgeCount); Random r = new Random(31337); foreach (int vid in graph.VertexIndices()) { Vector2d v = graph.GetVertex(vid); v += TestUtil.RandomPoints2(1, r, v, 25)[0]; graph.SetVertex(vid, v); } SVGWriter svg = new SVGWriter(); svg.AddGraph(graph); var vtx_style = SVGWriter.Style.Outline("red", 1.0f); foreach (int vid in graph.VertexIndices()) { Vector2d v = graph.GetVertex(vid); svg.AddCircle(new Circle2d(v, 10), vtx_style); } svg.Write(TestUtil.GetTestOutputPath("split_graph.svg")); }
/// <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 static void test_cells() { Polygon2d outer = Polygon2d.MakeCircle(1000, 17); Polygon2d hole = Polygon2d.MakeCircle(100, 32); hole.Reverse(); GeneralPolygon2d gpoly = new GeneralPolygon2d(outer); gpoly.AddHole(hole); DGraph2 graph = new DGraph2(); graph.AppendPolygon(gpoly); GraphSplitter2d splitter = new GraphSplitter2d(graph); splitter.InsideTestF = gpoly.Contains; for (int k = 0; k < outer.VertexCount; ++k) { Line2d line = new Line2d(outer[k], Vector2d.AxisY); splitter.InsertLine(line); } for (int k = 0; k < outer.VertexCount; ++k) { Line2d line = new Line2d(outer[k], Vector2d.AxisX); splitter.InsertLine(line); } for (int k = 0; k < outer.VertexCount; ++k) { Line2d line = new Line2d(outer[k], Vector2d.One.Normalized); splitter.InsertLine(line); } for (int k = 0; k < outer.VertexCount; ++k) { Line2d line = new Line2d(outer[k], new Vector2d(1, -1).Normalized); splitter.InsertLine(line); } GraphCells2d cells = new GraphCells2d(graph); cells.FindCells(); List <Polygon2d> polys = cells.ContainedCells(gpoly); for (int k = 0; k < polys.Count; ++k) { double offset = polys[k].IsClockwise ? 4 : 20; polys[k].PolyOffset(offset); } PlanarComplex cp = new PlanarComplex(); for (int k = 0; k < polys.Count; ++k) { cp.Add(polys[k]); } // convert back to solids var options = PlanarComplex.FindSolidsOptions.Default; options.WantCurveSolids = false; options.SimplifyDeviationTolerance = 0; var solids = cp.FindSolidRegions(options); SVGWriter svg = new SVGWriter(); svg.AddGraph(graph, SVGWriter.Style.Outline("red", 5)); for (int k = 0; k < polys.Count; ++k) { svg.AddPolygon(polys[k], SVGWriter.Style.Outline("black", 1)); } svg.Write(TestUtil.GetTestOutputPath("cells_graph.svg")); }