//------------------------------------------------------------------------------ private void DoOffset(double d) { solution = null; delta = d; double absDelta = Math.Abs(d); //if a Zero offset, then just copy CLOSED polygons to FSolution and return ... if (absDelta < Tolerance) { solution = new Paths(nodes.Count); foreach (PathNode node in nodes) { if (node.endType == EndType.Polygon) { solution.Add(node.path); } } return; } //MiterLimit: see offset_triginometry3.svg in the documentation folder ... if (MiterLimit > 2) { miterLim = 2 / (MiterLimit * MiterLimit); } else { miterLim = 0.5; } double arcTol; if (ArcTolerance < DefaultArcFrac) { arcTol = absDelta * DefaultArcFrac; } else { arcTol = ArcTolerance; } //see offset_triginometry2.svg in the documentation folder ... double steps = Math.PI / Math.Acos(1 - arcTol / absDelta); //steps per 360 degrees if (steps > absDelta * Math.PI) { steps = absDelta * Math.PI; //ie excessive precision check } sin = Math.Sin(TwoPi / steps); cos = Math.Cos(TwoPi / steps); if (d < 0) { sin = -sin; } stepsPerRad = steps / TwoPi; solution = new Paths(nodes.Count * 2); foreach (PathNode node in nodes) { pathIn = node.path; pathOut = new Path(); int pathInCnt = pathIn.Count; //if a single vertex then build circle or a square ... if (pathInCnt == 1) { if (node.joinType == JoinType.Round) { double X = 1.0, Y = 0.0; for (int j = 1; j <= steps; j++) { pathOut.Add(new Point( Round(pathIn[0].X + X * delta), Round(pathIn[0].Y + Y * delta))); double X2 = X; X = X * cos - sin * Y; Y = X2 * sin + Y * cos; } } else { double X = -1.0, Y = -1.0; for (int j = 0; j < 4; ++j) { pathOut.Add(new Point( Round(pathIn[0].X + X * delta), Round(pathIn[0].Y + Y * delta))); if (X < 0) { X = 1; } else if (Y < 0) { Y = 1; } else { X = -1; } } } solution.Add(pathOut); continue; } //end of single vertex offsetting //build norms ... norms.Clear(); norms.Capacity = pathInCnt; for (int j = 0; j < pathInCnt - 1; j++) { norms.Add(GetUnitNormal(pathIn[j], pathIn[j + 1])); } if (node.endType == EndType.OpenJoined || node.endType == EndType.Polygon) { norms.Add(GetUnitNormal(pathIn[pathInCnt - 1], pathIn[0])); } else { norms.Add(new PointD(norms[pathInCnt - 2])); } if (node.endType == EndType.Polygon) { int k = pathInCnt - 1; for (int j = 0; j < pathInCnt; j++) { OffsetPoint(j, ref k, node.joinType); } solution.Add(pathOut); } else if (node.endType == EndType.OpenJoined) { int k = pathInCnt - 1; for (int j = 0; j < pathInCnt; j++) { OffsetPoint(j, ref k, node.joinType); } solution.Add(pathOut); pathOut = new Path(); //re-build norms ... PointD n = norms[pathInCnt - 1]; for (int j = pathInCnt - 1; j > 0; j--) { norms[j] = new PointD(-norms[j - 1].X, -norms[j - 1].Y); } norms[0] = new PointD(-n.X, -n.Y); k = 0; for (int j = pathInCnt - 1; j >= 0; j--) { OffsetPoint(j, ref k, node.joinType); } solution.Add(pathOut); } else { int k = 0; for (int j = 1; j < pathInCnt - 1; j++) { OffsetPoint(j, ref k, node.joinType); } Point pt1; if (node.endType == EndType.OpenButt) { int j = pathInCnt - 1; pt1 = new Point((int)Round(pathIn[j].X + norms[j].X * delta), (int)Round(pathIn[j].Y + norms[j].Y * delta)); pathOut.Add(pt1); pt1 = new Point((int)Round(pathIn[j].X - norms[j].X * delta), (int)Round(pathIn[j].Y - norms[j].Y * delta)); pathOut.Add(pt1); } else { int j = pathInCnt - 1; k = pathInCnt - 2; sinA = 0; norms[j] = new PointD(-norms[j].X, -norms[j].Y); if (node.endType == EndType.OpenSquare) { DoSquare(j, k); } else { DoRound(j, k); } } //reverse norms ... for (int j = pathInCnt - 1; j > 0; j--) { norms[j] = new PointD(-norms[j - 1].X, -norms[j - 1].Y); } norms[0] = new PointD(-norms[1].X, -norms[1].Y); k = pathInCnt - 1; for (int j = k - 1; j > 0; --j) { OffsetPoint(j, ref k, node.joinType); } if (node.endType == EndType.OpenButt) { pt1 = new Point(Round(pathIn[0].X - norms[0].X * delta), Round(pathIn[0].Y - norms[0].Y * delta)); pathOut.Add(pt1); pt1 = new Point(Round(pathIn[0].X + norms[0].X * delta), Round(pathIn[0].Y + norms[0].Y * delta)); pathOut.Add(pt1); } else { k = 1; sinA = 0; if (node.endType == EndType.OpenSquare) { DoSquare(0, 1); } else { DoRound(0, 1); } } solution.Add(pathOut); } } }
public PointD(PointD dp) { this.X = dp.X; this.Y = dp.Y; }