import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
+import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
import java.io.Serializable;
/**
final Stroke routeLineStroke;
final double degenerateLineLength;
final double rounding;
+ final double offset;
transient Line2D line = new Line2D.Double();
transient Ellipse2D ellipse = new Ellipse2D.Double();
public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength,
- double rounding) {
+ double rounding, double offset) {
this.lineColor = lineColor;
this.branchPointColor = branchPointColor;
this.branchPointRadius = branchPointRadius;
this.routeLineStroke = routeLineStroke;
this.degenerateLineLength = degenerateLineLength;
this.rounding = rounding;
+ this.offset = offset;
+ }
+
+ public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength,
+ double rounding) {
+ this(lineColor, branchPointColor, branchPointRadius, lineStroke, routeLineStroke, degenerateLineLength, rounding, 0.0);
}
public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength) {
- this(lineColor, branchPointColor, branchPointRadius, lineStroke, routeLineStroke, degenerateLineLength, 0.0);
+ this(lineColor, branchPointColor, branchPointRadius, lineStroke, routeLineStroke, degenerateLineLength, 0.0, 0.0);
}
public Color getLineColor() {
if(rounding > 0.0) {
Object oldRenderingHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g.draw(round(path));
+ path = round(path);
+ if (offset != 0) {
+ path = offsetPath(path, offset);
+ }
+ g.draw(path);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldRenderingHint);
}
- else
+ 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) {
return rounding;
}
+ public double getOffset() {
+ return offset;
+ }
}