/// <summary> /// Convert self intersecting polygons into simple polygons /// </summary> /// <param name="polys"></param> /// <param name="fillType"></param> /// <returns></returns> public static List<List<IntPoint>> SimplifyPolygons(IEnumerable<List<IntPoint>> polys, PolyFillType fillType = PolyFillType.EvenOdd) { Contract.Requires(polys != null); Contract.Requires(Contract.ForAll(polys, a => a != null)); Contract.Ensures(Contract.Result<List<List<IntPoint>>>() != null); var result = new List<List<IntPoint>>(); var c = new Clipper { ForceSimple = true }; c.AddPolygons(polys, PolyType.Subject); c.Execute(ClipType.Union, result, fillType, fillType); return result; }
private PolyOffsetBuilder(IReadOnlyList<IReadOnlyList<IntPoint>> points, List<List<IntPoint>> solution, bool isPolygon, double delta, JoinType jointype, EndType endtype, double limit = 0) { Contract.Requires(points != null); Contract.Requires(solution != null); if (ReferenceEquals(solution, points)) throw new ArgumentException("Input and Output parameters cannot be the same", nameof(solution)); if (delta == 0) return; _p = points; _delta = delta; _rmin = 0.5; if (jointype == JoinType.Miter) { if (limit > 2) _rmin = 2.0 / (limit * limit); limit = 0.25; //just in case endtype == etRound } else { if (limit <= 0) limit = 0.25; else if (limit > Math.Abs(delta)) limit = Math.Abs(delta); } solution.Clear(); for (_i = 0; _i < points.Count; _i++) { var len = points[_i].Count; if (len == 0 || (len < 3 && delta <= 0)) continue; else if (len == 1) { _currentPoly = new List<IntPoint>(); _currentPoly = InternalHelpers.BuildArc(points[_i][0], 0, 2 * Math.PI, delta, limit); solution.Add(_currentPoly); continue; } var forceClose = points[_i][0].Equals(points[_i][len - 1]); if (forceClose) len--; //build normals ... _normals.Clear(); for (var j = 0; j < len - 1; ++j) _normals.Add(points[_i][j].UnitNormal(points[_i][j + 1])); if (isPolygon || forceClose) _normals.Add(points[_i][len - 1].UnitNormal(points[_i][0])); else _normals.Add(_normals[len - 2]); _currentPoly = new List<IntPoint>(); if (isPolygon || forceClose) { _k = len - 1; for (_j = 0; _j < len; ++_j) OffsetPoint(jointype, limit); solution.Add(_currentPoly); if (!isPolygon) { _currentPoly = new List<IntPoint>(); _delta = -_delta; _k = len - 1; for (_j = 0; _j < len; ++_j) OffsetPoint(jointype, limit); _delta = -_delta; _currentPoly.Reverse(); solution.Add(_currentPoly); } } else { _k = 0; for (_j = 1; _j < len - 1; ++_j) OffsetPoint(jointype, limit); IntPoint pt1; if (endtype == EndType.OpenButt) { _j = len - 1; pt1 = new IntPoint(InternalHelpers.Round(points[_i][_j].X + _normals[_j].X * delta), InternalHelpers.Round(points[_i][_j].Y + _normals[_j].Y * delta)); AddPoint(pt1); pt1 = new IntPoint(InternalHelpers.Round(points[_i][_j].X - _normals[_j].X * delta), InternalHelpers.Round(points[_i][_j].Y - _normals[_j].Y * delta)); AddPoint(pt1); } else { _j = len - 1; _k = len - 2; _normals[_j] = new DoublePoint(-_normals[_j].X, -_normals[_j].Y); if (endtype == EndType.OpenSquare) DoSquare(); else DoRound(limit); } //re-build Normals ... for (var 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 (_j = _k - 1; _j > 0; --_j) OffsetPoint(jointype, limit); if (endtype == EndType.OpenButt) { pt1 = new IntPoint(InternalHelpers.Round(points[_i][0].X - _normals[0].X * delta), InternalHelpers.Round(points[_i][0].Y - _normals[0].Y * delta)); AddPoint(pt1); pt1 = new IntPoint(InternalHelpers.Round(points[_i][0].X + _normals[0].X * delta), InternalHelpers.Round(points[_i][0].Y + _normals[0].Y * delta)); AddPoint(pt1); } else { _k = 1; if (endtype == EndType.OpenSquare) DoSquare(); else DoRound(limit); } solution.Add(_currentPoly); } } //finally, clean up untidy corners ... var clpr = new Clipper(); clpr.AddPolygons(solution, PolyType.Subject); if (delta > 0) { clpr.Execute(ClipType.Union, solution, PolyFillType.Positive, PolyFillType.Positive); } else { var r = clpr.GetBounds(); var outer = new List<IntPoint>(4) { 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) }; clpr.AddPolygon(outer, PolyType.Subject); clpr.ReverseSolution = true; clpr.Execute(ClipType.Union, solution, PolyFillType.Negative, PolyFillType.Negative); if (solution.Count > 0) solution.RemoveAt(0); } }