Example #1
0
        //------------------------------------------------------------------------------

        public bool AddPolygon(PolygonClp pg, PolyType polyType)
        {
            int len = pg.Count;
            if (len < 3) return false;
            Int64 maxVal;
            if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange;
            RangeTest(pg[0], ref maxVal);

            PolygonClp p = new PolygonClp(len);
            p.Add(new IntPoint(pg[0].X, pg[0].Y));
            int j = 0;
            for (int i = 1; i < len; ++i)
            {
                RangeTest(pg[i], ref maxVal);
                if (PointsEqual(p[j], pg[i])) continue;
                else if (j > 0 && SlopesEqual(p[j - 1], p[j], pg[i], maxVal == hiRange))
                {
                    if (PointsEqual(p[j - 1], pg[i])) j--;
                }
                else j++;
                if (j < p.Count)
                    p[j] = pg[i];
                else
                    p.Add(new IntPoint(pg[i].X, pg[i].Y));
            }
            if (j < 2) return false;
            m_UseFullRange = maxVal == hiRange;

            len = j + 1;
            while (len > 2)
            {
                //nb: test for point equality before testing slopes ...
                if (PointsEqual(p[j], p[0])) j--;
                else if (PointsEqual(p[0], p[1]) || SlopesEqual(p[j], p[0], p[1], m_UseFullRange))
                    p[0] = p[j--];
                else if (SlopesEqual(p[j - 1], p[j], p[0], m_UseFullRange)) j--;
                else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange))
                {
                    for (int i = 2; i <= j; ++i) p[i - 1] = p[i];
                    j--;
                }
                else break;
                len--;
            }
            if (len < 3) return false;

            //create a new edge array ...
            List<TEdge> edges = new List<TEdge>(len);
            for (int i = 0; i < len; i++) edges.Add(new TEdge());
            m_edges.Add(edges);

            //convert vertices to a double-linked-list of edges and initialize ...
            edges[0].xcurr = p[0].X;
            edges[0].ycurr = p[0].Y;
            InitEdge(edges[len - 1], edges[0], edges[len - 2], p[len - 1], polyType);
            for (int i = len - 2; i > 0; --i)
                InitEdge(edges[i], edges[i + 1], edges[i - 1], p[i], polyType);
            InitEdge(edges[0], edges[1], edges[len - 1], p[0], polyType);

            //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates
            //increase downward so the 'highest' edge will have the smallest ytop) ...
            TEdge e = edges[0];
            TEdge eHighest = e;
            do
            {
                e.xcurr = e.xbot;
                e.ycurr = e.ybot;
                if (e.ytop < eHighest.ytop) eHighest = e;
                e = e.next;
            }
            while (e != edges[0]);

            //make sure eHighest is positioned so the following loop works safely ...
            if (eHighest.windDelta > 0) eHighest = eHighest.next;
            if (eHighest.dx == horizontal) eHighest = eHighest.next;

            //finally insert each local minima ...
            e = eHighest;
            do
            {
                e = AddBoundsToLML(e);
            }
            while (e != eHighest);
            return true;
        }
Example #2
0
        //------------------------------------------------------------------------------

        //------------------------------------------------------------------------------
        // SimplifyPolygon functions ...
        // Convert self-intersecting polygons into simple polygons
        //------------------------------------------------------------------------------

        public static PolygonsClp SimplifyPolygon(PolygonClp poly,
              PolyFillType fillType = PolyFillType.pftEvenOdd)
        {
            PolygonsClp result = new PolygonsClp();
            Clipper c = new Clipper();
            c.ForceSimple = true;
            c.AddPolygon(poly, PolyType.ptSubject);
            c.Execute(ClipType.ctUnion, result, fillType, fillType);
            return result;
        }
Example #3
0
        //------------------------------------------------------------------------------

        public static PolygonClp CleanPolygon(PolygonClp poly,
            double distance = 1.415)
        {
            //distance = proximity in units/pixels below which vertices
            //will be stripped. Default ~= sqrt(2) so when adjacent
            //vertices have both x & y coords within 1 unit, then
            //the second vertex will be stripped.
            double distSqrd = (distance * distance);
            int highI = poly.Count - 1;
            PolygonClp result = new PolygonClp(highI + 1);
            while (highI > 0 && PointsAreClose(poly[highI], poly[0], distSqrd)) highI--;
            if (highI < 2) return result;
            IntPoint pt = poly[highI];
            int i = 0;
            for (; ; )
            {
                while (i < highI && PointsAreClose(pt, poly[i], distSqrd)) i += 2;
                int i2 = i;
                while (i < highI && (PointsAreClose(poly[i], poly[i + 1], distSqrd) ||
                    SlopesNearColinear(pt, poly[i], poly[i + 1], distSqrd))) i++;
                if (i >= highI) break;
                else if (i != i2) continue;
                pt = poly[i++];
                result.Add(pt);
            }
            if (i <= highI) result.Add(poly[i]);
            i = result.Count;
            if (i > 2 && SlopesNearColinear(result[i - 2], result[i - 1], result[0], distSqrd))
                result.RemoveAt(i - 1);
            if (result.Count < 3) result.Clear();
            return result;
        }
Example #4
0
            //------------------------------------------------------------------------------

            public PolyOffsetBuilder(PolygonsClp pts, PolygonsClp solution, bool isPolygon, double delta,
                JoinType jointype, EndType endtype, double limit = 0)
            {
                //precondition: solution != pts

                if (delta == 0) { solution = pts; return; }
                m_p = pts;
                m_delta = delta;
                m_rmin = 0.5;

                if (jointype == JoinType.jtMiter)
                {
                    if (limit > 2) m_rmin = 2.0 / (limit * limit);
                    limit = 0.25; //just in case endtype == etRound
                }
                else
                {
                    if (limit <= 0) limit = 0.25;
                    else if (limit > Math.Abs(delta)) limit = Math.Abs(delta);
                }

                double deltaSq = delta * delta;
                solution.Clear();
                solution.Capacity = pts.Count;
                for (m_i = 0; m_i < pts.Count; m_i++)
                {
                    int len = pts[m_i].Count;
                    if (len == 0 || (len < 3 && delta <= 0))
                        continue;
                    else if (len == 1)
                    {
                        currentPoly = new PolygonClp();
                        currentPoly = BuildArc(pts[m_i][0], 0, 2 * Math.PI, delta, limit);
                        solution.Add(currentPoly);
                        continue;
                    }

                    bool forceClose = PointsEqual(pts[m_i][0], pts[m_i][len - 1]);
                    if (forceClose) len--;

                    //build normals ...
                    normals.Clear();
                    normals.Capacity = len;
                    for (int j = 0; j < len - 1; ++j)
                        normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j + 1]));
                    if (isPolygon || forceClose)
                        normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0]));
                    else
                        normals.Add(new DoublePoint(normals[len - 2]));

                    currentPoly = new PolygonClp();
                    if (isPolygon || forceClose)
                    {
                        m_k = len - 1;
                        for (m_j = 0; m_j < len; ++m_j)
                            OffsetPoint(jointype, limit);
                        solution.Add(currentPoly);
                        if (!isPolygon)
                        {
                            currentPoly = new PolygonClp();
                            m_delta = -m_delta;
                            m_k = len - 1;
                            for (m_j = 0; m_j < len; ++m_j)
                                OffsetPoint(jointype, limit);
                            m_delta = -m_delta;
                            currentPoly.Reverse();
                            solution.Add(currentPoly);
                        }
                    }
                    else
                    {
                        m_k = 0;
                        for (m_j = 1; m_j < len - 1; ++m_j)
                            OffsetPoint(jointype, limit);

                        IntPoint pt1;
                        if (endtype == EndType.etButt)
                        {
                            m_j = len - 1;
                            pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_j].X *
                              delta), (Int64)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta));
                            AddPoint(pt1);
                            pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X - normals[m_j].X *
                              delta), (Int64)Round(pts[m_i][m_j].Y - normals[m_j].Y * delta));
                            AddPoint(pt1);
                        }
                        else
                        {
                            m_j = len - 1;
                            m_k = len - 2;
                            normals[m_j].X = -normals[m_j].X;
                            normals[m_j].Y = -normals[m_j].Y;
                            if (endtype == EndType.etSquare) DoSquare();
                            else DoRound(limit);
                        }

                        //re-build Normals ...
                        for (int j = len - 1; j > 0; j--)
                        {
                            normals[j].X = -normals[j - 1].X;
                            normals[j].Y = -normals[j - 1].Y;
                        }
                        normals[0].X = -normals[1].X;
                        normals[0].Y = -normals[1].Y;

                        m_k = len - 1;
                        for (m_j = m_k - 1; m_j > 0; --m_j)
                            OffsetPoint(jointype, limit);

                        if (endtype == EndType.etButt)
                        {
                            pt1 = new IntPoint((Int64)Round(pts[m_i][0].X - normals[0].X * delta),
                              (Int64)Round(pts[m_i][0].Y - normals[0].Y * delta));
                            AddPoint(pt1);
                            pt1 = new IntPoint((Int64)Round(pts[m_i][0].X + normals[0].X * delta),
                              (Int64)Round(pts[m_i][0].Y + normals[0].Y * delta));
                            AddPoint(pt1);
                        }
                        else
                        {
                            m_k = 1;
                            if (endtype == EndType.etSquare) DoSquare();
                            else DoRound(limit);
                        }
                        solution.Add(currentPoly);
                    }
                }

                //finally, clean up untidy corners ...
                Clipper clpr = new Clipper();
                clpr.AddPolygons(solution, PolyType.ptSubject);
                if (delta > 0)
                {
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
                }
                else
                {
                    IntRect r = clpr.GetBounds();
                    PolygonClp outer = new PolygonClp(4);

                    outer.Add(new IntPoint(r.left - 10, r.bottom + 10));
                    outer.Add(new IntPoint(r.right + 10, r.bottom + 10));
                    outer.Add(new IntPoint(r.right + 10, r.top - 10));
                    outer.Add(new IntPoint(r.left - 10, r.top - 10));

                    clpr.AddPolygon(outer, PolyType.ptSubject);
                    clpr.ReverseSolution = true;
                    clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
                    if (solution.Count > 0) solution.RemoveAt(0);
                }
            }
Example #5
0
        //------------------------------------------------------------------------------

        public static PolygonsClp OffsetPolyLines(PolygonsClp lines,
          double delta, JoinType jointype, EndType endtype,
          double limit)
        {
            PolygonsClp result = new PolygonsClp();

            //automatically strip duplicate points because it gets complicated with
            //open and closed lines and when to strip duplicates across begin-end ...
            PolygonsClp pts = new PolygonsClp(lines);
            for (int i = 0; i < pts.Count; ++i)
            {
                for (int j = pts[i].Count - 1; j > 0; j--)
                    if (PointsEqual(pts[i][j], pts[i][j - 1]))
                        pts[i].RemoveAt(j);
            }

            if (endtype == EndType.etClosed)
            {
                int sz = pts.Count;
                pts.Capacity = sz * 2;
                for (int i = 0; i < sz; ++i)
                {
                    PolygonClp line = new PolygonClp(pts[i]);
                    line.Reverse();
                    pts.Add(line);
                }
                new PolyOffsetBuilder(pts, result, true, delta, jointype, endtype, limit);
            }
            else
                new PolyOffsetBuilder(pts, result, false, delta, jointype, endtype, limit);

            return result;
        }
Example #6
0
        //------------------------------------------------------------------------------
        // OffsetPolygon functions ...
        //------------------------------------------------------------------------------

        internal static PolygonClp BuildArc(IntPoint pt, double a1, double a2, double r, double limit)
        {
            //see notes in clipper.pas regarding steps
            double arcFrac = Math.Abs(a2 - a1) / (2 * Math.PI);
            int steps = (int)(arcFrac * Math.PI / Math.Acos(1 - limit / Math.Abs(r)));
            if (steps < 2)
                steps = 2;
            else if (steps > (int)(222.0 * arcFrac))
                steps = (int)(222.0 * arcFrac);

            double x = Math.Cos(a1);
            double y = Math.Sin(a1);
            double c = Math.Cos((a2 - a1) / steps);
            double s = Math.Sin((a2 - a1) / steps);
            PolygonClp result = new PolygonClp(steps + 1);
            for (int i = 0; i <= steps; ++i)
            {
                result.Add(new IntPoint(pt.X + Round(x * r), pt.Y + Round(y * r)));
                double x2 = x;
                x = x * c - s * y;  //cross product
                y = x2 * s + y * c; //dot product
            }
            return result;
        }
Example #7
0
        //------------------------------------------------------------------------------

        public static double Area(PolygonClp poly)
        {
            int highI = poly.Count - 1;
            if (highI < 2) return 0;
            if (FullRangeNeeded(poly))
            {
                Int128 a = new Int128(0);
                a = Int128.Int128Mul(poly[highI].X + poly[0].X, poly[0].Y - poly[highI].Y);
                for (int i = 1; i <= highI; ++i)
                    a += Int128.Int128Mul(poly[i - 1].X + poly[i].X, poly[i].Y - poly[i - 1].Y);
                return a.ToDouble() / 2;
            }
            else
            {
                double area = ((double)poly[highI].X + poly[0].X) * ((double)poly[0].Y - poly[highI].Y);
                for (int i = 1; i <= highI; ++i)
                    area += ((double)poly[i - 1].X + poly[i].X) * ((double)poly[i].Y - poly[i - 1].Y);
                return area / 2;
            }
        }
Example #8
0
        //------------------------------------------------------------------------------

        private static bool FullRangeNeeded(PolygonClp pts)
        {
            bool result = false;
            for (int i = 0; i < pts.Count; i++)
            {
                if (Math.Abs(pts[i].X) > hiRange || Math.Abs(pts[i].Y) > hiRange)
                    throw new ClipperException("Coordinate exceeds range bounds.");
                else if (Math.Abs(pts[i].X) > loRange || Math.Abs(pts[i].Y) > loRange)
                    result = true;
            }
            return result;
        }
Example #9
0
        //------------------------------------------------------------------------------

        private void BuildResult(PolygonsClp polyg)
        {
            polyg.Clear();
            polyg.Capacity = m_PolyOuts.Count;
            for (int i = 0; i < m_PolyOuts.Count; i++)
            {
                OutRec outRec = m_PolyOuts[i];
                if (outRec.pts == null) continue;
                OutPt p = outRec.pts;
                int cnt = PointCount(p);
                if (cnt < 3) continue;
                PolygonClp pg = new PolygonClp(cnt);
                for (int j = 0; j < cnt; j++)
                {
                    pg.Add(p.pt);
                    p = p.prev;
                }
                polyg.Add(pg);
            }
        }
Example #10
0
        //------------------------------------------------------------------------------

        public static bool Orientation(PolygonClp poly)
        {
            return Area(poly) >= 0;
        }