]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminal.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / RouteTerminal.java
diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminal.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminal.java
new file mode 100644 (file)
index 0000000..31064ad
--- /dev/null
@@ -0,0 +1,452 @@
+/*******************************************************************************\r
+ * Copyright (c) 2011 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.connection;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+\r
+import java.awt.geom.Rectangle2D;\r
+import java.io.PrintStream;\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+\r
+import org.simantics.diagram.connection.RouteGraph.Interval;\r
+import org.simantics.diagram.connection.RouteGraph.IntervalCache;\r
+import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;\r
+\r
+public class RouteTerminal extends RoutePoint implements RouteNode, Serializable {\r
+\r
+    private static final long serialVersionUID = -8839093950347737029L;\r
+\r
+    public static final int   DIR_RIGHT        = (1 << 0);\r
+    public static final int   DIR_DOWN         = (1 << 1);\r
+    public static final int   DIR_LEFT         = (1 << 2);\r
+    public static final int   DIR_UP           = (1 << 3);\r
+    public static final int   DIR_DIRECT       = (1 << 4);\r
+\r
+    private Object data;\r
+    private double minX, minY;\r
+    private double maxX, maxY;\r
+    private int allowedDirections;\r
+    private ILineEndStyle style;\r
+    private ILineEndStyle dynamicStyle;\r
+    private boolean routeToBounds;\r
+\r
+    RouteLine line;\r
+\r
+    RouteTerminal(double x, double y, double minX, double minY,\r
+            double maxX, double maxY, int allowedDirections,\r
+            boolean routeToBounds,\r
+            ILineEndStyle style) {\r
+        super(x, y);\r
+        this.minX = minX;\r
+        this.minY = minY;\r
+        this.maxX = maxX;\r
+        this.maxY = maxY;\r
+        this.allowedDirections = allowedDirections;\r
+        this.routeToBounds = routeToBounds;\r
+        this.style = style;\r
+    }\r
+    \r
+    @Override\r
+    public void setData(Object data) {\r
+        this.data = data;\r
+    }\r
+    \r
+    @Override\r
+    public Object getData() {\r
+        return data;\r
+    }\r
+    \r
+    public int getAllowedDirections() {\r
+               return allowedDirections;\r
+       }\r
+    \r
+    public double getMinX() {\r
+               return minX;\r
+       }\r
+    \r
+    public double getMinY() {\r
+               return minY;\r
+       }\r
+    \r
+    public double getMaxX() {\r
+               return maxX;\r
+       }\r
+    \r
+    public double getMaxY() {\r
+               return maxY;\r
+       }\r
+    \r
+    public Rectangle2D getBounds() {\r
+       return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);\r
+    }\r
+\r
+    /**\r
+     * Routes connection from the terminal to route line\r
+     * adding necessary transient route lines.\r
+     * @param cache \r
+     */\r
+    void route(ArrayList<RouteLine> lines, IntervalCache cache, boolean boundingBoxesIntersect) {\r
+        if(routeToBounds) {\r
+            int lineDir;\r
+            boolean routeLineDoesNotIntersectTerminal;\r
+            double linePosition = line.position;\r
+            if(line.isHorizontal) { \r
+                lineDir = linePosition < y ? 3 : 1;\r
+                routeLineDoesNotIntersectTerminal = \r
+                        linePosition <= minY || linePosition >= maxY;\r
+            }\r
+            else {\r
+                lineDir = linePosition < x ? 2 : 0;\r
+                routeLineDoesNotIntersectTerminal = \r
+                        linePosition <= minX || linePosition >= maxX;\r
+            }\r
+            \r
+            if(routeLineDoesNotIntersectTerminal) {\r
+                RouteLine line0 = createLine0(lineDir);\r
+                new RouteLink(line0, line);\r
+                lines.add(line0);\r
+                switch(lineDir) {\r
+                case 0:\r
+                    x = maxX;\r
+                    y = 0.5*(minY+maxY);\r
+                    break;\r
+                case 1:\r
+                    x = 0.5*(minX+maxX);\r
+                    y = maxY;\r
+                    break;\r
+                case 2:\r
+                    x = minX;\r
+                    y = 0.5*(minY+maxY);\r
+                    break;\r
+                case 3:\r
+                    x = 0.5*(minX+maxX);\r
+                    y = minY;\r
+                    break;\r
+                }\r
+                return;\r
+            }\r
+            else {\r
+                line.addPoint(this);\r
+                Interval interval = cache.get(line);\r
+                if(line.isHorizontal) {\r
+                    if(interval.min < minX)\r
+                        x = minX;\r
+                    else\r
+                        x = maxX;\r
+                    y = linePosition;\r
+                }\r
+                else {\r
+                    x = linePosition;\r
+                    if(interval.min < minY)\r
+                        y = minY;\r
+                    else\r
+                        y = maxY;\r
+                }\r
+            }\r
+        }\r
+        else {\r
+            // In which direction the route line is?\r
+            int lineDir;\r
+            boolean routeLineDoesNotIntersectTerminal;\r
+            double linePosition = line.position;\r
+            if(line.isHorizontal) { \r
+                lineDir = linePosition < y ? 3 : 1;\r
+                routeLineDoesNotIntersectTerminal = linePosition <= minY || linePosition >= maxY \r
+                        || boundingBoxesIntersect /* we ignore intersection in this case */;\r
+            }\r
+            else {\r
+                lineDir = linePosition < x ? 2 : 0;\r
+                routeLineDoesNotIntersectTerminal = linePosition <= minX || linePosition >= maxX\r
+                        || boundingBoxesIntersect /* we ignore intersection in this case */;\r
+            }\r
+                    \r
+            // We can route the connection directly to the right direction\r
+            if(routeLineDoesNotIntersectTerminal && \r
+                    Directions.isAllowed(allowedDirections, lineDir)) {           \r
+                RouteLine line0 = createLine0(lineDir);\r
+                new RouteLink(line0, line);\r
+                lines.add(line0);\r
+                return;\r
+            }\r
+            \r
+            // We must make one bend\r
+            oneBend: {\r
+                int dir = 1-(lineDir&1);\r
+                if(Directions.isAllowed(allowedDirections, dir)) {\r
+                    if(Directions.isAllowed(allowedDirections, dir+2)) {\r
+                        Interval interval = cache.get(line);\r
+                        if(dir == 0) {\r
+                            if(interval.max <= maxX)\r
+                                dir = 2;\r
+                        }\r
+                        else /* dir == 1 */ {\r
+                            if(interval.max <= maxY)\r
+                                dir = 3;\r
+                        }\r
+                    }\r
+                    else {\r
+                        // ok\r
+                    }\r
+                }\r
+                else {\r
+                    if(Directions.isAllowed(allowedDirections, dir+2)) {\r
+                        dir = dir + 2;\r
+                    }\r
+                    else {\r
+                        break oneBend;\r
+                    }\r
+                }\r
+    \r
+                RouteLine line0 = createLine0(dir);\r
+                RouteLine line1 = createLine1(dir);\r
+                new RouteLink(line0, line1);\r
+                new RouteLink(line1, line);\r
+                lines.add(line0);\r
+                lines.add(line1);\r
+                line0.nextTransient = line1;\r
+                return;\r
+            }\r
+            \r
+            // We can begin to the right direction but do two bends\r
+            if(!routeLineDoesNotIntersectTerminal && \r
+                    Directions.isAllowed(allowedDirections, lineDir)) {  \r
+                RouteLine line0 = createLine0(lineDir);\r
+                RouteLine line1 = createLine1(lineDir);\r
+                RouteLine line2 = createLine2(lineDir, cache);\r
+                new RouteLink(line0, line1);\r
+                new RouteLink(line1, line2);\r
+                new RouteLink(line2, line);\r
+                lines.add(line0);\r
+                lines.add(line1);\r
+                lines.add(line2);\r
+                line0.nextTransient = line1;\r
+                line1.nextTransient = line2;\r
+                return;\r
+            }\r
+            \r
+            // Only allowed direction is to completely wrong direction:\r
+            // we must make two bends\r
+            {\r
+                int dir = lineDir^2;\r
+                RouteLine line0 = createLine0(dir);\r
+                RouteLine line1 = createLine1(dir);\r
+                RouteLine line2 = createLine2(dir, cache);\r
+                new RouteLink(line0, line1);\r
+                new RouteLink(line1, line2);\r
+                new RouteLink(line2, line);\r
+                lines.add(line0);\r
+                lines.add(line1);\r
+                lines.add(line2);\r
+                line0.nextTransient = line1;\r
+                line1.nextTransient = line2;\r
+                return;\r
+            }\r
+        }\r
+    }\r
+    \r
+    private RouteLine createLine0(int dir) {\r
+        RouteLine line0 = (dir&1) == 0 \r
+                ? new RouteLine(true, y)\r
+                : new RouteLine(false, x)\r
+                ;\r
+        line0.addPoint(this);\r
+        line0.terminal = this;\r
+        return line0;\r
+    }\r
+    \r
+    private RouteLine createLine1(int dir) {\r
+        RouteLine line1 = (dir&1) == 0 \r
+                ? new RouteLine(false, (dir&2) == 0 ? maxX : minX)\r
+                : new RouteLine(true, (dir&2) == 0 ? maxY : minY)\r
+                ;\r
+        line1.terminal = this;\r
+        return line1;\r
+    }\r
+    \r
+    private RouteLine createLine2(int dir, IntervalCache cache) {\r
+        Interval interval = cache.get(line);\r
+        RouteLine line2;\r
+        if((dir&1) == 0) {\r
+            double position;\r
+            if(minY < interval.min) {\r
+                if(maxY > interval.max) {\r
+                    position = 2*maxY-y-interval.max < interval.min+y-2*minY ? maxY : minY;\r
+                }\r
+                else {\r
+                    position = maxY;\r
+                }\r
+            }\r
+            else {\r
+                if(maxY > interval.max) {\r
+                    position = minY;\r
+                }\r
+                else {\r
+                    position = maxY-y < y-minY ? maxY : minY;\r
+                }\r
+            }\r
+            line2 = new RouteLine(true, position);\r
+        }\r
+        else {\r
+            double position;\r
+            if(minX < interval.min) {\r
+                if(maxX > interval.max) {\r
+                    position = 2*maxX-x-interval.max < interval.min+x-2*minX ? maxX : minX;\r
+                }\r
+                else {\r
+                    position = maxX;\r
+                }\r
+            }\r
+            else {\r
+                if(maxX > interval.max) {\r
+                    position = minX;\r
+                }\r
+                else {\r
+                    position = maxX-x < x-minX ? maxX : minX;\r
+                }\r
+            }\r
+            line2 = new RouteLine(false, position);\r
+        }\r
+        line2.terminal = this;                \r
+        return line2;\r
+    }    \r
+\r
+    public boolean isNear(double x2, double y2) {\r
+        return minX <= x2 && x2 <= maxX && minY <= y2 && y2 <= maxY;\r
+    }\r
+\r
+    void setLocation(double x2, double y2) {\r
+        double dx = x2 - x;\r
+        double dy = y2 - y;\r
+        x = x2;\r
+        y = y2;\r
+        minX += dx;\r
+        minY += dy;\r
+        maxX += dx;\r
+        maxY += dy;\r
+    }\r
+\r
+    void rotate(int amount) {\r
+        amount %= 4;\r
+        if(amount < 0)\r
+            amount += 4;\r
+        \r
+        int temp = (allowedDirections&15) << amount;\r
+        allowedDirections = (temp&15) | (temp >> 4) | (allowedDirections&16);       \r
+    }\r
+\r
+    public double approximatePositionToLine() {\r
+        // In which direction the route line is?\r
+        int lineDir = line.isHorizontal \r
+                ? (line.position < y ? 3 : 1)\r
+                : (line.position < x ? 2 : 0)\r
+                ;\r
+                \r
+        // We can route the connection directly to the right direction\r
+        if(Directions.isAllowed(allowedDirections, lineDir))\r
+            return line.isHorizontal ? x : y;\r
+        \r
+        // We must make one bend \r
+        for(int dir = 0;dir < 4;++dir) {\r
+            if(Directions.isAllowed(allowedDirections, dir) && ((dir^lineDir)&1) == 1) {\r
+                switch(dir) {\r
+                case 0: return maxX;\r
+                case 1: return maxY;\r
+                case 2: return minX;\r
+                case 3: return minY;\r
+                }\r
+            }\r
+        }\r
+        // Only allowed direction is to completely wrong direction:\r
+        // we must make two bends\r
+        {\r
+            // Approximation\r
+            return line.isHorizontal ? x : y;\r
+        }\r
+    }\r
+\r
+    RouteTerminal copy(THashMap<Object, Object> map) {\r
+       RouteTerminal copy = (RouteTerminal)map.get(this);\r
+       if(copy == null) {      \r
+               copy = new RouteTerminal(x,  y, minX, minY, maxX, maxY, \r
+                               allowedDirections, routeToBounds, style);\r
+               copy.setDynamicStyle(dynamicStyle);\r
+               map.put(this, copy);\r
+               copy.data = data;\r
+               copy.line = line == null ? null : line.copy(map);\r
+       }\r
+        return copy;\r
+    }\r
+\r
+    public void print(PrintStream out) {\r
+        out.print("     (" + x + "," + y + ") " + allowedDirections + " -> ");\r
+        if (line != null)\r
+            line.print(out);\r
+        else\r
+            out.print("NO LINE");\r
+        out.print(" (data=" + data + ")");\r
+        out.println();\r
+    }\r
+\r
+    public ILineEndStyle getStyle() {\r
+        return style;\r
+    }\r
+    \r
+    public ILineEndStyle getRenderStyle() {\r
+       if (dynamicStyle != null)\r
+               return dynamicStyle;\r
+        return style;\r
+    }\r
+\r
+    public boolean hasDirectConnection() {\r
+        return (allowedDirections&16) == 16;\r
+    }\r
+    \r
+    public RouteLine getLine() {\r
+        return line;\r
+    }\r
+    \r
+    public void setMinX(double minX) {\r
+               this.minX = minX;\r
+       }\r
+    public void setMinY(double minY) {\r
+               this.minY = minY;\r
+       }\r
+    \r
+    public void setMaxX(double maxX) {\r
+               this.maxX = maxX;\r
+       }\r
+    \r
+    public void setMaxY(double maxY) {\r
+               this.maxY = maxY;\r
+       }\r
+\r
+    public void toggleDirectLines() {\r
+        this.allowedDirections ^= 16;\r
+    }\r
+    \r
+    public boolean isRouteToBounds() {\r
+        return routeToBounds;\r
+    }\r
+    \r
+    public void setStyle(ILineEndStyle style) {\r
+               this.style = style;\r
+       }\r
+    \r
+    public ILineEndStyle getDynamicStyle() {\r
+               return dynamicStyle;\r
+       }\r
+    \r
+    public void setDynamicStyle(ILineEndStyle dynamicStyle) {\r
+               this.dynamicStyle = dynamicStyle;\r
+       }\r
+}\r