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 Polygon Cleaned(double distance = 1.415) { // distance = proximity in units/pixels below which vertices will be stripped. // Default ~= sqrt(2) so when adjacent vertices or semi-adjacent vertices have // both x & y coords within 1 unit, then the second vertex will be stripped. var pointCount = Count; if (pointCount == 0) { return(new Polygon()); } var outputPoints = new OutputPoint[pointCount]; for (var i = 0; i < pointCount; ++i) { outputPoints[i] = new OutputPoint(); } for (var i = 0; i < pointCount; ++i) { outputPoints[i].Point = this[i]; outputPoints[i].Next = outputPoints[(i + 1) % pointCount]; outputPoints[i].Next.Prev = outputPoints[i]; outputPoints[i].Index = 0; } var distSqrd = distance * distance; var op = outputPoints[0]; while (op.Index == 0 && op.Next != op.Prev) { if (GeometryHelper.PointsAreClose(op.Point, op.Prev.Point, distSqrd)) { op = ExcludeOutputPoint(op); pointCount--; } else if (GeometryHelper.PointsAreClose(op.Prev.Point, op.Next.Point, distSqrd)) { ExcludeOutputPoint(op.Next); op = ExcludeOutputPoint(op); pointCount -= 2; } else if (GeometryHelper.SlopesNearCollinear(op.Prev.Point, op.Point, op.Next.Point, distSqrd)) { op = ExcludeOutputPoint(op); pointCount--; } else { op.Index = 1; op = op.Next; } } if (pointCount < 3) { pointCount = 0; } var result = new Polygon(pointCount); for (var i = 0; i < pointCount; ++i) { result.Add(op.Point); op = op.Next; } return(result); }