]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / connection / HighlightActionPointsAction.java
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java
new file mode 100644 (file)
index 0000000..04d08e3
--- /dev/null
@@ -0,0 +1,313 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 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.scenegraph.g2d.nodes.connection;\r
+\r
+import java.awt.AlphaComposite;\r
+import java.awt.BasicStroke;\r
+import java.awt.Composite;\r
+import java.awt.Graphics2D;\r
+import java.awt.Stroke;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.image.BufferedImage;\r
+import java.io.IOException;\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+\r
+import javax.imageio.ImageIO;\r
+\r
+import org.simantics.diagram.connection.RouteGraph;\r
+import org.simantics.diagram.connection.RouteLineHalf;\r
+import org.simantics.diagram.connection.actions.IAction;\r
+import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class HighlightActionPointsAction implements IAction {\r
+\r
+    public static enum Action {\r
+        REMOVE,\r
+        RECONNECT\r
+    }\r
+\r
+    public static class Pick {\r
+        public static final Pick MISS = new Pick(null, null);\r
+\r
+        Action                   action;\r
+        RouteLineHalf            line;\r
+\r
+        public Pick(Action action, RouteLineHalf line) {\r
+            this.action = action;\r
+            this.line = line;\r
+        }\r
+\r
+        public boolean hasAction(Action action) {\r
+            return this.action == action;\r
+        }\r
+\r
+        public boolean hasResult() {\r
+            return action != null && line != null;\r
+        }\r
+\r
+        public boolean matches(Action action, RouteLineHalf line) {\r
+            if (this.action == null || this.line == null)\r
+                return false;\r
+            return this.action.equals(action) && this.line.equals(line);\r
+        }\r
+    }\r
+\r
+    static BufferedImage                cross;\r
+    static BufferedImage                cut;\r
+\r
+    static {\r
+        cross = safeReadImage("cross.png");\r
+        cut = safeReadImage("cut.png");\r
+    }\r
+\r
+    public static final Stroke          STROKE                  = new BasicStroke(0.1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);\r
+    public static final AlphaComposite  COMPOSITE               = AlphaComposite.SrcOver.derive(0.6f);\r
+\r
+    public static final double          DEGENERATED_LINE_LENGTH = 1;\r
+    public static final double          CUT_DIST_FROM_END       = 0.5;\r
+\r
+    RouteGraph                          rg;\r
+\r
+    transient Collection<RouteLineHalf> lhs                     = new ArrayList<RouteLineHalf>();\r
+    transient AffineTransform           transform               = new AffineTransform();\r
+    transient AffineTransform           transform2              = new AffineTransform();\r
+    transient Rectangle2D               rect                    = new Rectangle2D.Double();\r
+\r
+    public HighlightActionPointsAction(RouteGraph rg) {\r
+        this.rg = rg;\r
+    }\r
+\r
+    public void setRouteGraph(RouteGraph rg) {\r
+        this.rg = rg;\r
+    }\r
+\r
+    @Override\r
+    public void render(Graphics2D g, IRouteGraphRenderer renderer, double mouseX, double mouseY) {\r
+        // Cannot perform cut or delete segment actions on connections between 2\r
+        // terminals.\r
+        boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2);\r
+        boolean branchedConnection = rg.getTerminals().size() > 2;\r
+\r
+        lhs.clear();\r
+        rg.getLineHalves(lhs);\r
+\r
+        AffineTransform preTr = g.getTransform();\r
+        double viewScale = 1.0 / getScale(preTr);\r
+        transform2.setToScale(viewScale, viewScale);\r
+        Composite originalComposite = g.getComposite();\r
+        g.setComposite(COMPOSITE);\r
+\r
+        Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY);\r
+\r
+        if (!simpleConnection) {\r
+            double crossW = cross.getWidth()*viewScale*.5;\r
+            double crossH = cross.getHeight()*viewScale*.5;\r
+\r
+            // Render line removal markers\r
+            for (RouteLineHalf lh : lhs) {\r
+//                if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH)\r
+//                    continue;\r
+//                if (!lh.getLine().isTransient())\r
+//                    continue;\r
+                if (lh.getLine().getTerminal() == null)\r
+                    continue;\r
+                double x = lh.getLink().getX();\r
+                double y = lh.getLink().getY();\r
+                if (lh.getLine().isHorizontal()) {\r
+                    x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5;\r
+                } else {\r
+                    y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5;\r
+                }\r
+\r
+                boolean hit = pick.matches(Action.REMOVE, lh);\r
+\r
+                if (hit)\r
+                    g.setComposite(originalComposite);\r
+                transform.setToTranslation(x-crossW, y-crossH);\r
+                g.transform(transform);\r
+                g.drawImage(cross, transform2, null);\r
+                g.setTransform(preTr);\r
+                if (hit)\r
+                    g.setComposite(COMPOSITE);\r
+            }\r
+        }\r
+\r
+        // Render reconnection markers if the connection is branched.\r
+        if (branchedConnection) {\r
+            double cutW = cut.getWidth()*viewScale*.5;\r
+            double cutH = cut.getHeight()*viewScale*.5;\r
+\r
+            final double dist = CUT_DIST_FROM_END;\r
+            for (RouteLineHalf lh : lhs) {\r
+                if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3)\r
+                    continue;\r
+                double x = lh.getLink().getX();\r
+                double y = lh.getLink().getY();\r
+                if (lh.getLine().isHorizontal()) {\r
+                    if (lh.getLink() == lh.getLine().getBegin())\r
+                        x += dist*2;\r
+                    else\r
+                        x -= dist*2;\r
+                } else {\r
+                    if (lh.getLink() == lh.getLine().getBegin())\r
+                        y += dist*2;\r
+                    else\r
+                        y -= dist*2;\r
+                }\r
+\r
+                boolean hit = pick.matches(Action.RECONNECT, lh);\r
+\r
+                if (hit)\r
+                    g.setComposite(originalComposite);\r
+                transform.setToTranslation(x-cutW, y-cutH);\r
+                if (!lh.getLine().isHorizontal()) {\r
+                    transform.rotate(Math.PI/2, cutW, cutH);\r
+                }\r
+                g.transform(transform);\r
+                g.drawImage(cut, transform2, null);\r
+                g.setTransform(preTr);\r
+                if (hit)\r
+                    g.setComposite(COMPOSITE);\r
+            }\r
+        }\r
+\r
+        g.setComposite(originalComposite);\r
+    }\r
+\r
+    public Pick pickAction(RouteGraph rg, AffineTransform viewTr, double mouseX, double mouseY) {\r
+        boolean branchedConnection = rg.getTerminals().size() > 2;\r
+        boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2);\r
+        if (!branchedConnection || simpleConnection)\r
+            return null;\r
+\r
+        lhs.clear();\r
+        return pickAction(rg, rg.getLineHalves(lhs), viewTr, mouseX, mouseY);\r
+    }\r
+\r
+    public Pick pickAction(RouteGraph rg, Collection<RouteLineHalf> lhs, AffineTransform viewTr, double mouseX, double mouseY) {\r
+        boolean branchedConnection = rg.getTerminals().size() > 2;\r
+        boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2);\r
+        if (!branchedConnection || simpleConnection || viewTr == null)\r
+            return Pick.MISS;\r
+\r
+        lhs.clear();\r
+        rg.getLineHalves(lhs);\r
+\r
+        RouteLineHalf selected = null;\r
+        Action selectedAction = null;\r
+        double nearest = Double.MAX_VALUE;\r
+        double viewScale = 1.0 / getScale(viewTr);\r
+\r
+        if (!simpleConnection) {\r
+            double crossW = cross.getWidth()*viewScale*.5;\r
+            double crossH = cross.getHeight()*viewScale*.5;\r
+\r
+            // Render line removal markers\r
+            for (RouteLineHalf lh : lhs) {\r
+//                if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH)\r
+//                    continue;\r
+//                if (!lh.getLine().isTransient())\r
+//                    continue;\r
+                if (lh.getLine().getTerminal() == null)\r
+                    continue;\r
+                double x = lh.getLink().getX();\r
+                double y = lh.getLink().getY();\r
+                if (lh.getLine().isHorizontal()) {\r
+                    x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5;\r
+                } else {\r
+                    y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5;\r
+                }\r
+\r
+                rect.setFrameFromCenter(x, y, x-crossW, y-crossH);\r
+                boolean hit = rect.contains(mouseX, mouseY);\r
+                if (hit) {\r
+                    double distSq = distSq(x, y, mouseX, mouseY);\r
+                    if (distSq < nearest) {\r
+                        nearest = distSq;\r
+                        selected = lh;\r
+                        selectedAction = Action.REMOVE;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        // Render reconnection markers if the connection is branched.\r
+        if (branchedConnection) {\r
+            double cutW = cut.getWidth()*viewScale*.5;\r
+            double cutH = cut.getHeight()*viewScale*.5;\r
+\r
+            final double dist = CUT_DIST_FROM_END;\r
+            for (RouteLineHalf lh : lhs) {\r
+                if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3)\r
+                    continue;\r
+                double x = lh.getLink().getX();\r
+                double y = lh.getLink().getY();\r
+                if (lh.getLine().isHorizontal()) {\r
+                    if (lh.getLink() == lh.getLine().getBegin())\r
+                        x += dist*2;\r
+                    else\r
+                        x -= dist*2;\r
+                } else {\r
+                    if (lh.getLink() == lh.getLine().getBegin())\r
+                        y += dist*2;\r
+                    else\r
+                        y -= dist*2;\r
+                }\r
+\r
+                rect.setFrameFromCenter(x, y, x-cutW, y-cutH);\r
+                boolean hit = rect.contains(mouseX, mouseY);\r
+                if (hit) {\r
+                    double distSq = distSq(x, y, mouseX, mouseY);\r
+                    if (distSq < nearest) {\r
+                        nearest = dist;\r
+                        selected = lh;\r
+                        selectedAction = Action.RECONNECT;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        return selected == null ? Pick.MISS : new Pick(selectedAction, selected);\r
+    }\r
+\r
+    private static double distSq(double x1, double y1, double x2, double y2) {\r
+        double dx = x2 - x1;\r
+        double dy = y2 - y1;\r
+        return dx * dx + dy * dy;\r
+    }\r
+\r
+    private static double getScale(AffineTransform at)\r
+    {\r
+        double m00 = at.getScaleX();\r
+        double m11 = at.getScaleY();\r
+        double m10 = at.getShearY();\r
+        double m01 = at.getShearX();\r
+\r
+        return Math.sqrt(Math.abs(m00*m11 - m10*m01));\r
+    }\r
+\r
+    private static BufferedImage safeReadImage(String name) {\r
+        try {\r
+            URL url = HighlightActionPointsAction.class.getResource(name);\r
+            return ImageIO.read(url);\r
+        } catch (IOException e) {\r
+            return null;\r
+        }\r
+    }\r
+\r
+}\r