/// <summary> /// Create a collection of profiles from a collection of polygons. Inner polygons will be treated as voids in alternating fashion. /// </summary> /// <param name="polygons">The polygons to sort into profiles</param> /// <param name="tolerance">An optional tolerance.</param> /// <returns></returns> public static List <Profile> CreateFromPolygons(IEnumerable <Polygon> polygons, double tolerance = Vector3.EPSILON) { Clipper clipper = new Clipper(); foreach (var polygon in polygons) { var clipperPath = polygon.ToClipperPath(tolerance); clipper.AddPath(clipperPath, PolyType.ptSubject, true); } PolyTree solution = new PolyTree(); clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftEvenOdd); var joinedProfiles = solution.ToProfiles(tolerance); return(joinedProfiles); }
/// <summary> /// Split a set of profiles with a collection of open polylines, with an optional gap between results. /// </summary> /// <param name="profiles">The profiles to split</param> /// <param name="splitLines">The polylines defining the splits.</param> /// <param name="gapSize">An optional gap size between split pieces. If splits are failing, it can be helpful to increase this.</param> /// <param name="tolerance">An optional tolerance.</param> public static List <Profile> Split(IEnumerable <Profile> profiles, IEnumerable <Polyline> splitLines, double gapSize = 0, double tolerance = Vector3.EPSILON) { // We're doing something a little bit questionable here — we're offsetting the split curves by a hair's width // so that clipper can handle them as a subtraction, since it doesn't have a built-in split mechanism. // We increase the tolerance so that the result is well within the specified tolerance of the expected result. // This is imperfect, but no more imperfect than all of the other clipper-based operations we currently employ. var internalTolerance = tolerance / 10; // to keep splits within tolerance, we execute clipper at a 10x smaller tolerance. Clipper clipper = new Clipper(); foreach (var profile in profiles) { var clipperPaths = profile.ToClipperPaths(internalTolerance); clipper.AddPaths(clipperPaths, PolyType.ptSubject, true); } foreach (var line in splitLines) { var unionClipper = new Clipper(); // This is basically the same as // line.Offset(offsetDist, EndType.Butt, internalTolerance), // but without the unneccessary conversion back to Elements geometry. var co = new ClipperOffset(); var offsetSolution = new List <List <IntPoint> >(); var offsetPath = line.ToClipperPath(internalTolerance); var offsetDist = (internalTolerance) + gapSize; var clipperScale = 1.0 / internalTolerance; co.AddPath(offsetPath, JoinType.jtMiter, ClipperLib.EndType.etOpenButt); co.Execute(ref offsetSolution, offsetDist * clipperScale); List <List <IntPoint> > unionSolution = new List <List <IntPoint> >(); unionClipper.AddPaths(offsetSolution, PolyType.ptSubject, true); unionClipper.Execute(ClipType.ctUnion, unionSolution, PolyFillType.pftNonZero); clipper.AddPaths(unionSolution, PolyType.ptClip, true); } PolyTree solution = new PolyTree(); clipper.Execute(ClipType.ctDifference, solution, PolyFillType.pftNonZero); var joinedProfiles = solution.ToProfiles(internalTolerance); return(joinedProfiles); }
/// <summary> /// Constructs the intersections between two sets of profiles. /// </summary> /// <param name="firstSet">The first set of profiles to intersect with.</param> /// <param name="secondSet">The second set of profiles to intersect with.</param> /// <param name="tolerance">An optional tolerance.</param> /// <returns>A new list of profiles comprising the overlap between the first set and the second set.</returns> public static List <Profile> Intersection(IEnumerable <Profile> firstSet, IEnumerable <Profile> secondSet, double tolerance = Vector3.EPSILON) { Clipper clipper = new Clipper(); foreach (var profile in firstSet) { var clipperPaths = profile.ToClipperPaths(tolerance); clipper.AddPaths(clipperPaths, PolyType.ptSubject, true); } foreach (var profile in secondSet) { var clipperPaths = profile.ToClipperPaths(tolerance); clipper.AddPaths(clipperPaths, PolyType.ptClip, true); } PolyTree solution = new PolyTree(); clipper.Execute(ClipType.ctIntersection, solution, PolyFillType.pftNonZero); var joinedProfiles = solution.ToProfiles(tolerance); return(joinedProfiles); }
/// <summary> /// Perform a union operation on a set of multiple profiles. /// </summary> /// <param name="profiles">The profiles with which to create a union.</param> /// <param name="tolerance">An optional tolerance.</param> /// <returns>A new list of profiles comprising the union of all input profiles.</returns> public static List <Profile> UnionAll(IEnumerable <Profile> profiles, double tolerance = Vector3.EPSILON) { Clipper clipper = new Clipper(); foreach (var profile in profiles) { var subjectPolygons = new List <Polygon> { profile.Perimeter }; if (profile.Voids != null && profile.Voids.Count > 0) { subjectPolygons.AddRange(profile.Voids); } var clipperPaths = subjectPolygons.Select(s => s.ToClipperPath(tolerance)).ToList(); clipper.AddPaths(clipperPaths, PolyType.ptSubject, true); } PolyTree solution = new PolyTree(); clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive); var joinedProfiles = solution.ToProfiles(tolerance); return(joinedProfiles); }
/// <summary> /// Offset profiles by a given distance. /// </summary> /// <param name="profiles">The profiles to offset.</param> /// <param name="distance">The offset distance.</param> /// <param name="tolerance">An optional tolerance.</param> /// <returns>A collection of resulting profiles.</returns> public static List <Profile> Offset(IEnumerable <Profile> profiles, double distance, double tolerance = Vector3.EPSILON) { var clipperScale = 1.0 / tolerance; ClipperOffset clipper = new ClipperOffset(); foreach (var profile in profiles) { var subjectPolygons = new List <Polygon> { profile.Perimeter }; if (profile.Voids != null && profile.Voids.Count > 0) { subjectPolygons.AddRange(profile.Voids); } var clipperPaths = subjectPolygons.Select(s => s.ToClipperPath(tolerance)).ToList(); clipper.AddPaths(clipperPaths, JoinType.jtMiter, ClipperLib.EndType.etClosedPolygon); } PolyTree solution = new PolyTree(); clipper.Execute(ref solution, distance * clipperScale); var joinedProfiles = solution.ToProfiles(tolerance); return(joinedProfiles); }