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); }
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); } }
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); } } }