Esempio n. 1
0
        /// <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);
        }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
        /// <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);
        }
Esempio n. 4
0
        /// <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);
        }
Esempio n. 5
0
        /// <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);
        }