+ if(rounding > 0.0) {
+ Object oldRenderingHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ path = round(path);
+ if (offset != 0) {
+ path = offsetPath(path, offset);
+ }
+ g.draw(path);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldRenderingHint);
+ }
+ else {
+ if (offset != 0) {
+ path = offsetPath(path, offset);
+ }
+ g.draw(path);
+ }
+ }
+
+ private static Point2D getNormal(Point2D dir) {
+ return new Point2D.Double(-dir.getY(), dir.getX());
+ }
+
+ private static Point2D normalize(Point2D v) {
+ double d = Math.sqrt(v.getX() * v.getX() + v.getY() * v.getY());
+ v.setLocation(v.getX() / d, v.getY() / d);
+ return v;
+ }
+
+ private static Path2D offsetPath(Path2D path, double offset) {
+ Path2D result = new Path2D.Double();
+ PathIterator iter = new FlatteningPathIterator(path.getPathIterator(null), 0.05, 10);
+
+ double c[] = new double[6];
+ double initialX = 0;
+ double initialY = 0;
+ boolean first = true;
+ Point2D prevDir = null;
+ Point2D prevPos = null;
+
+ while (!iter.isDone()) {
+ int i = iter.currentSegment(c);
+ switch (i) {
+ case PathIterator.SEG_MOVETO:
+ if (first) {
+ initialX = c[0];
+ initialY = c[1];
+ first = false;
+ }
+ if (prevDir != null) {
+ Point2D N = normalize(getNormal(prevDir));
+ result.lineTo(prevPos.getX() + N.getX() * offset , prevPos.getY() + N.getY() * offset);
+ }
+ prevPos = new Point2D.Double(c[0], c[1]);
+ prevDir = null;
+ break;
+ case PathIterator.SEG_LINETO:
+ case PathIterator.SEG_CLOSE:
+ if (i == PathIterator.SEG_CLOSE) {
+ c[0] = initialX;
+ c[1] = initialY;
+ }
+ Point2D currentDir = new Point2D.Double(c[0] - prevPos.getX(), c[1] - prevPos.getY());
+ if (currentDir.getX() == 0.0 && currentDir.getY() == 0) break;
+
+ if (prevDir == null) {
+ Point2D N = normalize(getNormal(currentDir));
+ result.moveTo(prevPos.getX() + N.getX() * offset, prevPos.getY() + N.getY() * offset);
+ prevPos = new Point2D.Double(c[0], c[i]);
+ prevDir = currentDir;
+ } else {
+ Point2D N1 = normalize(getNormal(prevDir));
+ Point2D N2 = normalize(getNormal(currentDir));
+ Point2D N = normalize(new Point2D.Double(N1.getX() + N2.getX(), N1.getY() + N2.getY()));
+ double dot = N1.getX() * N.getX() + N1.getY() * N.getY();
+
+ if (!Double.isFinite(dot) || Math.abs(dot) < 0.1) {
+ result.lineTo(prevPos.getX() + (N1.getX() + N1.getY()) * offset, prevPos.getY() + (N1.getY() - N1.getX()) * offset);
+ result.lineTo(prevPos.getX() + (N2.getX() + N1.getY()) * offset, prevPos.getY() + (N2.getY() - N1.getX()) * offset);
+ prevPos = new Point2D.Double(c[0], c[i]);
+ prevDir = currentDir;
+ } else {
+ double Nx = N.getX() * offset / dot;
+ double Ny = N.getY() * offset / dot;
+ result.lineTo(prevPos.getX() + Nx, prevPos.getY() + Ny);
+ prevPos = new Point2D.Double(c[0], c[i]);
+ prevDir = currentDir;
+ }
+ }
+
+ break;
+ }
+ iter.next();
+ }
+ if (prevDir != null) {
+ Point2D N = normalize(getNormal(prevDir));
+ result.lineTo(prevPos.getX() + N.getX() * offset , prevPos.getY() + N.getY() * offset);
+ }
+ return result;
+ }
+
+ private Path2D round(Path2D path) {
+ Path2D newPath = new Path2D.Double();
+ PathIterator it = path.getPathIterator(new AffineTransform());
+ double[] coords = new double[6];
+ double newX=0.0, newY=0.0;
+ double curX=0.0, curY=0.0;
+ double oldX=0.0, oldY=0.0;
+ int state = 0;
+ while(!it.isDone()) {
+ int type = it.currentSegment(coords);
+ if(type == PathIterator.SEG_LINETO) {
+ newX = coords[0];
+ newY = coords[1];
+ if(state == 1) {
+ double dx1 = curX-oldX;
+ double dy1 = curY-oldY;
+ double dx2 = curX-newX;
+ double dy2 = curY-newY;
+ double r1 = Math.sqrt(dx1*dx1 + dy1*dy1);
+ double r2 = Math.sqrt(dx2*dx2 + dy2*dy2);
+ double maxRadius = 0.5 * Math.min(r1, r2);
+ double radius = Math.min(rounding, maxRadius);
+ double dx1Normalized = r1 > 0 ? dx1 / r1 : 0;
+ double dy1Normalized = r1 > 0 ? dy1 / r1 : 0;
+ double dx2Normalized = r2 > 0 ? dx2 / r2 : 0;
+ double dy2Normalized = r2 > 0 ? dy2 / r2 : 0;
+ newPath.lineTo(curX - radius*dx1Normalized, curY - radius*dy1Normalized);
+ newPath.curveTo(curX, curY,
+ curX, curY,
+ curX - radius*dx2Normalized, curY - radius*dy2Normalized);
+ }
+ else
+ ++state;
+ oldX = curX;
+ oldY = curY;
+ curX = newX;
+ curY = newY;
+ }
+ else {
+ if(state > 0) {
+ newPath.lineTo(curX, curY);
+ state = 0;
+ }
+ switch(type) {
+ case PathIterator.SEG_MOVETO:
+ curX = coords[0];
+ curY = coords[1];
+ newPath.moveTo(curX, curY);
+ break;
+ case PathIterator.SEG_QUADTO:
+ curX = coords[2];
+ curY = coords[3];
+ newPath.quadTo(coords[0], coords[1], coords[2], coords[3]);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ curX = coords[4];
+ curY = coords[5];
+ newPath.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+ break;
+ case PathIterator.SEG_CLOSE:
+ newPath.closePath();
+ break;
+ }
+ }
+ it.next();
+ }
+ if(state > 0)
+ newPath.lineTo(curX, curY);
+ return newPath;