(refs #7177) Rounded connections 74/474/3
authorHannu Niemistö <hannu.niemisto@semantum.fi>
Tue, 2 May 2017 17:04:35 +0000 (20:04 +0300)
committerHannu Niemistö <hannu.niemisto@semantum.fi>
Tue, 2 May 2017 19:32:21 +0000 (22:32 +0300)
Adds HasRounding property to G2D that rounds connections in the
diagrams.

Change-Id: I2f5429f90e926b9569633056d50a233cf9f4c395

bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/rendering/BasicConnectionStyle.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java
bundles/org.simantics.diagram/src/org/simantics/diagram/connection/ConnectionVisuals.java
bundles/org.simantics.diagram/src/org/simantics/diagram/query/ConnectionVisualsRequest.java
bundles/org.simantics.g2d.ontology/graph.tg
bundles/org.simantics.g2d.ontology/graph/G2D.pgraph
bundles/org.simantics.g2d.ontology/src/org/simantics/diagram/stubs/G2DResource.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java
bundles/org.simantics.scenegraph/testcases/org/simantics/scenegraph/tests/TestRouteGraphNodeApplet.java

index a8661d324755eb2d99e374088aabe69cd6ec8727..b2895fb342d60fc1571b7f9b1fe4424c07144203 100644 (file)
@@ -13,10 +13,13 @@ package org.simantics.diagram.connection.rendering;
 
 import java.awt.Color;
 import java.awt.Graphics2D;
+import java.awt.RenderingHints;
 import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Ellipse2D;
 import java.awt.geom.Line2D;
 import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
 import java.io.Serializable;
 
 /**
@@ -32,18 +35,25 @@ public class BasicConnectionStyle implements ConnectionStyle, Serializable {
     final double                    branchPointRadius;
     final Stroke                    lineStroke;
     final Stroke                    routeLineStroke;
-    final double                    degenerateLineLength; 
+    final double                    degenerateLineLength;
+    final double                    rounding;
 
     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) {
+    public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength,
+            double rounding) {
         this.lineColor = lineColor;
         this.branchPointColor = branchPointColor;
         this.branchPointRadius = branchPointRadius;
         this.lineStroke = lineStroke;
         this.routeLineStroke = routeLineStroke;
         this.degenerateLineLength = degenerateLineLength;
+        this.rounding = rounding;
+    }
+    
+    public BasicConnectionStyle(Color lineColor, Color branchPointColor, double branchPointRadius, Stroke lineStroke, Stroke routeLineStroke, double degenerateLineLength) {
+        this(lineColor, branchPointColor, branchPointRadius, lineStroke, routeLineStroke, degenerateLineLength, 0.0);
     }
 
     public Color getLineColor() {
@@ -88,7 +98,83 @@ public class BasicConnectionStyle implements ConnectionStyle, Serializable {
             g.setColor(lineColor);
         if (lineStroke != null)
             g.setStroke(lineStroke);
-        g.draw(path);
+        if(rounding > 0.0) {
+            Object oldRenderingHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            g.draw(round(path));
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldRenderingHint);
+        }
+        else
+            g.draw(path);
+    }
+    
+    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 maxRadius = 0.5 * Math.min(Math.sqrt(dx1*dx1 + dy1*dy1), Math.sqrt(dx2*dx2 + dy2*dy2));
+                    double radius = Math.min(rounding, maxRadius);
+                    newPath.lineTo(curX + radius*Math.signum(oldX-curX), curY + radius*Math.signum(oldY-curY));
+                    newPath.curveTo(curX, curY,
+                                    curX, curY,
+                                    curX + radius*Math.signum(newX-curX), curY + radius*Math.signum(newY-curY));
+
+                    //newPath.lineTo(curX + round*Math.signum(oldX-curX), curY + round*Math.signum(oldY-curY));
+                    //newPath.lineTo(curX + round*Math.signum(newX-curX), curY + round*Math.signum(newY-curY));
+                    //newPath.lineTo(curX, curY);
+                }
+                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;
     }
 
     @Override
@@ -170,4 +256,8 @@ public class BasicConnectionStyle implements ConnectionStyle, Serializable {
         return true;
     }
 
+    public double getRounding() {
+        return rounding;
+    }
+
 }
index 99d9acb705388a6b85f242ec5485c08d1dcdc208..60f7a09dbb93d50a9ef7896b1e3703cd47a1945a 100644 (file)
@@ -564,7 +564,7 @@ public class RouteGraphUtils {
         Color branchPointColor = Color.BLACK;
         double branchPointRadius = 0.5;
         double degenerateLineLength = 0.8;
-
+        
         Color lineColor = cv != null ? cv.toColor() : null;
         if (lineColor == null)
             lineColor = Color.DARK_GRAY;
@@ -572,6 +572,7 @@ public class RouteGraphUtils {
         if (lineStroke == null)
             lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0);
         Stroke routeLineStroke = GeometryUtils.scaleStrokeWidth(lineStroke, 2);
+        double rounding = cv.rounding == null ? 0.0 : cv.rounding;
 
         return new BasicConnectionStyle(
                 lineColor,
@@ -579,7 +580,8 @@ public class RouteGraphUtils {
                 branchPointRadius,
                 lineStroke,
                 routeLineStroke,
-                degenerateLineLength);
+                degenerateLineLength,
+                rounding);
     }
 
     public static void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) {
index 5aa8048acb7e5d415936a96c2a5dc5e30cdec3f4..45e4079065f76f1932f26c04b5035f61229a13ec 100644 (file)
@@ -25,13 +25,15 @@ public class ConnectionVisuals {
     public final float[] color;
     public final StrokeType strokeType;
     public final Stroke stroke;
+    public final Double rounding;
 
-    public ConnectionVisuals(float[] color, StrokeType strokeType, Stroke stroke) {
+    public ConnectionVisuals(float[] color, StrokeType strokeType, Stroke stroke, Double rounding) {
         if (color != null && color.length < 3)
             throw new IllegalArgumentException("colors must have at least 3 components (rgb), got " + color.length);
         this.color = color;
         this.strokeType = strokeType;
         this.stroke = stroke;
+        this.rounding = rounding;
     }
 
     public Color toColor() {
@@ -47,6 +49,7 @@ public class ConnectionVisuals {
         final int prime = 31;
         int result = 1;
         result = prime * result + Arrays.hashCode(color);
+        result = prime * result + ((rounding == null) ? 0 : rounding.hashCode());
         result = prime * result + ((stroke == null) ? 0 : stroke.hashCode());
         result = prime * result + ((strokeType == null) ? 0 : strokeType.hashCode());
         return result;
@@ -58,20 +61,22 @@ public class ConnectionVisuals {
             return true;
         if (obj == null)
             return false;
-        if (!(obj instanceof ConnectionVisuals))
+        if (getClass() != obj.getClass())
             return false;
         ConnectionVisuals other = (ConnectionVisuals) obj;
         if (!Arrays.equals(color, other.color))
             return false;
+        if (rounding == null) {
+            if (other.rounding != null)
+                return false;
+        } else if (!rounding.equals(other.rounding))
+            return false;
         if (stroke == null) {
             if (other.stroke != null)
                 return false;
         } else if (!stroke.equals(other.stroke))
             return false;
-        if (strokeType == null) {
-            if (other.strokeType != null)
-                return false;
-        } else if (!strokeType.equals(other.strokeType))
+        if (strokeType != other.strokeType)
             return false;
         return true;
     }
index 3e70f1210dc9ac4fe38897e95ffe177c9092811b..4af440a52c931fa81013388d41d57ab01d85a466 100644 (file)
@@ -52,8 +52,9 @@ public class ConnectionVisualsRequest extends ResourceRead<ConnectionVisuals> {
 
         StrokeType strokeType = toStrokeType(g.getPossibleObject(structuralConnectionType, g2d.HasStrokeType));
         Stroke stroke = G2DUtils.getStroke(g, g.getPossibleObject(structuralConnectionType, g2d.HasStroke));
-
-        return new ConnectionVisuals(color, strokeType, stroke);
+        Double rounding = g.getPossibleRelatedValue(structuralConnectionType, g2d.HasRounding, Bindings.DOUBLE);
+        
+        return new ConnectionVisuals(color, strokeType, stroke, rounding);
     }
 
     StrokeType toStrokeType(Resource strokeType) {
index b75d2eb3ac6e587cf58d374f0a03f1a19a3f30a2..a9eea99f0e275eb450d7dddf58f7e392553cf9d3 100644 (file)
Binary files a/bundles/org.simantics.g2d.ontology/graph.tg and b/bundles/org.simantics.g2d.ontology/graph.tg differ
index cb458bed2b28b7e3609036f1a92f1d4d697ba78a..fc58ade46911d32717b362fb7bc6a319767f1c47 100644 (file)
@@ -92,6 +92,8 @@ G2D.StrokeType <T L0.Property
     @L0.tag L0.Enumeration
 G2D.StrokeType.Scaling : G2D.StrokeType
 G2D.StrokeType.Nonscaling : G2D.StrokeType
+G2D.HasRounding <R L0.HasProperty : L0.FunctionalRelation
+    --> L0.Double
 G2D.LineEnd <T L0.Property
     @L0.optionalProperty G2D.HasLineEndStyle
     @L0.singleProperty G2D.HasSize
index a3fbcf24ae4ae7cf3457ce4f80cbb22c1f5555e7..4cfd323a2bb735d958905647f3d570401bdee180 100644 (file)
@@ -82,6 +82,8 @@ public class G2DResource {
     public final Resource HasRadii_Inverse;
     public final Resource HasRasterImage;
     public final Resource HasRasterImage_Inverse;
+    public final Resource HasRounding;
+    public final Resource HasRounding_Inverse;
     public final Resource HasSVGDocument;
     public final Resource HasSVGDocument_Inverse;
     public final Resource HasSVGScript;
@@ -214,6 +216,8 @@ public class G2DResource {
         public static final String HasRadii_Inverse = "http://www.simantics.org/G2D-1.1/HasRadii/Inverse";
         public static final String HasRasterImage = "http://www.simantics.org/G2D-1.1/HasRasterImage";
         public static final String HasRasterImage_Inverse = "http://www.simantics.org/G2D-1.1/HasRasterImage/Inverse";
+        public static final String HasRounding = "http://www.simantics.org/G2D-1.1/HasRounding";
+        public static final String HasRounding_Inverse = "http://www.simantics.org/G2D-1.1/HasRounding/Inverse";
         public static final String HasSVGDocument = "http://www.simantics.org/G2D-1.1/HasSVGDocument";
         public static final String HasSVGDocument_Inverse = "http://www.simantics.org/G2D-1.1/HasSVGDocument/Inverse";
         public static final String HasSVGScript = "http://www.simantics.org/G2D-1.1/HasSVGScript";
@@ -356,6 +360,8 @@ public class G2DResource {
         HasRadii_Inverse = getResourceOrNull(graph, URIs.HasRadii_Inverse);
         HasRasterImage = getResourceOrNull(graph, URIs.HasRasterImage);
         HasRasterImage_Inverse = getResourceOrNull(graph, URIs.HasRasterImage_Inverse);
+        HasRounding = getResourceOrNull(graph, URIs.HasRounding);
+        HasRounding_Inverse = getResourceOrNull(graph, URIs.HasRounding_Inverse);
         HasSVGDocument = getResourceOrNull(graph, URIs.HasSVGDocument);
         HasSVGDocument_Inverse = getResourceOrNull(graph, URIs.HasSVGDocument_Inverse);
         HasSVGScript = getResourceOrNull(graph, URIs.HasSVGScript);
index ad71bb126185c1ef6c49bcb3e5c654440b19171f..4650d2e13587c46cc85e3a2cccc3ba8470c0bda5 100644 (file)
@@ -221,14 +221,14 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
                                 baseStyle.getBranchPointColor(), baseStyle.getBranchPointRadius(),
                                     dynamicStroke != null ? dynamicStroke : baseStyle.getLineStroke(), 
                                             dynamicStroke != null ? dynamicStroke : baseStyle.getRouteLineStroke(),
-                                                    baseStyle.getDegeneratedLineLength()));
+                                                    baseStyle.getDegeneratedLineLength(), baseStyle.getRounding()));
             } catch (Exception e) {
                renderer = new StyledRouteGraphRenderer(new BasicConnectionStyle(
                         dynamicColor != null ? dynamicColor : baseStyle.getLineColor(),
                                 baseStyle.getBranchPointColor(), baseStyle.getBranchPointRadius(),
                                     dynamicStroke != null ? dynamicStroke : baseStyle.getLineStroke(), 
                                             dynamicStroke != null ? dynamicStroke : baseStyle.getRouteLineStroke(),
-                                                    baseStyle.getDegeneratedLineLength()));
+                                                    baseStyle.getDegeneratedLineLength(), baseStyle.getRounding()));
             }
             
             
index 3a5e4438641788ca296ca3c0b0b0e59765f5b4f8..16d026fd555a90078eea29837c77f6e56d10816d 100644 (file)
@@ -54,7 +54,7 @@ public class TestRouteGraphNodeApplet extends JApplet implements Runnable {
     G2DSceneGraph sg;
     RouteGraph rg;
     RouteGraphNode rgn;
-    BasicConnectionStyle style = new BasicConnectionStyle(LINE_COLOR1, Color.BLACK, 3.0, LINE_STROKE, ROUTE_LINE_STROKE, 8);
+    BasicConnectionStyle style = new BasicConnectionStyle(LINE_COLOR1, Color.BLACK, 3.0, LINE_STROKE, ROUTE_LINE_STROKE, 8, 0.0);
 
     Timer timer = new Timer();
     TimerTask task = new TimerTask() {