//------------------------------------------------------------------------------ public static void AddPolyNodeToPolygons(PolyNode polynode, PolygonsClp polygons) { if (polynode.Contour.Count > 0) polygons.Add(polynode.Contour); foreach (PolyNode pn in polynode.Childs) AddPolyNodeToPolygons(pn, polygons); }
//------------------------------------------------------------------------------ public bool AddPolygons(PolygonsClp ppg, PolyType polyType) { bool result = false; for (int i = 0; i < ppg.Count; ++i) if (AddPolygon(ppg[i], polyType)) result = true; return result; }
//------------------------------------------------------------------------------ public static PolygonsClp CleanPolygons(PolygonsClp polys, double distance = 1.415) { PolygonsClp result = new PolygonsClp(polys.Count); for (int i = 0; i < polys.Count; i++) result.Add(CleanPolygon(polys[i], distance)); return result; }
//------------------------------------------------------------------------------ public static void PolyTreeToPolygons(PolyTree polytree, PolygonsClp polygons) { polygons.Clear(); polygons.Capacity = polytree.Total; AddPolyNodeToPolygons(polytree, polygons); }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ public static PolygonsClp SimplifyPolygons(PolygonsClp polys, PolyFillType fillType = PolyFillType.pftEvenOdd) { PolygonsClp result = new PolygonsClp(); Clipper c = new Clipper(); c.ForceSimple = true; c.AddPolygons(polys, PolyType.ptSubject); c.Execute(ClipType.ctUnion, result, fillType, fillType); return result; }
//------------------------------------------------------------------------------ public static PolygonsClp OffsetPolygons(PolygonsClp poly, double delta, JoinType jointype) { return OffsetPolygons(poly, delta, jointype, 0, true); }
//------------------------------------------------------------------------------ public static PolygonsClp OffsetPolygons(PolygonsClp poly, double delta) { return OffsetPolygons(poly, delta, JoinType.jtSquare, 0, true); }
//------------------------------------------------------------------------------ public static PolygonsClp OffsetPolygons(PolygonsClp poly, double delta, JoinType jointype, double MiterLimit, bool AutoFix) { PolygonsClp result = new PolygonsClp(); //AutoFix - fixes polygon orientation if necessary and removes //duplicate vertices. Can be set false when you're sure that polygon //orientation is correct and that there are no duplicate vertices. if (AutoFix) { int Len = poly.Count, botI = 0; while (botI < Len && poly[botI].Count == 0) botI++; if (botI == Len) return result; //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point //This point (on pts[botI]) must be on an outer polygon ring and if //its orientation is false (counterclockwise) then assume all polygons //need reversing ... IntPoint botPt = poly[botI][0]; for (int i = botI; i < Len; ++i) { if (poly[i].Count == 0) continue; if (UpdateBotPt(poly[i][0], ref botPt)) botI = i; for (int j = poly[i].Count - 1; j > 0; j--) { if (PointsEqual(poly[i][j], poly[i][j - 1])) poly[i].RemoveAt(j); else if (UpdateBotPt(poly[i][j], ref botPt)) botI = i; } } if (!Orientation(poly[botI])) ReversePolygons(poly); } new PolyOffsetBuilder(poly, result, true, delta, jointype, EndType.etClosed, MiterLimit); return result; }
//------------------------------------------------------------------------------ public static PolygonsClp OffsetPolygons(PolygonsClp poly, double delta, JoinType jointype, double MiterLimit) { return OffsetPolygons(poly, delta, jointype, MiterLimit, true); }
//------------------------------------------------------------------------------ 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); } }
//------------------------------------------------------------------------------ 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); } }
//------------------------------------------------------------------------------ public static void ReversePolygons(PolygonsClp polys) { polys.ForEach(delegate(PolygonClp poly) { poly.Reverse(); }); }
//------------------------------------------------------------------------------ public bool Execute(ClipType clipType, PolygonsClp solution) { return Execute(clipType, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); }
//------------------------------------------------------------------------------ public bool Execute(ClipType clipType, PolygonsClp solution, PolyFillType subjFillType, PolyFillType clipFillType) { if (m_ExecuteLocked) return false; m_ExecuteLocked = true; solution.Clear(); m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; m_UsingPolyTree = false; bool succeeded = ExecuteInternal(); //build the return polygons ... if (succeeded) BuildResult(solution); m_ExecuteLocked = false; return succeeded; }