public List <DTPolygon> Subtract(DTPolygon subject, DTPolygon clippingPolygon) { if (!DTUtility.BoundsCheck(subject.Contour, clippingPolygon.Contour)) { // There is no overlap at all, so output a copy of the subject polygon return(new List <DTPolygon>() { new DTPolygon(new List <Vector2>(subject.Contour)) }); } clipper.Clear(); // Add subject polygon paths clipper.AddPath(subject.Contour.ToIntPointList(), PolyType.ptSubject, true); foreach (var hole in subject.Holes) { clipper.AddPath(hole.ToIntPointList(), PolyType.ptSubject, true); } // Add clipping polygon paths clipper.AddPath(clippingPolygon.Contour.ToIntPointList(), PolyType.ptClip, true); foreach (var hole in clippingPolygon.Holes) { clipper.AddPath(hole.ToIntPointList(), PolyType.ptClip, true); } // Execute subtraction and store result in a PolyTree so that we can easily identify holes PolyTree clipperOutput = new PolyTree(); clipper.Execute(ClipType.ctDifference, clipperOutput, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero); // Convert Polytree into list of DTPolygons return(clipperOutput.ToDTPolygons()); }
public List <List <DTPolygon> > SubtractPolygroup(IEnumerable <DTPolygon> inputPolygroup, IEnumerable <DTPolygon> clippingPolygons) { // No bounds check. We could do a bounds check to return now if the polygroups are entirely // disjoint, but we do that in the explosion executor anyway clipper.Clear(); // Add subject polygon paths foreach (DTPolygon poly in inputPolygroup) { // Convert the points to IntPoint and add that path to Clipper List <IntPoint> contourPath = poly.Contour.ToIntPointList(); clipper.AddPath(contourPath, PolyType.ptSubject, true); foreach (var hole in poly.Holes) { // Convert the points to IntPoint and add that path to Clipper List <IntPoint> holePath = hole.ToIntPointList(); clipper.AddPath(holePath, PolyType.ptSubject, true); } } // Add clipping polygon paths foreach (DTPolygon poly in clippingPolygons) { clipper.AddPath(poly.Contour.ToIntPointList(), PolyType.ptClip, true); foreach (var hole in poly.Holes) { clipper.AddPath(hole.ToIntPointList(), PolyType.ptClip, true); } } // Execute subtraction and store result in a PolyTree so that we can easily identify holes PolyTree clipperOutput = new PolyTree(); clipper.Execute(ClipType.ctDifference, clipperOutput, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero); // Convert Polytree into list of DTPolygons List <DTPolygon> clipperOutputDT = clipperOutput.ToDTPolygons(); // Group the polygons into polygroups based on shared points return(clipperOutputDT.CreatePolygroups()); }
// WARNING: This implementation is currently very volatile and can cause multiple input polygon groups to fuse // together if a vertex from one is very close to the vertex of another. public List <List <List <DTPolygon> > > SubtractBulk(IEnumerable <IEnumerable <DTPolygon> > inputPolygroups, IEnumerable <DTPolygon> clippingPolygons) { // No bounds check. We could do a bounds check to return early if polygroups were entirely // disjoint, but we do that in the explosion executor anyway clipper.Clear(); // Map the points of each polygon to the index of the polygon group to which the polygon belongs Dictionary <Vector2, int> inputPointToPolygroup = new Dictionary <Vector2, int>(new DTUtility.ApproximateVector2Comparer()); // Add subject polygon paths { int inputGroupIndex = 0; foreach (IEnumerable <DTPolygon> inputPolygons in inputPolygroups) { foreach (DTPolygon poly in inputPolygons) { // Convert the points to IntPoint and add that path to Clipper List <IntPoint> contourPath = poly.Contour.ToIntPointList(); clipper.AddPath(contourPath, PolyType.ptSubject, true); // Map the points to the subject group index foreach (Vector2 point in poly.Contour) { inputPointToPolygroup[point] = inputGroupIndex; } foreach (var hole in poly.Holes) { // Convert the points to IntPoint and add that path to Clipper List <IntPoint> holePath = hole.ToIntPointList(); clipper.AddPath(holePath, PolyType.ptSubject, true); // Don't bother putting hole points into inputPointGroups } } ++inputGroupIndex; } } // Add clipping polygon paths foreach (DTPolygon poly in clippingPolygons) { clipper.AddPath(poly.Contour.ToIntPointList(), PolyType.ptClip, true); foreach (var hole in poly.Holes) { clipper.AddPath(hole.ToIntPointList(), PolyType.ptClip, true); } } // Execute subtraction and store result in a PolyTree so that we can easily identify holes PolyTree clipperOutput = new PolyTree(); clipper.Execute(ClipType.ctDifference, clipperOutput, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero); List <DTPolygon> clipperOutputDT = clipperOutput.ToDTPolygons(); // Construct a list of point sets to identify unique groups of connected output polygons List <HashSet <Vector2> > outputPointGroups = new List <HashSet <Vector2> >(); foreach (var poly in clipperOutputDT) { poly.MergeIntoPointGroups(outputPointGroups); } // Map all output groups to an input group index. int numInputGroups = inputPolygroups.Count(); List <List <HashSet <Vector2> > > inputOutputGroupMappings = new List <List <HashSet <Vector2> > >(numInputGroups); foreach (var s in inputPolygroups) { inputOutputGroupMappings.Add(new List <HashSet <Vector2> >()); } // Output groups that could not be mapped are in their own list. List <HashSet <Vector2> > unmappedOutputGroups = new List <HashSet <Vector2> >(); foreach (var points in outputPointGroups) { int inputGroupIndex = points.GetFirstPointGroupIndex(inputPointToPolygroup); if (inputGroupIndex >= 0) { inputOutputGroupMappings[inputGroupIndex].Add(points); } else { unmappedOutputGroups.Add(points); } } // Convert Polytree into list of DTPolygons List <List <List <DTPolygon> > > output = new List <List <List <DTPolygon> > >(numInputGroups + unmappedOutputGroups.Count); for (int i = 0; i < numInputGroups + unmappedOutputGroups.Count; ++i) { output.Add(new List <List <DTPolygon> >()); if (i < numInputGroups) { for (int j = 0; j < inputOutputGroupMappings[i].Count; ++j) { output[i].Add(new List <DTPolygon>()); } } } foreach (var poly in clipperOutputDT) { // Find the correct place to put this polygon in the output structure int inputGroupIndex = poly.GetFirstPointGroupIndex(inputPointToPolygroup); if (inputGroupIndex >= 0) { int outputGroupIndex = poly.GetFirstPointGroupIndex(inputOutputGroupMappings[inputGroupIndex]); if (outputGroupIndex >= 0) { // Matched input group and output group output[inputGroupIndex][outputGroupIndex].Add(poly); } else { // Matched input group, new output group output[inputGroupIndex].Add(new List <DTPolygon>() { poly }); } } else { int outputGroupIndex = poly.GetFirstPointGroupIndex(unmappedOutputGroups); if (outputGroupIndex >= 0) { // No input group, matched output group output[inputOutputGroupMappings.Count + outputGroupIndex].Add(new List <DTPolygon>() { poly }); } else { // No input group, new output group output.Add(new List <List <DTPolygon> >() { new List <DTPolygon>() { poly } }); } } } return(output); }