Example #1
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;
        }
Example #2
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));
        }
Example #3
0
 public static void WriteTestOutputGraph(DGraph2 graph, string sFilename, double vtxRadius = 2.5)
 {
     WriteTestOutputGraphs(new List <DGraph2>()
     {
         graph
     }, sFilename, SVGWriter.Style.Outline("black", 1.0f),
                           SVGWriter.Style.Outline("red", 1.0f), vtxRadius);
 }
Example #4
0
        public static void UpdateOffsetAnimation()
        {
            AnimOffset += 0.1f;
            DGraph2 graph = TopoOffset2d.QuickCompute(Outer, AnimOffset, AnimSpacing);

            Active.Graphs.Clear();
            Active.AddGraph(graph, Colorf.Red);
        }
Example #5
0
        // split edges longer than fMinLen
        // NOTE: basically the same as DGraph2Resampler.SplitToMaxEdgeLength, but updates
        // our internal caches. Could we merge somehow?
        protected void SplitToMaxEdgeLength(DGraph2 graph, double fMaxLen)
        {
            List <int> queue = new List <int>();
            int        NE    = graph.MaxEdgeID;

            for (int eid = 0; eid < NE; ++eid)
            {
                if (!graph.IsEdge(eid))
                {
                    continue;
                }
                Index2i ev   = graph.GetEdgeV(eid);
                double  dist = graph.GetVertex(ev.a).Distance(graph.GetVertex(ev.b));
                if (dist > fMaxLen)
                {
                    DGraph2.EdgeSplitInfo splitInfo;
                    if (graph.SplitEdge(eid, out splitInfo) == MeshResult.Ok)
                    {
                        if (graph_cache != null)
                        {
                            graph_cache.InsertPointUnsafe(splitInfo.vNew, graph.GetVertex(splitInfo.vNew));
                        }
                        if (dist > 2 * fMaxLen)
                        {
                            queue.Add(eid);
                            queue.Add(splitInfo.eNewBN);
                        }
                    }
                }
            }
            while (queue.Count > 0)
            {
                int eid = queue[queue.Count - 1];
                queue.RemoveAt(queue.Count - 1);
                if (!graph.IsEdge(eid))
                {
                    continue;
                }
                Index2i ev   = graph.GetEdgeV(eid);
                double  dist = graph.GetVertex(ev.a).Distance(graph.GetVertex(ev.b));
                if (dist > fMaxLen)
                {
                    DGraph2.EdgeSplitInfo splitInfo;
                    if (graph.SplitEdge(eid, out splitInfo) == MeshResult.Ok)
                    {
                        if (graph_cache != null)
                        {
                            graph_cache.InsertPointUnsafe(splitInfo.vNew, graph.GetVertex(splitInfo.vNew));
                        }
                        if (dist > 2 * fMaxLen)
                        {
                            queue.Add(eid);
                            queue.Add(splitInfo.eNewBN);
                        }
                    }
                }
            }
        }
Example #6
0
        private DGraph2 FilterPathGraphSelfOverlaps(DGraph2 pathGraph)
        {
            PathOverlapRepair repair = new PathOverlapRepair(pathGraph);

            repair.OverlapRadius       = ToolWidth * SelfOverlapToolWidthX;
            repair.PreserveEdgeFilterF = (eid) =>
            {
                return(repair.Graph.GetEdgeGroup(eid) > 0);
            };
            repair.Compute();
            pathGraph = repair.GetResultGraph();
            return(pathGraph);
        }
Example #7
0
        /// <summary>
        /// Assumption is that input graph is a polygon with inserted ray-spans. We want to
        /// find a set of paths (ie no junctions) that cover all the spans, and travel between
        /// adjacent spans along edges of the input polygon.
        /// </summary>
        protected DGraph2 BuildPathGraph(DGraph2 input)
        {
            int NV = input.MaxVertexID;

            var minGraph  = CreateMinGraph(input, NV, out var MinEdgePaths, out var EdgeWeights);
            var pathGraph = CreatePathGraph(input, minGraph, MinEdgePaths, EdgeWeights);

            if (exportToSVG)
            {
                ExportToSVG(input, minGraph, pathGraph);
            }

            return(pathGraph);
        }
        public static SKPath ToSKPath(DGraph2 g, Func <Vector2d, SKPoint> mapF)
        {
            SKPath p = new SKPath();

            foreach (Index3i edge in g.Edges())
            {
                Vector2d a = g.GetVertex(edge.a);
                Vector2d b = g.GetVertex(edge.b);
                p.MoveTo(mapF(a));
                p.LineTo(mapF(b));
                p.Close();
            }

            return(p);
        }
Example #9
0
 public static void smooth_pass(DGraph2 graph, float alpha, Vector2d[] newPos)
 {
     foreach (int vid in graph.VertexIndices())
     {
         Vector2d v = graph.GetVertex(vid);
         bool     isvalid;
         Vector2d l = DGraph2Util.VertexLaplacian(graph, vid, out isvalid);
         v          += alpha * l;
         newPos[vid] = v;
     }
     foreach (int vid in graph.VertexIndices())
     {
         graph.SetVertex(vid, newPos[vid]);
     }
 }
Example #10
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);
        }
Example #11
0
        private static HashSet <int> IdentifyBoundaryHashSet(DGraph2 pathGraph)
        {
            HashSet <int> boundaries = new HashSet <int>();

            foreach (int vid in pathGraph.VertexIndices())
            {
                if (pathGraph.IsBoundaryVertex(vid))
                {
                    boundaries.Add(vid);
                }
                if (pathGraph.IsJunctionVertex(vid))
                {
                    throw new Exception("DenseLinesFillPolygon: PathGraph has a junction???");
                }
            }

            return(boundaries);
        }
Example #12
0
        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;
        }
        void DrawGraph(SKCanvas canvas, SKPaint paint, DGraph2 graph, Func <Vector2d, SKPoint> mapF)
        {
            Colorf color = Colorf.Red;

            if (Colors.ContainsKey(graph))
            {
                color = Colors[graph];
            }
            paint.Color = SkiaUtil.Color(color);

            SKPath path = SkiaUtil.ToSKPath(graph, mapF);

            paint.StrokeWidth = 2;
            canvas.DrawPath(path, paint);

            paint.StrokeWidth = 1;
            //paint.Color = SKColors.Black;
            foreach (Vector2d v in graph.Vertices())
            {
                SKPoint c = mapF(v);
                canvas.DrawCircle(c.X, c.Y, 3.0f, paint);
            }
        }
Example #14
0
        private static void ExportToSVG(DGraph2 input, DGraph2 minGraph, DGraph2 pathGraph)
        {
            SVGWriter writer = new SVGWriter();

            writer.AddGraph(input, SVGWriter.Style.Outline("blue", 0.1f));
            writer.AddGraph(minGraph, SVGWriter.Style.Outline("red", 0.1f));
            foreach (int eid in minGraph.EdgeIndices())
            {
                if (minGraph.GetEdgeGroup(eid) >= 0)
                {
                    writer.AddLine(minGraph.GetEdgeSegment(eid), SVGWriter.Style.Outline("green", 0.07f));
                }
            }
            writer.AddGraph(pathGraph, SVGWriter.Style.Outline("black", 0.03f));
            foreach (int vid in pathGraph.VertexIndices())
            {
                if (pathGraph.IsBoundaryVertex(vid))
                {
                    writer.AddCircle(new Circle2d(pathGraph.GetVertex(vid), 0.5f), SVGWriter.Style.Outline("blue", 0.03f));
                }
            }
            writer.Write("MIN_GRAPH.svg");
        }
Example #15
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);
        }
Example #16
0
        private static DGraph2 CreateMinGraph(DGraph2 input, int NV, out Dictionary <int, List <int> > minEdgePaths, out DVector <double> edgeWeights)
        {
            /*
             * OK, as input we have a graph of our original polygon and a bunch of inserted
             * segments ("spans"). Orig polygon segments have gid < 0, and span segments >= 0.
             * However between polygon/span junctions, we have an arbitrary # of polygon edges.
             * So first step is to simplify these to single-edge "connectors", in new graph MinGraph.
             * the [connector-edge, path] mappings (if pathlen > 1) are stored in MinEdgePaths
             * We also store a weight for each connector edge in EdgeWeights (just distance for now)
             */

            var minGraph = new DGraph2();

            minEdgePaths = new Dictionary <int, List <int> >();
            edgeWeights  = new DVector <double>();
            edgeWeights.resize(NV);
            BitArray done_edge = new BitArray(input.MaxEdgeID);  // we should see each edge twice, this avoids repetition

            // vertex map from input graph to MinGraph
            int[] MapV = new int[NV];
            for (int i = 0; i < NV; ++i)
            {
                MapV[i] = -1;
            }

            for (int a = 0; a < NV; ++a)
            {
                if (input.IsVertex(a) == false || input.IsJunctionVertex(a) == false)
                {
                    continue;
                }

                if (MapV[a] == -1)
                {
                    MapV[a] = minGraph.AppendVertex(input.GetVertex(a));
                }

                foreach (int eid in input.VtxEdgesItr(a))
                {
                    if (done_edge[eid])
                    {
                        continue;
                    }

                    Index2i ev = input.GetEdgeV(eid);
                    int     b  = (ev.a == a) ? ev.b : ev.a;

                    if (input.IsJunctionVertex(b))
                    {
                        // if we have junction/juntion connection, we can just copy this edge to MinGraph

                        if (MapV[b] == -1)
                        {
                            MapV[b] = minGraph.AppendVertex(input.GetVertex(b));
                        }

                        int gid      = input.GetEdgeGroup(eid);
                        int existing = minGraph.FindEdge(MapV[a], MapV[b]);
                        if (existing == DMesh3.InvalidID)
                        {
                            int    new_eid  = minGraph.AppendEdge(MapV[a], MapV[b], gid);
                            double path_len = input.GetEdgeSegment(eid).Length;
                            edgeWeights.insertAt(path_len, new_eid);
                        }
                        else
                        {
                            // we may have inserted this edge already in the simplify branch, this happens eg at the
                            // edge of a circle where the minimal path is between the same vertices as the segment.
                            // But if this is also a fill edge, we want to treat it that way (determind via positive gid)
                            if (gid >= 0)
                            {
                                minGraph.SetEdgeGroup(existing, gid);
                            }
                        }
                    }
                    else
                    {
                        // not a junction - walk until we find other vtx, and add single edge to MinGraph
                        List <int> path = DGraph2Util.WalkToNextNonRegularVtx(input, a, eid);
                        if (path == null || path.Count < 2)
                        {
                            throw new Exception("build_min_graph: invalid walk!");
                        }

                        int c = path[path.Count - 1];

                        // it is somehow possible to get loops...
                        if (c == a)
                        {
                            goto skip_this_edge;
                        }

                        if (MapV[c] == -1)
                        {
                            MapV[c] = minGraph.AppendVertex(input.GetVertex(c));
                        }

                        if (minGraph.FindEdge(MapV[a], MapV[c]) == DMesh3.InvalidID)
                        {
                            int new_eid = minGraph.AppendEdge(MapV[a], MapV[c], -2);
                            path.Add(MapV[a]); path.Add(MapV[c]);
                            minEdgePaths[new_eid] = path;
                            double path_len = DGraph2Util.PathLength(input, path);
                            edgeWeights.insertAt(path_len, new_eid);
                        }
                    }

skip_this_edge:
                    done_edge[eid] = true;
                }
            }
            return(minGraph);
        }
Example #17
0
        private DGraph2 CreatePathGraph(DGraph2 input, DGraph2 minGraph, Dictionary <int, List <int> > MinEdgePaths, DVector <double> EdgeWeights)
        {
            // [TODO] filter MinGraph to remove invalid connectors
            //    - can a connector between two connectors happen? that would be bad.
            //    - connector that is too close to paths should be ignored (ie avoid collisions)

            /*
             * Now that we have MinGraph, we can easily walk between the spans because
             * they are connected by at most one edge. To find a sequence of spans, we
             * pick one to start, then walk along connectors, discarding as we go,
             * so that we don't pass through these vertices again. Repeat until
             * there are no remaining spans.
             */

            // [TODO]
            //  do we actually have to delete from MinGraph? this prevents us from doing
            //  certain things, like trying different options. Maybe could use a hash for
            //  remaining vertices and edges instead?

            var pathGraph = new DGraph2();
            var sortAxis  = Vector2d.FromAngleDeg(AngleDeg).Perp;

            while (true)
            {
                // find most extreme edge to start at
                // [TODO] could use segment gid here as we set them based on insertion span!
                // [TODO] could use a smarter metric? like, closest to previous last endpoint? Using
                //   extrema like this tends to produce longest spans, though...
                double min_dot   = double.MaxValue;
                int    start_eid = -1;
                foreach (int eid in minGraph.EdgeIndices())
                {
                    Index3i evg = minGraph.GetEdge(eid);
                    if (evg.c >= 0)
                    {
                        double dot = minGraph.GetVertex(evg.a).Dot(sortAxis);
                        if (dot < min_dot)
                        {
                            min_dot   = dot;
                            start_eid = eid;
                        }
                    }
                }
                if (start_eid == -1)
                {
                    break;   // if we could not find a start edge, we must be done!
                }
                // ok now walk forward through connectors and spans. We do this in
                // connector/span pairs - we are always at an end-of-span point, and
                // we pick a next-connector and then a next-span.
                // We need to keep track of vertices in both the pathgraph and mingraph,
                // these are the "new" and "old" vertices
                Index3i start_evg = minGraph.GetEdge(start_eid);
                int     new_start = pathGraph.AppendVertex(minGraph.GetVertex(start_evg.a));
                int     new_prev  = pathGraph.AppendVertex(minGraph.GetVertex(start_evg.b));
                int     old_prev  = start_evg.b;
                pathGraph.AppendEdge(new_start, new_prev, start_evg.c);
                minGraph.RemoveVertex(start_evg.a, true);
                while (true)
                {
                    // choose next connector edge, outgoing from current vtx
                    int connector_e = -1;
                    foreach (int eid in minGraph.VtxEdgesItr(old_prev))
                    {
                        Index3i evg = minGraph.GetEdge(eid);
                        if (evg.c >= 0)
                        {
                            continue;  // what??
                        }
                        if (connector_e == -1 || EdgeWeights[connector_e] > EdgeWeights[eid])
                        {
                            connector_e = eid;
                        }
                    }
                    if (connector_e == -1)
                    {
                        break;
                    }

                    // find the vertex at end of connector
                    Index3i conn_evg   = minGraph.GetEdge(connector_e);
                    int     old_conn_v = (conn_evg.a == old_prev) ? conn_evg.b : conn_evg.a;

                    // can never look at prev vertex again, or any edges connected to it
                    // [TODO] are we sure none of these edges are unused spans?!?
                    minGraph.RemoveVertex(old_prev, true);

                    // now find outgoing span edge
                    int span_e = -1;
                    foreach (int eid in minGraph.VtxEdgesItr(old_conn_v))
                    {
                        Index3i evg = minGraph.GetEdge(eid);
                        if (evg.c >= 0)
                        {
                            span_e = eid;
                            break;
                        }
                    }
                    if (span_e == -1)
                    {
                        break;   // disaster!
                    }
                    // find vertex at far end of span
                    Index3i span_evg   = minGraph.GetEdge(span_e);
                    int     old_span_v = (span_evg.a == old_conn_v) ? span_evg.b : span_evg.a;

                    // ok we want to insert the connectr to the path graph, however the
                    // connector might actually have come from a more complex path in the input graph.
                    int new_conn_next;
                    if (MinEdgePaths.ContainsKey(connector_e))
                    {
                        // complex path case. Note that the order [old_prev, old_conn_v] may be the opposite
                        // of the order in the pathv. But above, we appended the [a,b] edge order to the pathv.
                        // So we can check if we need to flip, but this means we need to be a bit clever w/ indices...
                        List <int> pathv     = MinEdgePaths[connector_e];
                        int        N         = pathv.Count;
                        int        path_prev = new_prev;
                        int        k         = 1;
                        if (pathv[N - 2] != old_prev)
                        {   // case where order flipped
                            pathv.Reverse();
                            k = 3;
                        }
                        else
                        {
                            N = N - 2;
                        }
                        while (k < N)
                        {
                            int path_next = pathGraph.AppendVertex(input.GetVertex(pathv[k]));
                            pathGraph.AppendEdge(path_prev, path_next);
                            path_prev = path_next;
                            k++;
                        }
                        new_conn_next = path_prev;
                    }
                    else
                    {
                        new_conn_next = pathGraph.AppendVertex(minGraph.GetVertex(old_conn_v));
                        pathGraph.AppendEdge(new_prev, new_conn_next, conn_evg.c);
                    }

                    // add span to path
                    int new_fill_next = pathGraph.AppendVertex(minGraph.GetVertex(old_span_v));
                    pathGraph.AppendEdge(new_conn_next, new_fill_next, span_evg.c);

                    // remove the connector vertex
                    minGraph.RemoveVertex(old_conn_v, true);

                    // next iter starts at far end of span
                    new_prev = new_fill_next;
                    old_prev = old_span_v;
                }

                sortAxis = -sortAxis;
            }

            return(pathGraph);
        }
Example #18
0
        // join disconnected vertices within distance threshold
        protected int JoinInTolerance_Parallel(DGraph2 graph, double fMergeDist)
        {
            double mergeSqr = fMergeDist * fMergeDist;

            int NV = graph.MaxVertexID;

            if (collapse_cache.size < NV)
            {
                collapse_cache.resize(NV);
            }

            gParallel.ForEach(Interval1i.Range(NV), (a) =>
            {
                collapse_cache[a] = new Vector2d(-1, double.MaxValue);
                if (!graph.IsVertex(a))
                {
                    return;
                }

                Vector2d va = graph.GetVertex(a);

                int bNearest       = -1;
                double nearDistSqr = double.MaxValue;
                for (int b = a + 1; b < NV; ++b)
                {
                    if (b == a || graph.IsVertex(b) == false)
                    {
                        continue;
                    }
                    double distsqr = va.DistanceSquared(graph.GetVertex(b));
                    if (distsqr < mergeSqr && distsqr < nearDistSqr)
                    {
                        if (graph.FindEdge(a, b) == DGraph2.InvalidID)
                        {
                            nearDistSqr = distsqr;
                            bNearest    = b;
                        }
                    }
                }
                if (bNearest != -1)
                {
                    collapse_cache[a] = new Vector2d(bNearest, nearDistSqr);
                }
            });

            // [TODO] sort

            int merged = 0;

            for (int a = 0; a < NV; ++a)
            {
                if (collapse_cache[a].x == -1)
                {
                    continue;
                }

                int bNearest = (int)collapse_cache[a].x;

                Vector2d pos_a        = graph.GetVertex(a);
                Vector2d pos_bNearest = graph.GetVertex(bNearest);

                /*int eid = */
                graph.AppendEdge(a, bNearest);
                DGraph2.EdgeCollapseInfo collapseInfo;
                graph.CollapseEdge(bNearest, a, out collapseInfo);
                graph_cache.RemovePointUnsafe(a, pos_a);
                last_step_size[a] = 0;
                graph_cache.UpdatePointUnsafe(bNearest, pos_bNearest, graph.GetVertex(bNearest));
                merged++;
            }
            return(merged);
        }
Example #19
0
        // smooth vertices, but don't move further than max_move
        protected void smooth_pass(DGraph2 graph, int passes, double smooth_alpha, double max_move)
        {
            double             max_move_sqr = max_move * max_move;
            int                NV           = graph.MaxVertexID;
            DVector <Vector2d> smoothedV    = offset_cache;

            if (smoothedV.size < NV)
            {
                smoothedV.resize(NV);
            }

            if (position_cache.size < NV)
            {
                position_cache.resize(NV);
            }

            for (int pi = 0; pi < passes; ++pi)
            {
                gParallel.ForEach(Interval1i.Range(NV), (vid) =>
                {
                    if (!graph.IsVertex(vid))
                    {
                        return;
                    }
                    Vector2d v = graph.GetVertex(vid);
                    Vector2d c = Vector2d.Zero;
                    int n      = 0;
                    foreach (int vnbr in graph.VtxVerticesItr(vid))
                    {
                        c += graph.GetVertex(vnbr);
                        n++;
                    }
                    if (n >= 2)
                    {
                        c          /= n;
                        Vector2d dv = (smooth_alpha) * (c - v);
                        if (dv.LengthSquared > max_move_sqr)
                        {
                            /*double d = */
                            dv.Normalize();
                            dv *= max_move;
                        }
                        v += dv;
                    }
                    smoothedV[vid] = v;
                });

                if (pi == 0)
                {
                    for (int vid = 0; vid < NV; ++vid)
                    {
                        if (graph.IsVertex(vid))
                        {
                            position_cache[vid] = graph.GetVertex(vid);
                            graph.SetVertex(vid, smoothedV[vid]);
                        }
                    }
                }
                else
                {
                    for (int vid = 0; vid < NV; ++vid)
                    {
                        if (graph.IsVertex(vid))
                        {
                            graph.SetVertex(vid, smoothedV[vid]);
                        }
                    }
                }
            }

            for (int vid = 0; vid < NV; ++vid)
            {
                if (graph.IsVertex(vid))
                {
                    graph_cache.UpdatePointUnsafe(vid, position_cache[vid], smoothedV[vid]);
                }
            }
        }
Example #20
0
 public DGraph2 Compute()
 {
     reset_caches();
     Graph = compute_result(Polygon, Offset, PointSpacing);
     return(Graph);
 }
Example #21
0
        private FillCurveSet2d WalkPathGraph(DGraph2 pathGraph)
        {
            var boundaries = IdentifyBoundaryHashSet(pathGraph);

            var paths = new FillCurveSet2d();

            // walk paths from boundary vertices
            while (boundaries.Count > 0)
            {
                int start_vid = boundaries.First();
                boundaries.Remove(start_vid);
                int vid = start_vid;
                int eid = pathGraph.GetVtxEdges(vid)[0];

                var path = new FillCurve <FillSegment>()
                {
                    FillType = this.FillType
                };

                path.BeginCurve(pathGraph.GetVertex(vid));
                while (true)
                {
                    Index2i next = DGraph2Util.NextEdgeAndVtx(eid, vid, pathGraph);
                    eid = next.a;
                    vid = next.b;
                    int gid = pathGraph.GetEdgeGroup(eid);
                    if (gid < 0)
                    {
                        path.AddToCurve(pathGraph.GetVertex(vid), new FillSegment(true));
                    }
                    else
                    {
                        path.AddToCurve(pathGraph.GetVertex(vid));
                    }

                    if (boundaries.Contains(vid))
                    {
                        boundaries.Remove(vid);
                        break;
                    }
                }

                // discard paths that are too short
                if (path.TotalLength() < MinPathLengthMM)
                {
                    continue;
                }

                // run polyline simplification to get rid of unneccesary detail in connectors
                // [TODO] we could do this at graph level...)
                // [TODO] maybe should be checking for collisions? we could end up creating
                //  non-trivial overlaps here...
                if (SimplifyAmount != SimplificationLevel.None && path.Elements.Count > 1)
                {
                    path = SimplifyPath(path);
                }

                paths.Append(path);
            }
            return(paths);
        }
Example #22
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?)
            //GeneralPolygon2d smoothed = poly.Duplicate();
            //CurveUtils2.LaplacianSmoothConstrained(smoothed, 0.5, 5, ToolWidth / 2, true, false);
            //poly = smoothed;

            // 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)
            {
                PathOverlapRepair repair = new PathOverlapRepair(pathGraph);
                repair.OverlapRadius       = ToolWidth * SelfOverlapToolWidthX;
                repair.PreserveEdgeFilterF = (eid) => {
                    return(repair.Graph.GetEdgeGroup(eid) > 0);
                };
                repair.Compute();
                pathGraph = repair.GetResultGraph();
            }


            HashSet <int> boundaries = new HashSet <int>();

            foreach (int vid in pathGraph.VertexIndices())
            {
                if (pathGraph.IsBoundaryVertex(vid))
                {
                    boundaries.Add(vid);
                }
                if (pathGraph.IsJunctionVertex(vid))
                {
                    throw new Exception("DenseLinesFillPolygon: PathGraph has a junction???");
                }
            }

            // walk paths from boundary vertices
            while (boundaries.Count > 0)
            {
                int start_vid = boundaries.First();
                boundaries.Remove(start_vid);
                int vid = start_vid;
                int eid = pathGraph.GetVtxEdges(vid)[0];

                FillPolyline2d path = new FillPolyline2d()
                {
                    TypeFlags = this.TypeFlags
                };

                path.AppendVertex(pathGraph.GetVertex(vid));
                while (true)
                {
                    Index2i next = DGraph2Util.NextEdgeAndVtx(eid, vid, pathGraph);
                    eid = next.a;
                    vid = next.b;
                    int gid = pathGraph.GetEdgeGroup(eid);
                    if (gid < 0)
                    {
                        path.AppendVertex(pathGraph.GetVertex(vid), TPVertexFlags.IsConnector);
                    }
                    else
                    {
                        path.AppendVertex(pathGraph.GetVertex(vid));
                    }

                    if (boundaries.Contains(vid))
                    {
                        boundaries.Remove(vid);
                        break;
                    }
                }

                // discard paths that are too short
                if (path.ArcLength < MinPathLengthMM)
                {
                    continue;
                }


                // run polyline simplification to get rid of unneccesary detail in connectors
                // [TODO] we could do this at graph level...)
                // [TODO] maybe should be checkign for collisions? we could end up creating
                //  non-trivial overlaps here...
                if (SimplifyAmount != SimplificationLevel.None && path.VertexCount > 2)
                {
                    PolySimplification2 simp = new PolySimplification2(path);
                    switch (SimplifyAmount)
                    {
                    default:
                    case SimplificationLevel.Minor:
                        simp.SimplifyDeviationThreshold = ToolWidth / 4; break;

                    case SimplificationLevel.Aggressive:
                        simp.SimplifyDeviationThreshold = ToolWidth; break;

                    case SimplificationLevel.Moderate:
                        simp.SimplifyDeviationThreshold = ToolWidth / 2; break;
                    }
                    simp.Simplify();
                    path = new FillPolyline2d(simp.Result.ToArray())
                    {
                        TypeFlags = this.TypeFlags
                    };
                }

                paths.Append(path);
            }


            // Check to make sure that we are not putting way too much material in the
            // available volume. Computes extrusion volume from path length and if the
            // ratio is too high, scales down the path thickness
            // TODO: do we need to compute volume? If we just divide everything by
            // height we get the same scaling, no? Then we don't need layer height.
            if (MaxOverfillRatio > 0)
            {
                throw new NotImplementedException("this is not finished yet");
#if false
                double LayerHeight = 0.2;               // AAAHHH hardcoded nonono

                double len         = paths.TotalLength();
                double extrude_vol = ExtrusionMath.PathLengthToVolume(LayerHeight, ToolWidth, len);
                double polygon_vol = LayerHeight * Math.Abs(poly.Area);
                double ratio       = extrude_vol / polygon_vol;

                if (ratio > MaxOverfillRatio && PathSpacing == ToolWidth)
                {
                    double use_width = ExtrusionMath.WidthFromTargetVolume(LayerHeight, len, polygon_vol);
                    //System.Console.WriteLine("Extrusion volume: {0}   PolyVolume: {1}   % {2}   ScaledWidth: {3}",
                    //extrude_vol, polygon_vol, extrude_vol / polygon_vol, use_width);

                    foreach (var path in paths.Curves)
                    {
                        path.CustomThickness = use_width;
                    }
                }
#endif
            }

            return(paths);
        }
Example #23
0
        /// <summary>
        /// Assumption is that input graph is a polygon with inserted ray-spans. We want to
        /// find a set of paths (ie no junctions) that cover all the spans, and travel between
        /// adjacent spans along edges of the input polygon.
        /// </summary>
        protected DGraph2 BuildPathGraph(DGraph2 input)
        {
            int NV = input.MaxVertexID;

            /*
             * OK, as input we have a graph of our original polygon and a bunch of inserted
             * segments ("spans"). Orig polygon segments have gid < 0, and span segments >= 0.
             * However between polygon/span junctions, we have an arbitrary # of polygon edges.
             * So first step is to simplify these to single-edge "connectors", in new graph MinGraph.
             * the [connector-edge, path] mappings (if pathlen > 1) are stored in MinEdgePaths
             * We also store a weight for each connector edge in EdgeWeights (just distance for now)
             */

            DGraph2 MinGraph = new DGraph2();
            Dictionary <int, List <int> > MinEdgePaths = new Dictionary <int, List <int> >();
            DVector <double> EdgeWeights = new DVector <double>(); EdgeWeights.resize(NV);
            BitArray         done_edge   = new BitArray(input.MaxEdgeID); // we should see each edge twice, this avoids repetition

            // vertex map from input graph to MinGraph
            int[] MapV = new int[NV];
            for (int i = 0; i < NV; ++i)
            {
                MapV[i] = -1;
            }

            for (int a = 0; a < NV; ++a)
            {
                if (input.IsVertex(a) == false || input.IsJunctionVertex(a) == false)
                {
                    continue;
                }

                if (MapV[a] == -1)
                {
                    MapV[a] = MinGraph.AppendVertex(input.GetVertex(a));
                }

                foreach (int eid in input.VtxEdgesItr(a))
                {
                    if (done_edge[eid])
                    {
                        continue;
                    }

                    Index2i ev = input.GetEdgeV(eid);
                    int     b  = (ev.a == a) ? ev.b : ev.a;

                    if (input.IsJunctionVertex(b))
                    {
                        // if we have junction/juntion connection, we can just copy this edge to MinGraph

                        if (MapV[b] == -1)
                        {
                            MapV[b] = MinGraph.AppendVertex(input.GetVertex(b));
                        }

                        int gid      = input.GetEdgeGroup(eid);
                        int existing = MinGraph.FindEdge(MapV[a], MapV[b]);
                        if (existing == DMesh3.InvalidID)
                        {
                            int    new_eid  = MinGraph.AppendEdge(MapV[a], MapV[b], gid);
                            double path_len = input.GetEdgeSegment(eid).Length;
                            EdgeWeights.insertAt(path_len, new_eid);
                        }
                        else
                        {
                            // we may have inserted this edge already in the simplify branch, this happens eg at the
                            // edge of a circle where the minimal path is between the same vertices as the segment.
                            // But if this is also a fill edge, we want to treat it that way (determind via positive gid)
                            if (gid >= 0)
                            {
                                MinGraph.SetEdgeGroup(existing, gid);
                            }
                        }
                    }
                    else
                    {
                        // not a junction - walk until we find other vtx, and add single edge to MinGraph
                        List <int> path = DGraph2Util.WalkToNextNonRegularVtx(input, a, eid);
                        if (path == null || path.Count < 2)
                        {
                            throw new Exception("build_min_graph: invalid walk!");
                        }

                        int c = path[path.Count - 1];

                        // it is somehow possible to get loops...
                        if (c == a)
                        {
                            goto skip_this_edge;
                        }

                        if (MapV[c] == -1)
                        {
                            MapV[c] = MinGraph.AppendVertex(input.GetVertex(c));
                        }

                        if (MinGraph.FindEdge(MapV[a], MapV[c]) == DMesh3.InvalidID)
                        {
                            int new_eid = MinGraph.AppendEdge(MapV[a], MapV[c], -2);
                            path.Add(MapV[a]); path.Add(MapV[c]);
                            MinEdgePaths[new_eid] = path;
                            double path_len = DGraph2Util.PathLength(input, path);
                            EdgeWeights.insertAt(path_len, new_eid);
                        }
                    }

skip_this_edge:
                    done_edge[eid] = true;
                }
            }


            // [TODO] filter MinGraph to remove invalid connectors
            //    - can a connector between two connectors happen? that would be bad.
            ///   - connector that is too close to paths should be ignored (ie avoid collisions)


            /*
             * Now that we have MinGraph, we can easily walk between the spans because
             * they are connected by at most one edge. To find a sequence of spans, we
             * pick one to start, then walk along connectors, discarding as we go,
             * so that we don't pass through these vertices again. Repeat until
             * there are no remaining spans.
             */

            // [TODO]
            //  do we actually have to delete from MinGraph? this prevents us from doing
            //  certain things, like trying different options. Maybe could use a hash for
            //  remaining vertices and edges instead?

            DGraph2  PathGraph = new DGraph2();
            Vector2d sortAxis  = Vector2d.FromAngleDeg(AngleDeg).Perp;

            while (true)
            {
                // find most extreme edge to start at
                // [TODO] could use segment gid here as we set them based on insertion span!
                // [TODO] could use a smarter metric? like, closest to previous last endpoint? Using
                //   extrema like this tends to produce longest spans, though...
                double min_dot   = double.MaxValue;
                int    start_eid = -1;
                foreach (int eid in MinGraph.EdgeIndices())
                {
                    Index3i evg = MinGraph.GetEdge(eid);
                    if (evg.c >= 0)
                    {
                        double dot = MinGraph.GetVertex(evg.a).Dot(sortAxis);
                        if (dot < min_dot)
                        {
                            min_dot   = dot;
                            start_eid = eid;
                        }
                    }
                }
                if (start_eid == -1)
                {
                    break;   // if we could not find a start edge, we must be done!
                }
                // ok now walk forward through connectors and spans. We do this in
                // connector/span pairs - we are always at an end-of-span point, and
                // we pick a next-connector and then a next-span.
                // We need to keep track of vertices in both the pathgraph and mingraph,
                // these are the "new" and "old" vertices
                Index3i start_evg = MinGraph.GetEdge(start_eid);
                int     new_start = PathGraph.AppendVertex(MinGraph.GetVertex(start_evg.a));
                int     new_prev  = PathGraph.AppendVertex(MinGraph.GetVertex(start_evg.b));
                int     old_prev  = start_evg.b;
                PathGraph.AppendEdge(new_start, new_prev, start_evg.c);
                MinGraph.RemoveVertex(start_evg.a, true);
                while (true)
                {
                    // choose next connector edge, outgoing from current vtx
                    int connector_e = -1;
                    foreach (int eid in MinGraph.VtxEdgesItr(old_prev))
                    {
                        Index3i evg = MinGraph.GetEdge(eid);
                        if (evg.c >= 0)
                        {
                            continue;  // what??
                        }
                        if (connector_e == -1 || EdgeWeights[connector_e] > EdgeWeights[eid])
                        {
                            connector_e = eid;
                        }
                    }
                    if (connector_e == -1)
                    {
                        break;
                    }

                    // find the vertex at end of connector
                    Index3i conn_evg   = MinGraph.GetEdge(connector_e);
                    int     old_conn_v = (conn_evg.a == old_prev) ? conn_evg.b : conn_evg.a;

                    // can never look at prev vertex again, or any edges connected to it
                    // [TODO] are we sure none of these edges are unused spans?!?
                    MinGraph.RemoveVertex(old_prev, true);

                    // now find outgoing span edge
                    int span_e = -1;
                    foreach (int eid in MinGraph.VtxEdgesItr(old_conn_v))
                    {
                        Index3i evg = MinGraph.GetEdge(eid);
                        if (evg.c >= 0)
                        {
                            span_e = eid;
                            break;
                        }
                    }
                    if (span_e == -1)
                    {
                        break;   // disaster!
                    }
                    // find vertex at far end of span
                    Index3i span_evg   = MinGraph.GetEdge(span_e);
                    int     old_span_v = (span_evg.a == old_conn_v) ? span_evg.b : span_evg.a;

                    // ok we want to insert the connectr to the path graph, however the
                    // connector might actually have come from a more complex path in the input graph.
                    int new_conn_next = -1;
                    if (MinEdgePaths.ContainsKey(connector_e))
                    {
                        // complex path case. Note that the order [old_prev, old_conn_v] may be the opposite
                        // of the order in the pathv. But above, we appended the [a,b] edge order to the pathv.
                        // So we can check if we need to flip, but this means we need to be a bit clever w/ indices...
                        List <int> pathv     = MinEdgePaths[connector_e];
                        int        N         = pathv.Count;
                        int        path_prev = new_prev;
                        int        k         = 1;
                        if (pathv[N - 2] != old_prev)     // case where order flipped
                        {
                            pathv.Reverse();
                            k = 3;
                        }
                        else
                        {
                            N = N - 2;
                        }
                        while (k < N)
                        {
                            int path_next = PathGraph.AppendVertex(input.GetVertex(pathv[k]));
                            PathGraph.AppendEdge(path_prev, path_next);
                            path_prev = path_next;
                            k++;
                        }
                        new_conn_next = path_prev;
                    }
                    else
                    {
                        new_conn_next = PathGraph.AppendVertex(MinGraph.GetVertex(old_conn_v));
                        PathGraph.AppendEdge(new_prev, new_conn_next, conn_evg.c);
                    }

                    // add span to path
                    int new_fill_next = PathGraph.AppendVertex(MinGraph.GetVertex(old_span_v));
                    PathGraph.AppendEdge(new_conn_next, new_fill_next, span_evg.c);

                    // remove the connector vertex
                    MinGraph.RemoveVertex(old_conn_v, true);

                    // next iter starts at far end of span
                    new_prev = new_fill_next;
                    old_prev = old_span_v;
                }

                sortAxis = -sortAxis;
            }


            // for testing/debugging
            //SVGWriter writer = new SVGWriter();
            ////writer.AddGraph(input, SVGWriter.Style.Outline("blue", 0.1f));
            //writer.AddGraph(MinGraph, SVGWriter.Style.Outline("red", 0.1f));
            ////foreach ( int eid in MinGraph.EdgeIndices() ) {
            ////    if ( MinGraph.GetEdgeGroup(eid) >= 0 )  writer.AddLine(MinGraph.GetEdgeSegment(eid), SVGWriter.Style.Outline("green", 0.07f));
            ////}
            ////writer.AddGraph(MinGraph, SVGWriter.Style.Outline("black", 0.03f));
            //writer.AddGraph(PathGraph, SVGWriter.Style.Outline("black", 0.03f));
            //foreach (int vid in PathGraph.VertexIndices()) {
            //    if (PathGraph.IsBoundaryVertex(vid))
            //        writer.AddCircle(new Circle2d(PathGraph.GetVertex(vid), 0.5f), SVGWriter.Style.Outline("blue", 0.03f));
            //}
            ////writer.AddGraph(IntervalGraph, SVGWriter.Style.Outline("black", 0.03f));
            //writer.Write("c:\\scratch\\MIN_GRAPH.svg");


            return(PathGraph);
        }
Example #24
0
        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);
        }
Example #25
0
        // join disconnected vertices within distance threshold. Use point-hashtable to make this faster.
        protected int JoinInTolerance_Parallel_Cache(DGraph2 graph, double fMergeDist)
        {
            double mergeSqr = fMergeDist * fMergeDist;

            int NV = graph.MaxVertexID;

            if (collapse_cache.size < NV)
            {
                collapse_cache.resize(NV);
            }

            gParallel.ForEach(Interval1i.Range(NV), (a) =>
            {
                collapse_cache[a] = new Vector2d(-1, double.MaxValue);
                if (!graph.IsVertex(a))
                {
                    return;
                }

                Vector2d va = graph.GetVertex(a);

                KeyValuePair <int, double> found =
                    graph_cache.FindNearestInRadius(va, mergeSqr,
                                                    (b) => { return(va.DistanceSquared(graph.GetVertex(b))); },
                                                    (b) => { return(b <= a || (graph.FindEdge(a, b) != DGraph2.InvalidID)); });

                if (found.Key != -1)
                {
                    collapse_cache[a] = new Vector2d(found.Key, found.Value);
                }
            });

            // [TODO] sort

            int merged = 0;

            for (int a = 0; a < NV; ++a)
            {
                if (collapse_cache[a].x == -1)
                {
                    continue;
                }

                int bNearest = (int)collapse_cache[a].x;
                if (!graph.IsVertex(bNearest))
                {
                    continue;
                }

                Vector2d pos_a        = graph.GetVertex(a);
                Vector2d pos_bNearest = graph.GetVertex(bNearest);

                /*int eid = */
                graph.AppendEdge(a, bNearest);
                DGraph2.EdgeCollapseInfo collapseInfo;
                graph.CollapseEdge(bNearest, a, out collapseInfo);

                graph_cache.RemovePointUnsafe(a, pos_a);
                last_step_size[a] = 0;
                graph_cache.UpdatePointUnsafe(bNearest, pos_bNearest, graph.GetVertex(bNearest));
                collapse_cache[bNearest] = new Vector2d(-1, double.MaxValue);

                merged++;
            }
            return(merged);
        }
Example #26
0
 public PathOverlapRepair()
 {
     Graph = new DGraph2();
 }
Example #27
0
        // collapse edges shorter than fMinLen
        // NOTE: basically the same as DGraph2Resampler.CollapseToMinEdgeLength, but updates
        // our internal caches. Could we merge somehow?
        protected void CollapseToMinEdgeLength(DGraph2 graph, double fMinLen)
        {
            double sharp_threshold_deg = 140.0f;

            double minLenSqr  = fMinLen * fMinLen;
            bool   done       = false;
            int    max_passes = 100;
            int    pass_count = 0;

            while (done == false && pass_count++ < max_passes)
            {
                done = true;

                // [RMS] do modulo-indexing here to avoid pathological cases where we do things like
                // continually collapse a short edge adjacent to a long edge (which will result in crazy over-collapse)
                int       N       = graph.MaxEdgeID;
                const int nPrime  = 31337;    // any prime will do...
                int       cur_eid = 0;
                do
                {
                    int eid = cur_eid;
                    cur_eid = (cur_eid + nPrime) % N;

                    if (!graph.IsEdge(eid))
                    {
                        continue;
                    }
                    Index2i ev = graph.GetEdgeV(eid);

                    Vector2d va      = graph.GetVertex(ev.a);
                    Vector2d vb      = graph.GetVertex(ev.b);
                    double   distSqr = va.DistanceSquared(vb);
                    if (distSqr < minLenSqr)
                    {
                        int vtx_idx = -1;    // collapse to this vertex

                        // check valences. want to preserve positions of non-valence-2
                        int na = graph.GetVtxEdgeCount(ev.a);
                        int nb = graph.GetVtxEdgeCount(ev.b);
                        if (na != 2 && nb != 2)
                        {
                            continue;
                        }
                        if (na != 2)
                        {
                            vtx_idx = 0;
                        }
                        else if (nb != 2)
                        {
                            vtx_idx = 1;
                        }

                        // check opening angles. want to preserve sharp(er) angles
                        if (vtx_idx == -1)
                        {
                            double opena = Math.Abs(graph.OpeningAngle(ev.a));
                            double openb = Math.Abs(graph.OpeningAngle(ev.b));
                            if (opena < sharp_threshold_deg && openb < sharp_threshold_deg)
                            {
                                continue;
                            }
                            else if (opena < sharp_threshold_deg)
                            {
                                vtx_idx = 0;
                            }
                            else if (openb < sharp_threshold_deg)
                            {
                                vtx_idx = 1;
                            }
                        }

                        Vector2d newPos = (vtx_idx == -1) ? 0.5 * (va + vb) : ((vtx_idx == 0) ? va : vb);

                        int keep = ev.a, remove = ev.b;
                        if (vtx_idx == 1)
                        {
                            remove = ev.a; keep = ev.b;
                        }

                        Vector2d remove_pos = graph.GetVertex(remove);
                        Vector2d keep_pos   = graph.GetVertex(keep);

                        DGraph2.EdgeCollapseInfo collapseInfo;
                        if (graph.CollapseEdge(keep, remove, out collapseInfo) == MeshResult.Ok)
                        {
                            graph_cache.RemovePointUnsafe(collapseInfo.vRemoved, remove_pos);
                            last_step_size[collapseInfo.vRemoved] = 0;
                            graph_cache.UpdatePointUnsafe(collapseInfo.vKept, keep_pos, newPos);
                            graph.SetVertex(collapseInfo.vKept, newPos);
                            done = false;
                        }
                    }
                } while (cur_eid != 0);
            }
        }
Example #28
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);
        }
Example #29
0
 public PathOverlapRepair(DGraph2 graph)
 {
     Graph = graph;
 }
Example #30
0
        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();
        }