Example #1
0
        void insert_triangle(int triangle_id, ref Vector2d a, ref Vector2d b, ref Vector2d c, bool threadsafe = true)
        {
            bool lockTaken = false;

            while (threadsafe == true && lockTaken == false)
            {
                spinlock.Enter(ref lockTaken);
            }

            // [TODO] actually want to conservatively rasterize triangles here, not just
            // store in every cell in bbox!

            AxisAlignedBox2d bounds = BoundsUtil.Bounds(ref a, ref b, ref c);
            Vector2i         imin   = indexer.ToGrid(bounds.Min);
            Vector2i         imax   = indexer.ToGrid(bounds.Max);

            for (int yi = imin.y; yi <= imax.y; ++yi)
            {
                for (int xi = imin.x; xi <= imax.x; ++xi)
                {
                    // check if triangle overlaps this grid cell...

                    int bin_i = yi * bins_x + xi;
                    bins_list.Insert(bin_i, triangle_id);
                }
            }

            if (lockTaken)
            {
                spinlock.Exit();
            }
        }
Example #2
0
 public Box2d(AxisAlignedBox2d aaBox)
 {
     Extent = 0.5 * aaBox.Diagonal;
     Center = aaBox.Min + Extent;
     AxisX  = Vector2d.AxisX;
     AxisY  = Vector2d.AxisY;
 }
Example #3
0
        /// <summary>
        /// find all triangles that overlap range
        /// </summary>
        public void FindTrianglesInRange(AxisAlignedBox2d range, HashSet <int> triangles)
        {
            Vector2i grid_min = indexer.ToGrid(range.Min);

            if (grid_bounds.Contains(grid_min) == false)
            {
                throw new Exception("TriangleBinsGrid2d.FindTrianglesInRange: range.Min is out of bounds");
            }

            Vector2i grid_max = indexer.ToGrid(range.Max);

            if (grid_bounds.Contains(grid_max) == false)
            {
                throw new Exception("TriangleBinsGrid2d.FindTrianglesInRange: range.Max is out of bounds");
            }

            for (int yi = grid_min.y; yi <= grid_max.y; ++yi)
            {
                for (int xi = grid_min.x; xi <= grid_max.x; ++xi)
                {
                    int bin_i = yi * bins_x + xi;
                    foreach (int tid in bins_list.ValueItr(bin_i))
                    {
                        triangles.Add(tid);
                    }
                }
            }
        }
Example #4
0
        public List <Vector2d> FindIntersections(Segment2d oseg)
        {
            List <Vector2d> v          = new List <Vector2d>();
            var             osegBounds = new AxisAlignedBox2d();

            osegBounds.Contain(oseg.P0);
            osegBounds.Contain(oseg.P1);
            if (!this.GetBounds().Intersects(osegBounds))
            {
                return(v);
            }

            foreach (Segment2d seg in SegmentItr())
            {
                // this computes test twice for intersections, but seg.intersects doesn't
                // create any new objects so it should be much faster for majority of segments (should profile!)
                if (seg.Intersects(oseg))
                {
                    IntrSegment2Segment2 intr = new IntrSegment2Segment2(seg, oseg);
                    if (intr.Find())
                    {
                        v.Add(intr.Point0);
                        if (intr.Quantity == 2)
                        {
                            v.Add(intr.Point1);
                        }
                    }
                }
            }
            return(v);
        }
Example #5
0
 public void Contain(AxisAlignedBox2d box)
 {
     Min.x = Math.Min(Min.x, box.Min.x);
     Min.y = Math.Min(Min.y, box.Min.y);
     Max.x = Math.Max(Max.x, box.Max.x);
     Max.y = Math.Max(Max.y, box.Max.y);
 }
Example #6
0
        public void PrintStats(string label = "")
        {
            System.Console.WriteLine("PlanarComplex Stats {0}", label);
            var Loops  = new List <SmoothLoopElement>(LoopsItr());
            var Curves = new List <SmoothCurveElement>(CurvesItr());

            AxisAlignedBox2d bounds = Bounds();

            System.Console.WriteLine("  Bounding Box  w: {0} h: {1}  range {2} ", bounds.Width, bounds.Height, bounds);

            var vEndpoints = new List <ComplexEndpoint2d>(EndpointsItr());

            System.Console.WriteLine("  Closed Loops {0}  Open Curves {1}   Open Endpoints {2}",
                                     Loops.Count, Curves.Count, vEndpoints.Count);

            int nSegments    = CountType(typeof(Segment2d));
            int nArcs        = CountType(typeof(Arc2d));
            int nCircles     = CountType(typeof(Circle2d));
            int nNURBS       = CountType(typeof(NURBSCurve2));
            int nEllipses    = CountType(typeof(Ellipse2d));
            int nEllipseArcs = CountType(typeof(EllipseArc2d));
            int nSeqs        = CountType(typeof(ParametricCurveSequence2));

            System.Console.WriteLine("  [Type Counts]   // {0} multi-curves", nSeqs);
            System.Console.WriteLine("    segments {0,4}  arcs     {1,4}  circles      {2,4}", nSegments, nArcs, nCircles);
            System.Console.WriteLine("    nurbs    {0,4}  ellipses {1,4}  ellipse-arcs {2,4}", nNURBS, nEllipses, nEllipseArcs);
        }
        public void PrintStats(string label = "")
        {
            System.Console.WriteLine("PlanarComplex Stats {0}", label);
            List <SmoothLoopElement>  Loops  = new List <SmoothLoopElement>(LoopsItr());
            List <SmoothCurveElement> Curves = new List <SmoothCurveElement>(CurvesItr());

            AxisAlignedBox2d bounds = Bounds();

            System.Console.WriteLine("  Bounding Box: " + bounds);

            System.Console.WriteLine("  Closed Loops: " + Loops.Count.ToString());
            System.Console.WriteLine("  Open Curves: " + Curves.Count.ToString());

            List <ComplexEndpoint2d> vEndpoints = new List <ComplexEndpoint2d>(EndpointsItr());

            System.Console.WriteLine("  Open Endpoints: " + vEndpoints.Count.ToString());

            int nSegments    = CountType(typeof(Segment2d));
            int nArcs        = CountType(typeof(Arc2d));
            int nCircles     = CountType(typeof(Circle2d));
            int nNURBS       = CountType(typeof(NURBSCurve2));
            int nEllipses    = CountType(typeof(Ellipse2d));
            int nEllipseArcs = CountType(typeof(EllipseArc2d));
            int nSeqs        = CountType(typeof(ParametricCurveSequence2));

            System.Console.WriteLine("  Type Counts: ");
            System.Console.WriteLine("    segments {0,4}  arcs     {1,4}  circles      {2,4}", nSegments, nArcs, nCircles);
            System.Console.WriteLine("    nurbs    {0,4}  ellipses {1,4}  ellipse-arcs {2,4}", nNURBS, nEllipses, nEllipseArcs);
            System.Console.WriteLine("    multi    {0,4}  ", nSeqs);
        }
Example #8
0
        void remove_triangle(int triangle_id, ref Vector2d a, ref Vector2d b, ref Vector2d c, bool threadsafe = true)
        {
            bool lockTaken = false;

            while (threadsafe == true && lockTaken == false)
            {
                spinlock.Enter(ref lockTaken);
            }

            AxisAlignedBox2d bounds = BoundsUtil.Bounds(ref a, ref b, ref c);
            Vector2i         imin   = indexer.ToGrid(bounds.Min);
            Vector2i         imax   = indexer.ToGrid(bounds.Max);

            for (int yi = imin.y; yi <= imax.y; ++yi)
            {
                for (int xi = imin.x; xi <= imax.x; ++xi)
                {
                    int bin_i = yi * bins_x + xi;
                    bins_list.Remove(bin_i, triangle_id);
                }
            }

            if (lockTaken)
            {
                spinlock.Exit();
            }
        }
Example #9
0
        public AxisAlignedBox2d GetBounds()
        {
            AxisAlignedBox2d box = AxisAlignedBox2d.Empty;

            box.Contain(vertices);
            return(box);
        }
Example #10
0
        public Arrangement2d(AxisAlignedBox2d boundsHint)
        {
            Graph = new DGraph2();

            double cellSize = boundsHint.MaxDim / 64;

            PointHash = new PointHashGrid2d <int>(cellSize, -1);
        }
Example #11
0
        public SVGWriter()
        {
            Objects = new List <object>();
            Bounds  = AxisAlignedBox2d.Empty;

            DefaultPolygonStyle  = Style.Outline("black", 1);
            DefaultPolylineStyle = Style.Outline("black", 1);
            DefaultCircleStyle   = Style.Filled("green", "black", 1);
        }
        public AxisAlignedBox2d Bounds()
        {
            AxisAlignedBox2d box = AxisAlignedBox2d.Empty;

            foreach (Element e in vElements)
            {
                box.Contain(e.Bounds());
            }
            return(box);
        }
Example #13
0
        public void AddBox(AxisAlignedBox2d box, Style style)
        {
            Polygon2d poly = new Polygon2d();

            for (int k = 0; k < 4; ++k)
            {
                poly.AppendVertex(box.GetCorner(k));
            }
            AddPolygon(poly, style);
        }
        public static AxisAlignedBox2d Bounds(IEnumerable <Vector2d> positions)
        {
            AxisAlignedBox2d box = AxisAlignedBox2d.Empty;

            foreach (Vector2d v in positions)
            {
                box.Contain(v);
            }
            return(box);
        }
Example #15
0
        public SVGWriter()
        {
            Objects = new List <object>();
            Bounds  = AxisAlignedBox2d.Empty;

            DefaultPolygonStyle  = Style.Outline("grey", 1);
            DefaultPolylineStyle = Style.Outline("cyan", 1);
            DefaultCircleStyle   = Style.Filled("green", "black", 1);
            DefaultArcStyle      = Style.Outline("magenta", 1);
            DefaultLineStyle     = Style.Outline("black", 1);
            DefaultDGraphStyle   = Style.Outline("blue", 1);
        }
Example #16
0
        /// <summary>
        /// Actually computes the insertion. In some cases we would like more info
        /// coming back than we get by using Generate() api. Note that resulting
        /// mesh is *not* compacted.
        /// </summary>
        public DMesh3 ComputeResult(out MeshInsertPolygon insertion)
        {
            AxisAlignedBox2d bounds  = Polygon.Bounds;
            double           padding = 0.1 * bounds.DiagonalLength;

            bounds.Expand(padding);

            TrivialRectGenerator rectgen = (Subdivisions == 1) ?
                                           new TrivialRectGenerator() : new GriddedRectGenerator()
            {
                EdgeVertices = Subdivisions
            };

            rectgen.Width      = (float)bounds.Width;
            rectgen.Height     = (float)bounds.Height;
            rectgen.IndicesMap = new Index2i(1, 2);
            rectgen.UVMode     = UVMode;
            rectgen.Clockwise  = true;              // MeshPolygonInserter assumes mesh faces are CW? (except code says CCW...)
            rectgen.Generate();
            var base_mesh = new DMesh3();

            rectgen.MakeMesh(base_mesh);

            var      shiftPolygon = new GeneralPolygon2d(Polygon);
            Vector2d shift        = bounds.Center;

            shiftPolygon.Translate(-shift);

            var insert = new MeshInsertPolygon()
            {
                Mesh    = base_mesh,
                Polygon = shiftPolygon
            };
            bool bOK = insert.Insert();

            if (!bOK)
            {
                throw new Exception("TriangulatedPolygonGenerator: failed to Insert()");
            }

            MeshFaceSelection selected = insert.InteriorTriangles;
            var editor = new MeshEditor(base_mesh);

            editor.RemoveTriangles((tid) => { return(selected.IsSelected(tid) == false); }, true);

            var shift3 = new Vector3d(shift.x, shift.y, 0);

            MeshTransforms.Translate(base_mesh, shift3);

            insertion = insert;
            return(base_mesh);
        }
        void compute_polygon()
        {
            SpansPoly = new Polygon2d();
            for (int i = 0; i < FillSpans.Count; ++i)
            {
                foreach (int vid in FillSpans[i].Vertices)
                {
                    Vector2d v = to2D(Mesh.GetVertex(vid));
                    SpansPoly.AppendVertex(v);
                }
            }

            Bounds = SpansPoly.Bounds;
        }
Example #18
0
        /// <summary>
        /// "invalid" value will be returned by queries if no valid result is found (eg bounded-distance query)
        /// </summary>
        public TriangleBinsGrid2d(AxisAlignedBox2d bounds, int numCells)
        {
            this.bounds = bounds;
            double   cellsize = bounds.MaxDim / (double)numCells;
            Vector2d origin   = bounds.Min - cellsize * 0.5 * Vector2d.One;

            indexer = new ShiftGridIndexer2(origin, cellsize);

            bins_x      = (int)(bounds.Width / cellsize) + 2;
            bins_y      = (int)(bounds.Height / cellsize) + 2;
            grid_bounds = new AxisAlignedBox2i(0, 0, bins_x - 1, bins_y - 1);
            bins_list   = new SmallListSet();
            bins_list.Resize(bins_x * bins_y);
        }
Example #19
0
        public AxisAlignedBox2d GetBounds()
        {
            if (vertices.Count == 0)
            {
                return(AxisAlignedBox2d.Empty);
            }
            AxisAlignedBox2d box = new AxisAlignedBox2d(vertices[0]);

            for (int i = 1; i < vertices.Count; ++i)
            {
                box.Contain(vertices[i]);
            }
            return(box);
        }
Example #20
0
        public AxisAlignedBox2d Intersect(AxisAlignedBox2d box)
        {
            AxisAlignedBox2d intersect = new AxisAlignedBox2d(
                Math.Max(Min.x, box.Min.x), Math.Max(Min.y, box.Min.y),
                Math.Min(Max.x, box.Max.x), Math.Min(Max.y, box.Max.y));

            if (intersect.Height <= 0 || intersect.Width <= 0)
            {
                return(AxisAlignedBox2d.Empty);
            }
            else
            {
                return(intersect);
            }
        }
Example #21
0
        public SVGWriter()
        {
            CurrentLayer = new Layer("default");
            Layers       = new List <Layer>()
            {
                CurrentLayer
            };

            Bounds = AxisAlignedBox2d.Empty;

            DefaultPolygonStyle  = Style.Outline("grey", 1);
            DefaultPolylineStyle = Style.Outline("cyan", 1);
            DefaultCircleStyle   = Style.Filled("green", "black", 1);
            DefaultArcStyle      = Style.Outline("magenta", 1);
            DefaultLineStyle     = Style.Outline("black", 1);
            DefaultDGraphStyle   = Style.Outline("blue", 1);
        }
        void compute_polygons()
        {
            Bounds = AxisAlignedBox2d.Empty;
            for (int i = 0; i < Loops.Count; ++i)
            {
                EdgeLoop  loop = Loops[i].edgeLoop;
                Polygon2d poly = new Polygon2d();

                foreach (int vid in loop.Vertices)
                {
                    Vector2d v = to2D(Mesh.GetVertex(vid));
                    poly.AppendVertex(v);
                }

                Loops[i].poly = poly;
                Bounds.Contain(poly.Bounds);
            }
        }
 public void Contain(ref AxisAlignedBox2d box)
 {
     if (box.Min.x < Min.x)
     {
         Min.x = box.Min.x;
     }
     if (box.Max.x > Max.x)
     {
         Max.x = box.Max.x;
     }
     if (box.Min.y < Min.y)
     {
         Min.y = box.Min.y;
     }
     if (box.Max.y > Max.y)
     {
         Max.y = box.Max.y;
     }
 }
Example #24
0
        public bool Equals(AxisAlignedBox2d other, double tolerance = MathUtil.Epsilon)
        {
            if (!MathUtil.EpsilonEqual(Min.x, other.Min.x, tolerance))
            {
                return(false);
            }

            if (!MathUtil.EpsilonEqual(Min.y, other.Min.y, tolerance))
            {
                return(false);
            }

            if (!MathUtil.EpsilonEqual(Max.x, other.Max.x, tolerance))
            {
                return(false);
            }

            if (!MathUtil.EpsilonEqual(Max.y, other.Max.y, tolerance))
            {
                return(false);
            }

            return(true);
        }
Example #25
0
        /// <summary>
        /// Regular-grid tiling of element inside bounds, with spacing between elements
        /// Returns list of translations to element.
        /// Always allows at least one row and column, even if element overflows bounds in that dimension.
        /// </summary>
        public static List <Vector2d> BoundedRegularTiling2(AxisAlignedBox2d element, AxisAlignedBox2d bounds,
                                                            double spacing)
        {
            Vector2d oshift = -element.Min;
            double   w = element.Width; double h = element.Height;

            int    nx     = Math.Max(1, (int)(bounds.Width / w));
            double spacew = (nx - 1) * spacing;

            while (nx > 1 && bounds.Width - (w * nx + spacew) < 0)
            {
                nx--;
            }

            int    ny     = Math.Max(1, (int)(bounds.Height / h));
            double spaceh = (ny - 1) * spacing;

            while (ny > 1 && bounds.Height - (h * ny + spaceh) < 0)
            {
                ny--;
            }

            var translations = new List <Vector2d>();

            for (int yi = 0; yi < ny; ++yi)
            {
                double dy = yi * h + yi * spacing;
                for (int xi = 0; xi < nx; ++xi)
                {
                    double dx = xi * w + xi * spacing;
                    translations.Add(new Vector2d(dx, dy) + oshift + bounds.Min);
                }
            }

            return(translations);
        }
        // (sequentially) find each triangle that path point lies in, and insert a vertex for
        // that point into mesh.
        void insert_corners(HashSet <int> MeshVertsOnCurve)
        {
            PrimalQuery2d query = new PrimalQuery2d(PointF);

            if (UseTriSpatial)
            {
                int count = Mesh.TriangleCount + Curve.VertexCount;
                int bins  = 32;
                if (count < 25)
                {
                    bins = 8;
                }
                else if (count < 100)
                {
                    bins = 16;
                }
                AxisAlignedBox3d bounds3 = Mesh.CachedBounds;
                AxisAlignedBox2d bounds2 = new AxisAlignedBox2d(bounds3.Min.xy, bounds3.Max.xy);
                triSpatial = new TriangleBinsGrid2d(bounds2, bins);
                foreach (int tid in Mesh.TriangleIndices())
                {
                    spatial_add_triangle(tid);
                }
            }

            Func <int, Vector2d, bool> inTriangleF = (tid, pos) => {
                Index3i tv           = Mesh.GetTriangle(tid);
                int     query_result = query.ToTriangleUnsigned(pos, tv.a, tv.b, tv.c);
                return(query_result == -1 || query_result == 0);
            };

            CurveVertices = new int[Curve.VertexCount];
            for (int i = 0; i < Curve.VertexCount; ++i)
            {
                Vector2d vInsert  = Curve[i];
                bool     inserted = false;

                int contain_tid = DMesh3.InvalidID;
                if (triSpatial != null)
                {
                    contain_tid = triSpatial.FindContainingTriangle(vInsert, inTriangleF);
                }
                else
                {
                    foreach (int tid in Mesh.TriangleIndices())
                    {
                        Index3i tv = Mesh.GetTriangle(tid);
                        // [RMS] using unsigned query here because we do not need to care about tri CW/CCW orientation
                        //   (right? otherwise we have to explicitly invert mesh. Nothing else we do depends on tri orientation)
                        //int query_result = query.ToTriangle(vInsert, tv.a, tv.b, tv.c);
                        int query_result = query.ToTriangleUnsigned(vInsert, tv.a, tv.b, tv.c);
                        if (query_result == -1 || query_result == 0)
                        {
                            contain_tid = tid;
                            break;
                        }
                    }
                }

                if (contain_tid != DMesh3.InvalidID)
                {
                    Index3i  tv   = Mesh.GetTriangle(contain_tid);
                    Vector3d bary = MathUtil.BarycentricCoords(vInsert, PointF(tv.a), PointF(tv.b), PointF(tv.c));
                    // SpatialEpsilon is our zero-tolerance, so merge if we are closer than that
                    bool is_existing_v;
                    int  vid = insert_corner_from_bary(i, contain_tid, bary, 0.01, 100 * SpatialEpsilon, out is_existing_v);
                    if (vid > 0)      // this should be always happening..
                    {
                        CurveVertices[i] = vid;
                        if (is_existing_v)
                        {
                            MeshVertsOnCurve.Add(vid);
                        }
                        inserted = true;
                    }
                    else
                    {
                        throw new Exception("MeshInsertUVPolyCurve.insert_corners: failed to insert vertex " + i.ToString());
                    }
                }

                if (inserted == false)
                {
                    throw new Exception("MeshInsertUVPolyCurve.insert_corners: curve vertex "
                                        + i.ToString() + " is not inside or on any mesh triangle!");
                }
            }
        }
Example #27
0
 public void AddBox(AxisAlignedBox2d box)
 {
     AddBox(box, DefaultPolygonStyle);
 }
        // Finds set of "solid" regions - eg boundary loops with interior holes.
        // Result has outer loops being clockwise, and holes counter-clockwise
        public SolidRegionInfo FindSolidRegions(double fSimplifyDeviationTol = 0.1, bool bWantCurveSolids = true)
        {
            List <SmoothLoopElement> valid = new List <SmoothLoopElement>(LoopsItr());
            int N = valid.Count;

            // precompute bounding boxes
            int maxid = 0;

            foreach (var v in valid)
            {
                maxid = Math.Max(maxid, v.ID + 1);
            }
            AxisAlignedBox2d[] bounds = new AxisAlignedBox2d[maxid];
            foreach (var v in valid)
            {
                bounds[v.ID] = v.Bounds();
            }

            // copy polygons, simplify if desired
            double fClusterTol   = 0.0;                         // don't do simple clustering, can lose corners
            double fDeviationTol = fSimplifyDeviationTol;

            Polygon2d[] polygons = new Polygon2d[maxid];
            foreach (var v in valid)
            {
                Polygon2d p = new Polygon2d(v.polygon);
                if (fClusterTol > 0 || fDeviationTol > 0)
                {
                    p.Simplify(fClusterTol, fDeviationTol);
                }
                polygons[v.ID] = p;
            }

            // sort by bbox containment to speed up testing (does it??)
            valid.Sort((x, y) => {
                return(bounds[x.ID].Contains(bounds[y.ID]) ? -1 : 1);
            });

            // containment sets
            bool[] bIsContained = new bool[N];
            Dictionary <int, List <int> > ContainSets = new Dictionary <int, List <int> >();

            // construct containment sets
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = valid[i];
                Polygon2d         polyi = polygons[loopi.ID];

                for (int j = 0; j < N; ++j)
                {
                    if (i == j)
                    {
                        continue;
                    }
                    SmoothLoopElement loopj = valid[j];
                    Polygon2d         polyj = polygons[loopj.ID];

                    // cannot be contained if bounds are not contained
                    if (bounds[loopi.ID].Contains(bounds[loopj.ID]) == false)
                    {
                        continue;
                    }

                    // any other early-outs??

                    if (polyi.Contains(polyj))
                    {
                        if (ContainSets.ContainsKey(i) == false)
                        {
                            ContainSets.Add(i, new List <int>());
                        }
                        ContainSets[i].Add(j);
                        bIsContained[j] = true;
                    }
                }
            }

            List <GeneralPolygon2d>  polysolids = new List <GeneralPolygon2d>();
            List <PlanarSolid2d>     solids     = new List <PlanarSolid2d>();
            List <SmoothLoopElement> used       = new List <SmoothLoopElement>();

            // extract solids from containment relationships
            foreach (var i in ContainSets.Keys)
            {
                SmoothLoopElement outer_element = valid[i];
                used.Add(outer_element);
                if (bIsContained[i])
                {
                    throw new Exception("PlanarComplex.FindSolidRegions: multiply-nested regions not supported!");
                }

                Polygon2d          outer_poly = polygons[outer_element.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? outer_element.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                GeneralPolygon2d g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                PlanarSolid2d s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                foreach (int hi in ContainSets[i])
                {
                    SmoothLoopElement he = valid[hi];
                    used.Add(he);
                    Polygon2d          hole_poly = polygons[he.ID];
                    IParametricCurve2d hole_loop = (bWantCurveSolids) ? he.source.Clone() : null;
                    if (hole_poly.IsClockwise)
                    {
                        hole_poly.Reverse();
                        if (bWantCurveSolids)
                        {
                            hole_loop.Reverse();
                        }
                    }

                    try {
                        g.AddHole(hole_poly);
                        if (hole_loop != null)
                        {
                            s.AddHole(hole_loop);
                        }
                    } catch {
                        // don't add this hole - must intersect or something
                        // We should have caught this earlier!
                    }
                }

                polysolids.Add(g);
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }

            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = valid[i];
                if (used.Contains(loopi))
                {
                    continue;
                }

                Polygon2d          outer_poly = polygons[loopi.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                GeneralPolygon2d g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                PlanarSolid2d s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                polysolids.Add(g);
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }

            return(new SolidRegionInfo()
            {
                Polygons = polysolids,
                Solids = (bWantCurveSolids) ? solids : null
            });
        }
        public virtual bool Apply()
        {
            HashSet <int> OnCurveVerts = new HashSet <int>();     // original vertices that were epsilon-coincident w/ curve vertices

            insert_corners(OnCurveVerts);

            // [RMS] not using this?
            //HashSet<int> corner_v = new HashSet<int>(CurveVertices);

            // not sure we need to track all of these
            HashSet <int> ZeroEdges    = new HashSet <int>();
            HashSet <int> ZeroVertices = new HashSet <int>();

            OnCutEdges = new HashSet <int>();

            HashSet <int> NewEdges       = new HashSet <int>();
            HashSet <int> NewCutVertices = new HashSet <int>();

            sbyte[] signs = new sbyte[2 * Mesh.MaxVertexID + 2 * Curve.VertexCount];

            HashSet <int> segTriangles = new HashSet <int>();
            HashSet <int> segVertices  = new HashSet <int>();
            HashSet <int> segEdges     = new HashSet <int>();

            // loop over segments, insert each one in sequence
            int N = (IsLoop) ? Curve.VertexCount : Curve.VertexCount - 1;

            for (int si = 0; si < N; ++si)
            {
                int       i0  = si;
                int       i1  = (si + 1) % Curve.VertexCount;
                Segment2d seg = new Segment2d(Curve[i0], Curve[i1]);

                int i0_vid = CurveVertices[i0];
                int i1_vid = CurveVertices[i1];

                // If these vertices are already connected by an edge, we can just continue.
                int existing_edge = Mesh.FindEdge(i0_vid, i1_vid);
                if (existing_edge != DMesh3.InvalidID)
                {
                    add_cut_edge(existing_edge);
                    continue;
                }

                if (triSpatial != null)
                {
                    segTriangles.Clear(); segVertices.Clear(); segEdges.Clear();
                    AxisAlignedBox2d segBounds = new AxisAlignedBox2d(seg.P0); segBounds.Contain(seg.P1);
                    segBounds.Expand(MathUtil.ZeroTolerancef * 10);
                    triSpatial.FindTrianglesInRange(segBounds, segTriangles);
                    IndexUtil.TrianglesToVertices(Mesh, segTriangles, segVertices);
                    IndexUtil.TrianglesToEdges(Mesh, segTriangles, segEdges);
                }

                int MaxVID = Mesh.MaxVertexID;
                IEnumerable <int> vertices = Interval1i.Range(MaxVID);
                if (triSpatial != null)
                {
                    vertices = segVertices;
                }

                // compute edge-crossing signs
                // [TODO] could walk along mesh from a to b, rather than computing for entire mesh?
                if (signs.Length < MaxVID)
                {
                    signs = new sbyte[2 * MaxVID];
                }
                gParallel.ForEach(vertices, (vid) => {
                    if (Mesh.IsVertex(vid))
                    {
                        if (vid == i0_vid || vid == i1_vid)
                        {
                            signs[vid] = 0;
                        }
                        else
                        {
                            Vector2d v2 = PointF(vid);
                            // tolerance defines band in which we will consider values to be zero
                            signs[vid] = (sbyte)seg.WhichSide(v2, SpatialEpsilon);
                        }
                    }
                    else
                    {
                        signs[vid] = sbyte.MaxValue;
                    }
                });

                // have to skip processing of new edges. If edge id
                // is > max at start, is new. Otherwise if in NewEdges list, also new.
                // (need both in case we re-use an old edge index)
                int MaxEID = Mesh.MaxEdgeID;
                NewEdges.Clear();
                NewCutVertices.Clear();
                NewCutVertices.Add(i0_vid);
                NewCutVertices.Add(i1_vid);

                // cut existing edges with segment
                IEnumerable <int> edges = Interval1i.Range(MaxEID);
                if (triSpatial != null)
                {
                    edges = segEdges;
                }
                foreach (int eid in edges)
                {
                    if (Mesh.IsEdge(eid) == false)
                    {
                        continue;
                    }
                    if (eid >= MaxEID || NewEdges.Contains(eid))
                    {
                        continue;
                    }

                    // cannot cut boundary edges?
                    if (Mesh.IsBoundaryEdge(eid))
                    {
                        continue;
                    }

                    Index2i ev       = Mesh.GetEdgeV(eid);
                    int     eva_sign = signs[ev.a];
                    int     evb_sign = signs[ev.b];

                    // [RMS] should we be using larger epsilon here? If we don't track OnCurveVerts explicitly, we
                    // need to at least use same epsilon we passed to insert_corner_from_bary...do we still also
                    // need that to catch the edges we split in the poke?
                    bool eva_in_segment = false;
                    if (eva_sign == 0)
                    {
                        eva_in_segment = OnCurveVerts.Contains(ev.a) || Math.Abs(seg.Project(PointF(ev.a))) < (seg.Extent + SpatialEpsilon);
                    }
                    bool evb_in_segment = false;
                    if (evb_sign == 0)
                    {
                        evb_in_segment = OnCurveVerts.Contains(ev.b) || Math.Abs(seg.Project(PointF(ev.b))) < (seg.Extent + SpatialEpsilon);
                    }

                    // If one or both vertices are on-segment, we have special case.
                    // If just one vertex is on the segment, we can skip this edge.
                    // If both vertices are on segment, then we can just re-use this edge.
                    if (eva_in_segment || evb_in_segment)
                    {
                        if (eva_in_segment && evb_in_segment)
                        {
                            ZeroEdges.Add(eid);
                            add_cut_edge(eid);
                            NewCutVertices.Add(ev.a); NewCutVertices.Add(ev.b);
                        }
                        else
                        {
                            int zvid = eva_in_segment ? ev.a : ev.b;
                            ZeroVertices.Add(zvid);
                            NewCutVertices.Add(zvid);
                        }
                        continue;
                    }

                    // no crossing
                    if (eva_sign * evb_sign > 0)
                    {
                        continue;
                    }

                    // compute segment/segment intersection
                    Vector2d             va       = PointF(ev.a);
                    Vector2d             vb       = PointF(ev.b);
                    Segment2d            edge_seg = new Segment2d(va, vb);
                    IntrSegment2Segment2 intr     = new IntrSegment2Segment2(seg, edge_seg);
                    intr.Compute();
                    if (intr.Type == IntersectionType.Segment)
                    {
                        // [RMS] we should have already caught this above, so if it happens here it is probably spurious?
                        // we should have caught this case above, but numerics are different so it might occur again
                        ZeroEdges.Add(eid);
                        NewCutVertices.Add(ev.a); NewCutVertices.Add(ev.b);
                        add_cut_edge(eid);
                        continue;
                    }
                    else if (intr.Type != IntersectionType.Point)
                    {
                        continue; // no intersection
                    }
                    Vector2d x = intr.Point0;
                    double   t = Math.Sqrt(x.DistanceSquared(va) / va.DistanceSquared(vb));

                    // this case happens if we aren't "on-segment" but after we do the test the intersection pt
                    // is within epsilon of one end of the edge. This is a spurious t-intersection and we
                    // can ignore it. Some other edge should exist that picks up this vertex as part of it.
                    // [TODO] what about if this edge is degenerate?
                    bool x_in_segment = Math.Abs(edge_seg.Project(x)) < (edge_seg.Extent - SpatialEpsilon);
                    if (!x_in_segment)
                    {
                        continue;
                    }

                    Index2i et = Mesh.GetEdgeT(eid);
                    spatial_remove_triangles(et.a, et.b);

                    // split edge at this segment
                    DMesh3.EdgeSplitInfo splitInfo;
                    MeshResult           result = Mesh.SplitEdge(eid, out splitInfo, t);
                    if (result != MeshResult.Ok)
                    {
                        throw new Exception("MeshInsertUVSegment.Apply: SplitEdge failed - " + result.ToString());
                        //return false;
                    }

                    // move split point to intersection position
                    SetPointF(splitInfo.vNew, x);
                    NewCutVertices.Add(splitInfo.vNew);

                    NewEdges.Add(splitInfo.eNewBN);
                    NewEdges.Add(splitInfo.eNewCN);

                    spatial_add_triangles(et.a, et.b);
                    spatial_add_triangles(splitInfo.eNewT2, splitInfo.eNewT3);

                    // some splits - but not all - result in new 'other' edges that are on
                    // the polypath. We want to keep track of these edges so we can extract loop later.
                    Index2i ecn = Mesh.GetEdgeV(splitInfo.eNewCN);
                    if (NewCutVertices.Contains(ecn.a) && NewCutVertices.Contains(ecn.b))
                    {
                        add_cut_edge(splitInfo.eNewCN);
                    }

                    // since we don't handle bdry edges this should never be false, but maybe we will handle bdry later...
                    if (splitInfo.eNewDN != DMesh3.InvalidID)
                    {
                        NewEdges.Add(splitInfo.eNewDN);
                        Index2i edn = Mesh.GetEdgeV(splitInfo.eNewDN);
                        if (NewCutVertices.Contains(edn.a) && NewCutVertices.Contains(edn.b))
                        {
                            add_cut_edge(splitInfo.eNewDN);
                        }
                    }
                }
            }

            // extract the cut paths
            if (EnableCutSpansAndLoops)
            {
                find_cut_paths(OnCutEdges);
            }

            return(true);
        }         // Apply()
Example #30
0
        // Finds set of "solid" regions - eg boundary loops with interior holes.
        // Result has outer loops being clockwise, and holes counter-clockwise
        public SolidRegionInfo FindSolidRegions(FindSolidsOptions options)
        {
            var validLoops = new List <SmoothLoopElement>(LoopsItr());
            int N          = validLoops.Count;

            // precompute bounding boxes
            int maxid = 0;

            foreach (var v in validLoops)
            {
                maxid = Math.Max(maxid, v.ID + 1);
            }

            var bounds = new AxisAlignedBox2d[maxid];

            foreach (var v in validLoops)
            {
                bounds[v.ID] = v.Bounds();
            }

            // copy polygons, simplify if desired
            double fClusterTol   = 0.0;                 // don't do simple clustering, can lose corners
            double fDeviationTol = options.SimplifyDeviationTolerance;
            var    polygons      = new Polygon2d[maxid];

            foreach (var v in validLoops)
            {
                var p = new Polygon2d(v.polygon);
                if (fClusterTol > 0 || fDeviationTol > 0)
                {
                    p.Simplify(fClusterTol, fDeviationTol);
                }

                polygons[v.ID] = p;
            }

            // sort by bbox containment to speed up testing (does it??)
            validLoops.Sort((x, y) =>
            {
                return(bounds[x.ID].Contains(bounds[y.ID]) ? -1 : 1);
            });

            // containment sets
            bool[] bIsContained     = new bool[N];
            var    ContainSets      = new Dictionary <int, List <int> >();
            var    ContainedParents = new Dictionary <int, List <int> >();

            bool bUseOrient       = options.TrustOrientations;
            bool bWantCurveSolids = options.WantCurveSolids;
            bool bCheckHoles      = !options.AllowOverlappingHoles;

            // construct containment sets
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = validLoops[i];
                Polygon2d         polyi = polygons[loopi.ID];

                for (int j = 0; j < N; ++j)
                {
                    if (i == j)
                    {
                        continue;
                    }

                    SmoothLoopElement loopj = validLoops[j];
                    Polygon2d         polyj = polygons[loopj.ID];

                    // if we are preserving orientations, holes cannot contain holes and
                    // outers cannot contain outers!
                    if (bUseOrient && loopj.polygon.IsClockwise == loopi.polygon.IsClockwise)
                    {
                        continue;
                    }

                    // cannot be contained if bounds are not contained
                    if (bounds[loopi.ID].Contains(bounds[loopj.ID]) == false)
                    {
                        continue;
                    }

                    // any other early-outs??

                    if (polyi.Contains(polyj))
                    {
                        if (ContainSets.ContainsKey(i) == false)
                        {
                            ContainSets.Add(i, new List <int>());
                        }

                        ContainSets[i].Add(j);
                        bIsContained[j] = true;

                        if (ContainedParents.ContainsKey(j) == false)
                        {
                            ContainedParents.Add(j, new List <int>());
                        }

                        ContainedParents[j].Add(i);
                    }
                }
            }

            var polysolids     = new List <GeneralPolygon2d>();
            var polySolidsInfo = new List <GeneralSolid>();

            var solids = new List <PlanarSolid2d>();
            var used   = new HashSet <SmoothLoopElement>();

            var LoopToOuterIndex = new Dictionary <SmoothLoopElement, int>();

            var ParentsToProcess = new List <int>();


            // The following is a lot of code but it is very similar, just not clear how
            // to refactor out the common functionality
            //   1) we find all the top-level uncontained polys and add them to the final polys list
            //   2a) for any poly contained in those parent-polys, that is not also contained in anything else,
            //       add as hole to that poly
            //   2b) remove all those used parents & holes from consideration
            //   2c) now find all the "new" top-level polys
            //   3) repeat 2a-c until done all polys
            //   4) any remaining polys must be interior solids w/ no holes
            //          **or** weird leftovers like intersecting polys...

            // add all top-level uncontained polys
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = validLoops[i];
                if (bIsContained[i])
                {
                    continue;
                }

                Polygon2d          outer_poly = polygons[loopi.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                var g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                var s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                int idx = polysolids.Count;
                LoopToOuterIndex[loopi] = idx;
                used.Add(loopi);

                if (ContainSets.ContainsKey(i))
                {
                    ParentsToProcess.Add(i);
                }

                polysolids.Add(g);
                polySolidsInfo.Add(new GeneralSolid()
                {
                    Outer = loopi
                });
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }


            // keep iterating until we processed all parent loops
            while (ParentsToProcess.Count > 0)
            {
                var ContainersToRemove = new List <int>();

                // now for all top-level polys that contain children, add those children
                // as long as they do not have multiple contain-parents
                foreach (int i in ParentsToProcess)
                {
                    SmoothLoopElement parentloop = validLoops[i];
                    int outer_idx = LoopToOuterIndex[parentloop];

                    List <int> children = ContainSets[i];
                    foreach (int childj in children)
                    {
                        SmoothLoopElement childLoop = validLoops[childj];
                        Debug.Assert(used.Contains(childLoop) == false);

                        // skip multiply-contained children
                        List <int> parents = ContainedParents[childj];
                        if (parents.Count > 1)
                        {
                            continue;
                        }

                        Polygon2d          hole_poly = polygons[childLoop.ID];
                        IParametricCurve2d hole_loop = (bWantCurveSolids) ? childLoop.source.Clone() : null;
                        if (hole_poly.IsClockwise)
                        {
                            hole_poly.Reverse();
                            if (bWantCurveSolids)
                            {
                                hole_loop.Reverse();
                            }
                        }

                        try
                        {
                            polysolids[outer_idx].AddHole(hole_poly, bCheckHoles);
                            polySolidsInfo[outer_idx].Holes.Add(childLoop);
                            if (hole_loop != null)
                            {
                                solids[outer_idx].AddHole(hole_loop);
                            }
                        }
                        catch
                        {
                            // don't add this hole - must intersect or something?
                            // We should have caught this earlier!
                        }

                        used.Add(childLoop);
                        if (ContainSets.ContainsKey(childj))
                        {
                            ContainersToRemove.Add(childj);
                        }
                    }
                    ContainersToRemove.Add(i);
                }

                // remove all containers that are no longer valid
                foreach (int ci in ContainersToRemove)
                {
                    ContainSets.Remove(ci);

                    // have to remove from each ContainedParents list
                    var keys = new List <int>(ContainedParents.Keys);
                    foreach (int j in keys)
                    {
                        if (ContainedParents[j].Contains(ci))
                        {
                            ContainedParents[j].Remove(ci);
                        }
                    }
                }

                ParentsToProcess.Clear();

                // ok now find next-level uncontained parents...
                for (int i = 0; i < N; ++i)
                {
                    SmoothLoopElement loopi = validLoops[i];
                    if (used.Contains(loopi))
                    {
                        continue;
                    }

                    if (ContainSets.ContainsKey(i) == false)
                    {
                        continue;
                    }

                    List <int> parents = ContainedParents[i];
                    if (parents.Count > 0)
                    {
                        continue;
                    }

                    Polygon2d          outer_poly = polygons[loopi.ID];
                    IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                    if (outer_poly.IsClockwise == false)
                    {
                        outer_poly.Reverse();
                        if (bWantCurveSolids)
                        {
                            outer_loop.Reverse();
                        }
                    }

                    var g = new GeneralPolygon2d();
                    g.Outer = outer_poly;
                    var s = new PlanarSolid2d();
                    if (bWantCurveSolids)
                    {
                        s.SetOuter(outer_loop, true);
                    }

                    int idx = polysolids.Count;
                    LoopToOuterIndex[loopi] = idx;
                    used.Add(loopi);

                    if (ContainSets.ContainsKey(i))
                    {
                        ParentsToProcess.Add(i);
                    }

                    polysolids.Add(g);
                    polySolidsInfo.Add(new GeneralSolid()
                    {
                        Outer = loopi
                    });
                    if (bWantCurveSolids)
                    {
                        solids.Add(s);
                    }
                }
            }


            // any remaining loops must be top-level
            for (int i = 0; i < N; ++i)
            {
                SmoothLoopElement loopi = validLoops[i];
                if (used.Contains(loopi))
                {
                    continue;
                }

                Polygon2d          outer_poly = polygons[loopi.ID];
                IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null;
                if (outer_poly.IsClockwise == false)
                {
                    outer_poly.Reverse();
                    if (bWantCurveSolids)
                    {
                        outer_loop.Reverse();
                    }
                }

                var g = new GeneralPolygon2d();
                g.Outer = outer_poly;
                var s = new PlanarSolid2d();
                if (bWantCurveSolids)
                {
                    s.SetOuter(outer_loop, true);
                }

                polysolids.Add(g);
                polySolidsInfo.Add(new GeneralSolid()
                {
                    Outer = loopi
                });
                if (bWantCurveSolids)
                {
                    solids.Add(s);
                }
            }



            return(new SolidRegionInfo()
            {
                Polygons = polysolids,
                PolygonsSources = polySolidsInfo,
                Solids = (bWantCurveSolids) ? solids : null
            });
        }