Example #1
0
        public PolygonPath Cleaned(double distance = 1.415)
        {
            var cleaned = new PolygonPath(Count);

            cleaned.AddRange(this.Select(polygon => polygon.Cleaned(distance)));
            return(cleaned);
        }
Example #2
0
 public void AddPaths(PolygonPath paths, JoinType joinType, EndType endType)
 {
     foreach (var p in paths)
     {
         AddPath(p, joinType, endType);
     }
 }
Example #3
0
        public static bool Contains(PolygonPath subject, PolygonPath clip)
        {
            // The subjectPath polygon path contains the clipPath polygon path if:
            // 1. The union operation must result in one polygon result
            // 2. The area of the union equals subjectPath area.
            // 3. The area of the clipPath must be <= than the area of subjectPath.

            var solution = new PolygonPath();
            var clipper  = new Clipper();

            if (!clipper.Execute(ClipOperation.Union, subject, clip, solution))
            {
                return(false);
            }

            if (solution.Count != 1)
            {
                return(false);
            }
            if (!GeometryHelper.NearZero(subject.Area - solution.Area))
            {
                return(false);
            }
            return(clip.Area <= subject.Area);
        }
Example #4
0
        private static void AddNodeToPath(PolygonNode node, NodeType nodeType, PolygonPath paths)
        {
            var match = true;

            switch (nodeType)
            {
            case NodeType.Open: return;

            case NodeType.Closed: match = !node.IsOpen; break;

            case NodeType.Any:
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(nodeType), nodeType, null);
            }

            if (node.Polygon.Count > 0 && match)
            {
                paths.Add(node.Polygon);
            }

            foreach (var childNode in node.Children)
            {
                AddNodeToPath(childNode, nodeType, paths);
            }
        }
Example #5
0
        public PolygonPath GetClosedPath()
        {
            var path = new PolygonPath {
                Capacity = Children.Count
            };

            AddNodeToPath(this, NodeType.Closed, path);
            return(path);
        }
Example #6
0
        public PolygonPath GetOpenPath()
        {
            var path = new PolygonPath {
                Capacity = Children.Count
            };

            path.AddRange(from node in Children where node.IsOpen select node.Polygon);
            return(path);
        }
Example #7
0
        public static PolygonPath Minkowski(Polygon pattern, Polygon path, bool isSum, bool isClosed)
        {
            var delta        = isClosed ? 1 : 0;
            var patternCount = pattern.Count;
            var pathCount    = path.Count;
            var solution     = new PolygonPath(pathCount);

            if (isSum)
            {
                for (var i = 0; i < pathCount; i++)
                {
                    var polygon = new Polygon(patternCount);
                    polygon.AddRange(
                        pattern.Select(p => new PointL(path[i].X + p.X, path[i].Y + p.Y)));
                    solution.Add(polygon);
                }
            }
            else
            {
                for (var i = 0; i < pathCount; i++)
                {
                    var polygon = new Polygon(patternCount);
                    polygon.AddRange(pattern.Select(p => new PointL(path[i].X - p.X, path[i].Y - p.Y)));
                    solution.Add(polygon);
                }
            }

            var quads = new PolygonPath((pathCount + delta) * (patternCount + 1));

            for (var i = 0; i < pathCount - 1 + delta; i++)
            {
                for (var j = 0; j < patternCount; j++)
                {
                    var quad = new Polygon(4)
                    {
                        solution[i % pathCount][j % patternCount],
                        solution[(i + 1) % pathCount][j % patternCount],
                        solution[(i + 1) % pathCount][(j + 1) % patternCount],
                        solution[i % pathCount][(j + 1) % patternCount]
                    };

                    if (quad.Orientation == PolygonOrientation.Clockwise)
                    {
                        quad.Reverse();
                    }
                    quads.Add(quad);
                }
            }

            return(quads);
        }
Example #8
0
        public void Execute(ref PolygonPath solution, double delta)
        {
            solution.Clear();
            FixOrientations();
            DoOffset(delta);

            //now clean up 'corners' ...
            var clipper = new Clipper();

            if (delta > 0)
            {
                clipper.Execute(
                    ClipOperation.Union,
                    _destinationPolygons,
                    null,
                    solution,
                    false,
                    PolygonFillType.Positive, PolygonFillType.Positive);
            }
            else
            {
                var r     = Clipper.GetBounds(_destinationPolygons);
                var outer = new Polygon
                {
                    new IntPoint(r.Left - 10, r.Bottom + 10),
                    new IntPoint(r.Right + 10, r.Bottom + 10),
                    new IntPoint(r.Right + 10, r.Top - 10),
                    new IntPoint(r.Left - 10, r.Top - 10)
                };

                clipper.ReverseOrientation = true;

                clipper.Execute(
                    ClipOperation.Union,
                    new PolygonPath(outer),
                    null,
                    solution,
                    false,
                    PolygonFillType.Negative,
                    PolygonFillType.Negative);

                if (solution.Count > 0)
                {
                    solution.RemoveAt(0);
                }
            }
        }
Example #9
0
        public static PolygonPath MinkowskiSum(Polygon pattern, PolygonPath paths, bool pathIsClosed)
        {
            var solution = new PolygonPath();
            var clipper  = new Clipper();

            foreach (var polygon in paths)
            {
                var tmp = Minkowski(pattern, polygon, true, pathIsClosed);
                clipper.AddPath(tmp, PolygonKind.Subject);
                if (!pathIsClosed)
                {
                    continue;
                }

                var translated = polygon.Translated(pattern[0]);
                clipper.AddPath(translated, PolygonKind.Clip);
            }

            clipper.Execute(ClipOperation.Union, solution, PolygonFillType.NonZero, PolygonFillType.NonZero);

            return(solution);
        }
Example #10
0
        private void DoOffset(double delta)
        {
            _destinationPolygons = new PolygonPath();
            _delta = delta;

            //if Zero offset, just copy any CLOSED polygons to m_p and return ...
            if (GeometryHelper.NearZero(delta))
            {
                _destinationPolygons.Capacity = _polygonNodes.Children.Count;
                foreach (var node in _polygonNodes.Children)
                {
                    if (node.EndType == EndType.ClosedPolygon)
                    {
                        _destinationPolygons.Add(node.Polygon);
                    }
                }
                return;
            }

            // see offset_triginometry3.svg in the documentation folder ...
            if (MiterLimit > 2)
            {
                _miterLim = 2 / (MiterLimit * MiterLimit);
            }
            else
            {
                _miterLim = 0.5;
            }

            double y;

            if (ArcTolerance <= 0.0)
            {
                y = DefaulArcTolerance;
            }
            else if (ArcTolerance > Math.Abs(delta) * DefaulArcTolerance)
            {
                y = Math.Abs(delta) * DefaulArcTolerance;
            }
            else
            {
                y = ArcTolerance;
            }

            // see offset_triginometry2.svg in the documentation folder ...
            var steps = Math.PI / Math.Acos(1 - y / Math.Abs(delta));

            _sin         = Math.Sin(TwpPi / steps);
            _cos         = Math.Cos(TwpPi / steps);
            _stepsPerRad = steps / TwpPi;
            if (delta < 0.0)
            {
                _sin = -_sin;
            }

            _destinationPolygons.Capacity = _polygonNodes.Children.Count * 2;

            foreach (var node in _polygonNodes.Children)
            {
                _sourcePolygon = node.Polygon;

                var len = _sourcePolygon.Count;

                if (len == 0 ||
                    delta <= 0 &&
                    (len < 3 || node.EndType != EndType.ClosedPolygon))
                {
                    continue;
                }

                _destinationPolygon = new Polygon();

                if (len == 1)
                {
                    if (node.JoinType == JoinType.Round)
                    {
                        var x = 1.0;
                        var Y = 0.0;
                        for (var j = 1; j <= steps; j++)
                        {
                            _destinationPolygon.Add(new IntPoint(
                                                        (_sourcePolygon[0].X + x * delta).RoundToLong(),
                                                        (_sourcePolygon[0].Y + Y * delta).RoundToLong()));
                            var x2 = x;
                            x = x * _cos - _sin * Y;
                            Y = x2 * _sin + Y * _cos;
                        }
                    }
                    else
                    {
                        var x = -1.0;
                        var Y = -1.0;

                        for (var j = 0; j < 4; ++j)
                        {
                            _destinationPolygon.Add(new IntPoint(
                                                        (_sourcePolygon[0].X + x * delta).RoundToLong(),
                                                        (_sourcePolygon[0].Y + Y * delta).RoundToLong()));

                            if (x < 0)
                            {
                                x = 1;
                            }
                            else if (Y < 0)
                            {
                                Y = 1;
                            }
                            else
                            {
                                x = -1;
                            }
                        }
                    }
                    _destinationPolygons.Add(_destinationPolygon);
                    continue;
                }

                //build _normals ...
                _normals.Clear();
                _normals.Capacity = len;
                for (int j = 0; j < len - 1; j++)
                {
                    _normals.Add(GetUnitNormal(_sourcePolygon[j], _sourcePolygon[j + 1]));
                }
                if (node.EndType == EndType.ClosedLine ||
                    node.EndType == EndType.ClosedPolygon)
                {
                    _normals.Add(GetUnitNormal(_sourcePolygon[len - 1], _sourcePolygon[0]));
                }
                else
                {
                    _normals.Add(new DoublePoint(_normals[len - 2]));
                }

                if (node.EndType == EndType.ClosedPolygon)
                {
                    int k = len - 1;
                    for (int j = 0; j < len; j++)
                    {
                        OffsetPoint(j, ref k, node.JoinType);
                    }
                    _destinationPolygons.Add(_destinationPolygon);
                }
                else if (node.EndType == EndType.ClosedLine)
                {
                    int k = len - 1;
                    for (int j = 0; j < len; j++)
                    {
                        OffsetPoint(j, ref k, node.JoinType);
                    }
                    _destinationPolygons.Add(_destinationPolygon);
                    _destinationPolygon = new Polygon();
                    //re-build _normals ...
                    DoublePoint n = _normals[len - 1];
                    for (int j = len - 1; j > 0; j--)
                    {
                        _normals[j] = new DoublePoint(-_normals[j - 1].X, -_normals[j - 1].Y);
                    }
                    _normals[0] = new DoublePoint(-n.X, -n.Y);
                    k           = 0;
                    for (int j = len - 1; j >= 0; j--)
                    {
                        OffsetPoint(j, ref k, node.JoinType);
                    }
                    _destinationPolygons.Add(_destinationPolygon);
                }
                else
                {
                    var k = 0;
                    for (var j = 1; j < len - 1; ++j)
                    {
                        OffsetPoint(j, ref k, node.JoinType);
                    }

                    IntPoint pt1;
                    if (node.EndType == EndType.OpenButt)
                    {
                        var j = len - 1;
                        pt1 = new IntPoint(
                            (_sourcePolygon[j].X + _normals[j].X * delta).RoundToLong(),
                            (_sourcePolygon[j].Y + _normals[j].Y * delta).RoundToLong());

                        _destinationPolygon.Add(pt1);

                        pt1 = new IntPoint(
                            (_sourcePolygon[j].X - _normals[j].X * delta).RoundToLong(),
                            (_sourcePolygon[j].Y - _normals[j].Y * delta).RoundToLong());

                        _destinationPolygon.Add(pt1);
                    }
                    else
                    {
                        var j = len - 1;
                        k           = len - 2;
                        _sinA       = 0;
                        _normals[j] = new DoublePoint(-_normals[j].X, -_normals[j].Y);
                        if (node.EndType == EndType.OpenSquare)
                        {
                            DoSquare(j, k);
                        }
                        else
                        {
                            DoRound(j, k);
                        }
                    }

                    //re-build _normals ...
                    for (int j = len - 1; j > 0; j--)
                    {
                        _normals[j] = new DoublePoint(-_normals[j - 1].X, -_normals[j - 1].Y);
                    }

                    _normals[0] = new DoublePoint(-_normals[1].X, -_normals[1].Y);

                    k = len - 1;
                    for (int j = k - 1; j > 0; --j)
                    {
                        OffsetPoint(j, ref k, node.JoinType);
                    }

                    if (node.EndType == EndType.OpenButt)
                    {
                        pt1 = new IntPoint(
                            (_sourcePolygon[0].X - _normals[0].X * delta).RoundToLong(),
                            (_sourcePolygon[0].Y - _normals[0].Y * delta).RoundToLong());

                        _destinationPolygon.Add(pt1);

                        pt1 = new IntPoint(
                            (_sourcePolygon[0].X + _normals[0].X * delta).RoundToLong(),
                            (_sourcePolygon[0].Y + _normals[0].Y * delta).RoundToLong());

                        _destinationPolygon.Add(pt1);
                    }
                    else
                    {
                        _sinA = 0;
                        if (node.EndType == EndType.OpenSquare)
                        {
                            DoSquare(0, 1);
                        }
                        else
                        {
                            DoRound(0, 1);
                        }
                    }
                    _destinationPolygons.Add(_destinationPolygon);
                }
            }
        }
Example #11
0
        public static bool SimplifyPolygon <T>(PolygonPath paths, T solution) where T : IClipSolution
        {
            var clipper = new Clipper();

            return(clipper.Execute(ClipOperation.Union, paths, null, solution, true));
        }