]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/rendering/BasicConnectionStyle.java
Improvements to styling of connection lines
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / rendering / BasicConnectionStyle.java
index 2f218d8fab325c4c0f7e082a3f977a74fa798486..94de76bb447af1a435a8323ea7d24c4187ab7b4a 100644 (file)
@@ -17,9 +17,11 @@ import java.awt.RenderingHints;
 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;
 
 /**
@@ -37,12 +39,13 @@ public class BasicConnectionStyle implements ConnectionStyle, 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;
@@ -50,10 +53,16 @@ public class BasicConnectionStyle implements ConnectionStyle, Serializable {
         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() {
@@ -101,11 +110,101 @@ public class BasicConnectionStyle implements ConnectionStyle, Serializable {
         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) {
@@ -266,4 +365,7 @@ public class BasicConnectionStyle implements ConnectionStyle, Serializable {
         return rounding;
     }
 
+    public double getOffset() {
+        return offset;
+    }
 }