示例#1
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);
     }
 }
示例#2
0
        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);
        }
示例#3
0
 /// <summary>
 /// Collision polygons are fixed thickened paths we need to clip against
 /// </summary>
 public void AddCollisionConstraint(Polygon2d poly)
 {
     CollisionGraph.AppendPolygon(poly);
 }
示例#4
0
        /// <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);
        }
示例#5
0
        protected DGraph2 compute_result(GeneralPolygon2d poly, double fOffset, double fTargetSpacing)
        {
            double dt     = fTargetSpacing / 2;
            int    nSteps = (int)(Math.Abs(fOffset) / dt);

            if (nSteps < 10)
            {
                nSteps = 10;
            }

            // [TODO] we could cache this over multiple runs...
            DGraph2 graph = new DGraph2();

            graph.AppendPolygon(poly.Outer);
            foreach (var h in poly.Holes)
            {
                graph.AppendPolygon(h);
            }

            // resample to nbrhood of target spacing
            SplitToMaxEdgeLength(graph, fTargetSpacing * 1.33);

            // build bvtree for polygon
            if (poly_tree == null || poly_tree.Polygon != poly)
            {
                poly_tree = new GeneralPolygon2dBoxTree(poly);
            }

            // allocate and resize caches as necessary
            if (offset_cache == null)
            {
                offset_cache = new DVector <Vector2d>();
            }
            if (offset_cache.size < graph.VertexCount)
            {
                offset_cache.resize(graph.VertexCount * 2);
            }
            if (position_cache == null)
            {
                position_cache = new DVector <Vector2d>();
            }
            if (position_cache.size < graph.VertexCount)
            {
                position_cache.resize(graph.VertexCount * 2);
            }
            if (collapse_cache == null)
            {
                collapse_cache = new DVector <Vector2d>();
            }
            if (collapse_cache.size < graph.VertexCount)
            {
                collapse_cache.resize(graph.VertexCount * 2);
            }
            if (last_step_size == null)
            {
                last_step_size = new DVector <double>();
            }
            if (last_step_size.size < graph.VertexCount)
            {
                last_step_size.resize(graph.VertexCount * 2);
            }

            // insert all points into a hashgrid. We will dynamically update this grid as we proceed
            // [TODO] is this a good bounds-size?
            graph_cache = new PointHashGrid2d <int>(poly.Bounds.MaxDim / 64, -1);
            foreach (int vid in graph.VertexIndices())
            {
                graph_cache.InsertPoint(vid, graph.GetVertex(vid));
            }

            LocalProfiler p = (_enable_profiling) ? new LocalProfiler() : null;

            if (_enable_profiling)
            {
                p.Start("All");
            }

            // run a bunch of steps. The last few are tuning steps where we use half-steps,
            // which seems to help?
            int TUNE_STEPS = nSteps / 2;

            nSteps *= 2;
            for (int i = 0; i < nSteps; ++i)
            {
                if (_enable_profiling)
                {
                    p.Start("offset");
                }

                double step_dt = dt;
                if (i > nSteps - TUNE_STEPS)
                {
                    step_dt = dt / 2;
                }
                if (last_step_size.size < graph.VertexCount)
                {
                    last_step_size.resize(graph.VertexCount + 256);
                }

                // Each vertex steps forward. In fact we compute two steps and average them,
                // this helps w/ convergence. To produce more accurate convergence, we track
                // the size of the actual step we took at the last round, and use that the next
                // time. (The assumption is that the steps will get smaller at the target distance).
                gParallel.ForEach(graph.VertexIndices(), (vid) =>
                {
                    // use tracked step size if we have it
                    double use_dt = step_dt;
                    if (last_step_size[vid] > 0)
                    {
                        use_dt = Math.Min(last_step_size[vid], dt);
                    }

                    Vector2d cur_pos = graph.GetVertex(vid);
                    double err, err_2;
                    // take two sequential steps and average them. this vastly improves convergence.
                    Vector2d new_pos   = compute_offset_step(cur_pos, poly, fOffset, use_dt, out err);
                    Vector2d new_pos_2 = compute_offset_step(new_pos, poly, fOffset, use_dt, out err_2);

                    // weighted blend of points - prefer one w/ smaller error
                    //double w = 1.0 / Math.Max(err, MathUtil.ZeroTolerancef);
                    //double w_2 = 1.0 / Math.Max(err_2, MathUtil.ZeroTolerancef);
                    //new_pos = w * new_pos + w_2 * new_pos_2;
                    //new_pos /= (w + w_2);
                    // [RMS] weighted blend doesn't seem to matter if we are tracking per-vertex step size.
                    new_pos = Vector2d.Lerp(new_pos, new_pos_2, 0.5);

                    // keep track of actual step we are taking and use that next iteration
                    double actual_step_dist = cur_pos.Distance(new_pos);
                    if (last_step_size[vid] == 0)
                    {
                        last_step_size[vid] = actual_step_dist;
                    }
                    else
                    {
                        last_step_size[vid] = (0.75) * last_step_size[vid] + (0.25) * actual_step_dist;
                    }

                    // update point in hashtable and graph
                    graph_cache.UpdatePoint(vid, cur_pos, new_pos);
                    graph.SetVertex(vid, new_pos);
                });

                if (_enable_profiling)
                {
                    p.StopAndAccumulate("offset"); p.Start("smooth");
                }

                // Do a smoothing pass, but for the last few steps, reduce smoothing
                // (otherwise it pulls away from target solution)
                int smooth_steps = 5; double smooth_alpha = 0.75;
                if (i > nSteps - TUNE_STEPS)
                {
                    smooth_steps = 2; smooth_alpha = 0.25;
                }
                smooth_pass(graph, smooth_steps, smooth_alpha, fTargetSpacing / 2);

                if (_enable_profiling)
                {
                    p.StopAndAccumulate("smooth"); p.Start("join");
                }

                // if a vertex is within targetSpacing from another vertex, and they are
                // not geodesically connected in the graph, them we merge/weld them together.
                int joined = 0;
                do
                {
                    //joined = JoinInTolerance(graph, fMergeThresh);
                    //joined = JoinInTolerance_Parallel(graph, fMergeThresh);
                    joined = JoinInTolerance_Parallel_Cache(graph, fTargetSpacing);
                } while (joined > 0);

                if (_enable_profiling)
                {
                    p.StopAndAccumulate("join"); p.Start("refine");
                }

                // now do a pass of graph refinement, to collapse short edges and split long ones
                CollapseToMinEdgeLength(graph, fTargetSpacing * 0.66f);
                SplitToMaxEdgeLength(graph, fTargetSpacing * 1.33);

                if (_enable_profiling)
                {
                    p.StopAndAccumulate("refine");
                }
            }

            if (_enable_profiling)
            {
                p.Stop("All");
                System.Console.WriteLine("All: " + p.Elapsed("All"));
                System.Console.WriteLine(p.AllAccumulatedTimes());
            }

            // get rid of junction vertices, if requested
            if (DisconnectGraphJunctions)
            {
                DGraph2Util.DisconnectJunctions(graph);
            }

            return(graph);
        }
        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"));
        }
        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"));
        }