public PolygonPath Cleaned(double distance = 1.415) { var cleaned = new PolygonPath(Count); cleaned.AddRange(this.Select(polygon => polygon.Cleaned(distance))); return(cleaned); }
public void AddPaths(PolygonPath paths, JoinType joinType, EndType endType) { foreach (var p in paths) { AddPath(p, joinType, endType); } }
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); }
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); } }
public PolygonPath GetClosedPath() { var path = new PolygonPath { Capacity = Children.Count }; AddNodeToPath(this, NodeType.Closed, path); return(path); }
public PolygonPath GetOpenPath() { var path = new PolygonPath { Capacity = Children.Count }; path.AddRange(from node in Children where node.IsOpen select node.Polygon); return(path); }
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); }
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); } } }
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); }
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); } } }
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)); }