/// <summary> /// Convert into an array of polygons /// </summary> /// <remarks> /// The polytree will nest deeply if solids are inside of holes. But Polygon2D would treat the solids inside of holes as their own /// independent isntances /// /// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/PolyTree/_Body.htm /// </remarks> private static Polygon2D[] ConvertOutput(PolyTree solution, double scaleInverse) { List<Polygon2D> retVal = new List<Polygon2D>(); // Walk the tree, and get all parents (need to look at contour count so that the root gets skipped) foreach (PolyNode parent in ((PolyNode)solution).Descendants(o => o.Childs).Where(o => !o.IsHole && o.Contour.Count > 0)) { // Convert the parent polygon Point[] points = parent.Contour.Select(o => new Point(o.X * scaleInverse, o.Y * scaleInverse)).ToArray(); if (parent.Childs.Count == 0) { // No holes retVal.Add(new Polygon2D(points)); } else { List<Point[]> holes = new List<Point[]>(); foreach (PolyNode child in parent.Childs) { if (!child.IsHole) { throw new ApplicationException("Expected the child of a non hole to be a hole"); } // Convert the hole polygon holes.Add(child.Contour.Select(o => new Point(o.X * scaleInverse, o.Y * scaleInverse)).ToArray()); } // Store with holes retVal.Add(new Polygon2D(points, holes.ToArray())); } } // Exit Function return retVal.ToArray(); }
public static Polygon2D[] GetPolyUnion(Point[][] polygons) { if (polygons == null || polygons.Length == 0) { return new Polygon2D[0]; } double scale = GetScale(polygons); var convertedPolys = ConvertInput(polygons, scale); Clipper clipper = new Clipper(); clipper.AddPolygons(convertedPolys, PolyType.ptSubject); // when doing union, I don't think it matters what is subject and what is union //clipper.ForceSimple = true; // Here is a page describing PolyFillType (nonzero is what you intuitively think of for a union) // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/PolyFillType.htm PolyTree solution = new PolyTree(); if (!clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftNonZero, PolyFillType.pftNonZero)) { return new Polygon2D[0]; } return ConvertOutput(solution, 1d / scale); }
//------------------------------------------------------------------------------ public static void PolyTreeToPolygons(PolyTree polytree, PolygonsClp polygons) { polygons.Clear(); polygons.Capacity = polytree.Total; AddPolyNodeToPolygons(polytree, polygons); }
//------------------------------------------------------------------------------ private void BuildResult2(PolyTree polytree) { polytree.Clear(); //add each output polygon/contour to polytree ... polytree.m_AllPolys.Capacity = m_PolyOuts.Count; for (int i = 0; i < m_PolyOuts.Count; i++) { OutRec outRec = m_PolyOuts[i]; int cnt = PointCount(outRec.pts); if (cnt < 3) continue; FixHoleLinkage(outRec); PolyNode pn = new PolyNode(); polytree.m_AllPolys.Add(pn); outRec.polyNode = pn; pn.m_polygon.Capacity = cnt; OutPt op = outRec.pts; for (int j = 0; j < cnt; j++) { pn.m_polygon.Add(op.pt); op = op.prev; } } //fixup PolyNode links etc ... polytree.m_Childs.Capacity = m_PolyOuts.Count; for (int i = 0; i < m_PolyOuts.Count; i++) { OutRec outRec = m_PolyOuts[i]; if (outRec.polyNode == null) continue; if (outRec.FirstLeft == null) polytree.AddChild(outRec.polyNode); else outRec.FirstLeft.polyNode.AddChild(outRec.polyNode); } }
//------------------------------------------------------------------------------ public bool Execute(ClipType clipType, PolyTree polytree) { return Execute(clipType, polytree, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); }
//------------------------------------------------------------------------------ public bool Execute(ClipType clipType, PolyTree polytree, PolyFillType subjFillType, PolyFillType clipFillType) { if (m_ExecuteLocked) return false; m_ExecuteLocked = true; m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; m_UsingPolyTree = true; bool succeeded = ExecuteInternal(); //build the return polygons ... if (succeeded) BuildResult2(polytree); m_ExecuteLocked = false; return succeeded; }