]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Support for dynamic transforms for both elements and terminals 61/561/5
authorAntti Villberg <antti.villberg@semantum.fi>
Sat, 27 May 2017 06:29:23 +0000 (09:29 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Mon, 29 May 2017 10:12:00 +0000 (13:12 +0300)
Includes also minor code warning/logging cleanup.

refs #7119

Change-Id: I2df8f0f0707e8adf1569679ab41a74f605ae1268

22 files changed:
bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteGraph.java
bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminal.java
bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminalPosition.java [new file with mode: 0644]
bundles/org.simantics.diagram.ontology/graph/Diagram.pgraph
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/DefinedElementTerminals.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ElementFactoryUtil.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/NodeRequest.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphConnectionClassFactory.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/SVGElementClassFactory.java
bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java
bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/TerminalUtil.java
bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/TerminalLayout.java
bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/impl/Terminals.java
bundles/org.simantics.modeling/scl/Simantics/Scenegraph.scl
bundles/org.simantics.modeling/src/org/simantics/modeling/adapters/SymbolCodeStyle.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/AffineTransformFunctions.java [new file with mode: 0644]
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DNodeModification.java [new file with mode: 0644]
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNode.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNodeAssignment.java [new file with mode: 0644]
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/TransformationAssignment.java [new file with mode: 0644]
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java

index 1905cfe2b2c172969e2883a5659a0d807a106761..feaefeb69d15a251e46f8b2a7eb31dfd4460206b 100644 (file)
@@ -45,6 +45,13 @@ public class RouteGraph implements Serializable {
     boolean isSimpleConnection;
     boolean needsUpdate = false;
 
     boolean isSimpleConnection;
     boolean needsUpdate = false;
 
+    public void updateTerminals() {
+        boolean changed = false;
+        for(RouteTerminal terminal : terminals)
+            changed |= terminal.updateDynamicPosition();
+        if(changed) update();
+    }
+
     /**
      * Adds a route line to the graph.
      * @param isHorizontal true, if the line is horizontal
     /**
      * Adds a route line to the graph.
      * @param isHorizontal true, if the line is horizontal
@@ -78,21 +85,31 @@ public class RouteGraph implements Serializable {
      * </pre>
      * @param style Tells what kind of line end is drawn 
      *        to the connection.
      * </pre>
      * @param style Tells what kind of line end is drawn 
      *        to the connection.
+     * @param position a provider for a dynamic position for the terminal or
+     *        <code>null</code> if terminal position is not dynamic
      * @return The new terminal.
      */
      * @return The new terminal.
      */
+    public RouteTerminal addTerminal(double x, double y, 
+            double minX, double minY,
+            double maxX, double maxY, 
+            int allowedDirections,
+            ILineEndStyle style, RouteTerminalPosition position) {
+        return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, style, null, position);
+    }
+
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
             ILineEndStyle style) {
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
             ILineEndStyle style) {
-       return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, style, null);
-       
+        return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, style, null, null);
     }
     }
+
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
-            ILineEndStyle style, ILineEndStyle dynamicStyle) {
+            ILineEndStyle style, ILineEndStyle dynamicStyle, RouteTerminalPosition position) {
         if(CHECK_PARAMERS) {
             if(allowedDirections > 0x1f)
                 throw new IllegalArgumentException("Illegal allowedDirection flags.");
         if(CHECK_PARAMERS) {
             if(allowedDirections > 0x1f)
                 throw new IllegalArgumentException("Illegal allowedDirection flags.");
@@ -102,7 +119,7 @@ public class RouteGraph implements Serializable {
         if(style == null)
             style = PlainLineEndStyle.INSTANCE;
         RouteTerminal terminal = new RouteTerminal(x, y, minX, minY,
         if(style == null)
             style = PlainLineEndStyle.INSTANCE;
         RouteTerminal terminal = new RouteTerminal(x, y, minX, minY,
-                maxX, maxY, allowedDirections, false, style); 
+                maxX, maxY, allowedDirections, false, style, position); 
         terminal.setDynamicStyle(dynamicStyle);
         terminals.add(terminal);
         return terminal;
         terminal.setDynamicStyle(dynamicStyle);
         terminals.add(terminal);
         return terminal;
@@ -124,7 +141,7 @@ public class RouteGraph implements Serializable {
                 0.5*(minX+maxX), 0.5*(minY+maxY),
                 minX, minY,
                 maxX, maxY, 
                 0.5*(minX+maxX), 0.5*(minY+maxY),
                 minX, minY,
                 maxX, maxY, 
-                0xf, true, style); 
+                0xf, true, style, null); 
         terminal.setDynamicStyle(dynamicStyle);
         
         terminals.add(terminal);
         terminal.setDynamicStyle(dynamicStyle);
         
         terminals.add(terminal);
@@ -142,7 +159,7 @@ public class RouteGraph implements Serializable {
             ILineEndStyle style) {
         return addTerminal(x, y, 
                 bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY(), 
             ILineEndStyle style) {
         return addTerminal(x, y, 
                 bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY(), 
-                allowedDirections, style);
+                allowedDirections, style, null);
     }
 
     /**
     }
 
     /**
@@ -152,7 +169,7 @@ public class RouteGraph implements Serializable {
         RouteTerminal newTerminal = addTerminal(terminal.x, terminal.y,
                 terminal.getMinX(), terminal.getMinY(),
                 terminal.getMaxX(), terminal.getMaxY(),
         RouteTerminal newTerminal = addTerminal(terminal.x, terminal.y,
                 terminal.getMinX(), terminal.getMinY(),
                 terminal.getMaxX(), terminal.getMaxY(),
-                terminal.getAllowedDirections(), terminal.getStyle(), terminal.getDynamicStyle());
+                terminal.getAllowedDirections(), terminal.getStyle(), terminal.getDynamicStyle(), terminal.getDynamicPosition());
         newTerminal.setData(terminal.getData());
         return terminal;
     }
         newTerminal.setData(terminal.getData());
         return terminal;
     }
@@ -166,7 +183,7 @@ public class RouteGraph implements Serializable {
             double maxX, double maxY, 
             int allowedDirections) {
         return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, 
             double maxX, double maxY, 
             int allowedDirections) {
         return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, 
-                PlainLineEndStyle.INSTANCE);
+                PlainLineEndStyle.INSTANCE, null);
     }
     
     /**
     }
     
     /**
index 2f3fc9f86ec7ce7668c0dc12e87f741a4ae4c74e..c43c7de1c4996914d8ac6bbc5c208e4c9b2c64ff 100644 (file)
@@ -13,6 +13,7 @@ package org.simantics.diagram.connection;
 
 import gnu.trove.map.hash.THashMap;
 
 
 import gnu.trove.map.hash.THashMap;
 
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.io.PrintStream;
 import java.io.Serializable;
 import java.awt.geom.Rectangle2D;
 import java.io.PrintStream;
 import java.io.Serializable;
@@ -39,13 +40,14 @@ public class RouteTerminal extends RoutePoint implements RouteNode, Serializable
     private ILineEndStyle style;
     private ILineEndStyle dynamicStyle;
     private boolean routeToBounds;
     private ILineEndStyle style;
     private ILineEndStyle dynamicStyle;
     private boolean routeToBounds;
+    private RouteTerminalPosition dynamicPosition;
 
     RouteLine line;
 
     RouteTerminal(double x, double y, double minX, double minY,
             double maxX, double maxY, int allowedDirections,
             boolean routeToBounds,
 
     RouteLine line;
 
     RouteTerminal(double x, double y, double minX, double minY,
             double maxX, double maxY, int allowedDirections,
             boolean routeToBounds,
-            ILineEndStyle style) {
+            ILineEndStyle style, RouteTerminalPosition dynamicPosition) {
         super(x, y);
         this.minX = minX;
         this.minY = minY;
         super(x, y);
         this.minX = minX;
         this.minY = minY;
@@ -54,8 +56,9 @@ public class RouteTerminal extends RoutePoint implements RouteNode, Serializable
         this.allowedDirections = allowedDirections;
         this.routeToBounds = routeToBounds;
         this.style = style;
         this.allowedDirections = allowedDirections;
         this.routeToBounds = routeToBounds;
         this.style = style;
+        this.dynamicPosition = dynamicPosition;
     }
     }
-    
+
     @Override
     public void setData(Object data) {
         this.data = data;
     @Override
     public void setData(Object data) {
         this.data = data;
@@ -378,7 +381,7 @@ public class RouteTerminal extends RoutePoint implements RouteNode, Serializable
        RouteTerminal copy = (RouteTerminal)map.get(this);
        if(copy == null) {      
                copy = new RouteTerminal(x,  y, minX, minY, maxX, maxY, 
        RouteTerminal copy = (RouteTerminal)map.get(this);
        if(copy == null) {      
                copy = new RouteTerminal(x,  y, minX, minY, maxX, maxY, 
-                               allowedDirections, routeToBounds, style);
+                               allowedDirections, routeToBounds, style, dynamicPosition);
                copy.setDynamicStyle(dynamicStyle);
                map.put(this, copy);
                copy.data = data;
                copy.setDynamicStyle(dynamicStyle);
                map.put(this, copy);
                copy.data = data;
@@ -449,4 +452,25 @@ public class RouteTerminal extends RoutePoint implements RouteNode, Serializable
     public void setDynamicStyle(ILineEndStyle dynamicStyle) {
                this.dynamicStyle = dynamicStyle;
        }
     public void setDynamicStyle(ILineEndStyle dynamicStyle) {
                this.dynamicStyle = dynamicStyle;
        }
+
+    public RouteTerminalPosition getDynamicPosition() {
+        return dynamicPosition;
+    }
+
+    public boolean updateDynamicPosition() {
+        boolean changed = false;
+        if (dynamicPosition != null) {
+            AffineTransform tr = dynamicPosition.getTransform();
+            if (tr != null) {
+                double nx = tr.getTranslateX();
+                changed |= x != nx;
+                x = nx;
+                double ny = tr.getTranslateY();
+                changed |= y != ny;
+                y = ny;
+            }
+        }
+        return changed;
+    }
+
 }
 }
diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminalPosition.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminalPosition.java
new file mode 100644 (file)
index 0000000..13ca043
--- /dev/null
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.diagram.connection;
+
+import java.awt.geom.AffineTransform;
+
+/**
+ * @author Antti Villberg
+ * @since 1.29.0
+ */
+public interface RouteTerminalPosition {
+
+       public AffineTransform getTransform();
+
+}
index 9e006381dbad4f8eb2ecffa5e41b34d2fd6781b2..7b7bb6b2e2d7eca5b83bcc764733423b8c385e4e 100644 (file)
@@ -113,7 +113,9 @@ DIA.Functions.activeProfileModifier : L0.Function
 DIA.Functions.diagramElementIssuePath : L0.Function
 
 DIA.DefinedElement
 DIA.Functions.diagramElementIssuePath : L0.Function
 
 DIA.DefinedElement
-  >-- DIA.symbolCode ==> "[String]" <R L0.HasProperty : L0.FunctionalRelation
+  // RequiresValueType ]is omitted during transition period.
+  // Used to be ==> "[String]" but DIA.symbolCode now returns either "[String]" or "[G2DNodeModification]" directly
+  >-- DIA.symbolCode <R L0.HasProperty : L0.FunctionalRelation
   >-- DIA.symbolDropHandler ==> "[WorkbenchSelectionElement] -> <WriteGraph,Proc> ()" <R L0.HasProperty : L0.FunctionalRelation
 
 DIA.DiagramActivityCondition <T L0.Entity
   >-- DIA.symbolDropHandler ==> "[WorkbenchSelectionElement] -> <WriteGraph,Proc> ()" <R L0.HasProperty : L0.FunctionalRelation
 
 DIA.DiagramActivityCondition <T L0.Entity
index 6f83954e01b6163e10d1c748cbf57421b1159a82..d67f9f547f9c5a27a2ad552849f5842e0746bdd8 100644 (file)
@@ -21,7 +21,6 @@ import org.simantics.g2d.element.handler.TerminalLayout;
 import org.simantics.g2d.element.handler.TerminalTopology;
 import org.simantics.g2d.element.handler.impl.ObjectTerminal;
 import org.simantics.g2d.element.handler.impl.Terminals;
 import org.simantics.g2d.element.handler.TerminalTopology;
 import org.simantics.g2d.element.handler.impl.ObjectTerminal;
 import org.simantics.g2d.element.handler.impl.Terminals;
-import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
 import org.simantics.scenegraph.utils.NodeUtil;
 import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
 import org.simantics.scenegraph.utils.NodeUtil;
@@ -72,7 +71,7 @@ public class DefinedElementTerminals extends Terminals {
         ObjectTerminal ti = terminalMap.get(t);
         if (ti == null)
             return null;
         ObjectTerminal ti = terminalMap.get(t);
         if (ti == null)
             return null;
-        return new AffineTransform(ti.getTransform());
+        return ti.getTransform();
     }
 
 }
\ No newline at end of file
     }
 
 }
\ No newline at end of file
index f747de0b0ee5f8bf740a2c84e64637381cc36956..89ee666bb21b92e75519a3df2724a674dad938a9 100644 (file)
@@ -13,12 +13,13 @@ package org.simantics.diagram.adapter;
 
 import java.awt.geom.AffineTransform;
 
 
 import java.awt.geom.AffineTransform;
 
-import org.simantics.databoard.Bindings;
 import org.simantics.db.AsyncReadGraph;
 import org.simantics.db.AsyncReadGraph;
+import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
 import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter;
 import org.simantics.db.Resource;
 import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter;
+import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.procedure.AsyncProcedure;
 import org.simantics.db.procedure.AsyncProcedure;
-import org.simantics.diagram.stubs.G2DResource;
+import org.simantics.diagram.stubs.DiagramResource;
 import org.simantics.diagram.synchronization.ErrorHandler;
 import org.simantics.diagram.synchronization.ISynchronizationContext;
 import org.simantics.diagram.synchronization.SynchronizationHints;
 import org.simantics.diagram.synchronization.ErrorHandler;
 import org.simantics.diagram.synchronization.ISynchronizationContext;
 import org.simantics.diagram.synchronization.SynchronizationHints;
@@ -31,35 +32,31 @@ import org.simantics.g2d.element.ElementUtils;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.layers.ILayersEditor;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.layers.ILayersEditor;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ElementFactoryUtil {
 
 
 public class ElementFactoryUtil {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(ElementFactoryUtil.class);
+
     public static void readTransform(AsyncReadGraph graph, final Resource resource, final IElement e) {
         readTransform(graph, resource, e, new AsyncProcedureAdapter<IElement>() {
             @Override
             public void exception(AsyncReadGraph graph, Throwable t) {
     public static void readTransform(AsyncReadGraph graph, final Resource resource, final IElement e) {
         readTransform(graph, resource, e, new AsyncProcedureAdapter<IElement>() {
             @Override
             public void exception(AsyncReadGraph graph, Throwable t) {
-                // Nowhere to log properly.
-                t.printStackTrace();
+                LOGGER.error("Error reading transform from " + resource + " into element " + e, t);
             }
         });
     }
 
     public static void readTransform(AsyncReadGraph graph, final Resource resource, final IElement e, final AsyncProcedure<IElement> procedure) {
             }
         });
     }
 
     public static void readTransform(AsyncReadGraph graph, final Resource resource, final IElement e, final AsyncProcedure<IElement> procedure) {
-        G2DResource G2D = graph.getService(G2DResource.class);
-        graph.forPossibleRelatedValue(resource, G2D.HasTransform, Bindings.DOUBLE_ARRAY, new AsyncProcedure<double[]>() {
-            @Override
-            public void exception(AsyncReadGraph graph, Throwable throwable) {
-                procedure.exception(graph, throwable);
-            }
-
-            @Override
-            public void execute(AsyncReadGraph graph, double[] mat) {
-                mat = DiagramGraphUtil.validateAffineTransform(resource, mat);
-                AffineTransform tr = mat != null ? new AffineTransform(mat) : new AffineTransform();
-                ElementUtils.setTransform(e, tr);
-                procedure.execute(graph, e);
-            }
-        });
+        DiagramResource DIA = graph.getService(DiagramResource.class);
+        try {
+            AffineTransform tr = DiagramGraphUtil.getDynamicAffineTransform((ReadGraph)graph, null, resource, DIA.HasDynamicTransform, true);
+            ElementUtils.setTransform(e, tr);
+        } catch (DatabaseException e1) {
+            ElementUtils.setTransform(e, new AffineTransform());
+        }
+        procedure.execute(graph, e);
     }
 
     public static ISynchronizationContext getContext(IDiagram diagram) {
     }
 
     public static ISynchronizationContext getContext(IDiagram diagram) {
index fba5de5065705053179ab50d695027d331369210..a6f2e0a77f4f354c146f98d52e38072a0f1cb7fd 100644 (file)
@@ -22,12 +22,16 @@ import org.simantics.g2d.canvas.ICanvasContext;
 import org.simantics.g2d.diagram.IDiagram;
 import org.simantics.g2d.element.ElementClass;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.diagram.IDiagram;
 import org.simantics.g2d.element.ElementClass;
 import org.simantics.g2d.element.IElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * @author Antti Villberg
  */
 public class NodeRequest extends BaseRequest2<Resource, IElement> {
 
 
 /**
  * @author Antti Villberg
  */
 public class NodeRequest extends BaseRequest2<Resource, IElement> {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(NodeRequest.class);
+
     final IDiagram diagram;
     final Listener<IElement> loadListener;
 
     final IDiagram diagram;
     final Listener<IElement> loadListener;
 
@@ -101,7 +105,7 @@ public class NodeRequest extends BaseRequest2<Resource, IElement> {
 
                             @Override
                             public void exception(AsyncReadGraph graph, Throwable throwable) {
 
                             @Override
                             public void exception(AsyncReadGraph graph, Throwable throwable) {
-                                throwable.printStackTrace();
+                                LOGGER.error("Unexpected error in GetElementClassRequest", throwable);
                                 procedure.execute(graph, null);
                             }
 
                                 procedure.execute(graph, null);
                             }
 
@@ -112,7 +116,7 @@ public class NodeRequest extends BaseRequest2<Resource, IElement> {
 
                                     @Override
                                     public void exception(AsyncReadGraph graph, Throwable throwable) {
 
                                     @Override
                                     public void exception(AsyncReadGraph graph, Throwable throwable) {
-                                        throwable.printStackTrace();
+                                        LOGGER.error("Unexpected error in SpawnRequest", throwable);
                                         procedure.execute(graph, null);
                                     }
 
                                         procedure.execute(graph, null);
                                     }
 
@@ -128,8 +132,7 @@ public class NodeRequest extends BaseRequest2<Resource, IElement> {
                                             factory.load(graph, canvas, diagram, data, element, new AsyncProcedure<IElement>() {
                                                 @Override
                                                 public void exception(AsyncReadGraph graph, Throwable throwable) {
                                             factory.load(graph, canvas, diagram, data, element, new AsyncProcedure<IElement>() {
                                                 @Override
                                                 public void exception(AsyncReadGraph graph, Throwable throwable) {
-                                                    // TODO: proper logging
-                                                    throwable.printStackTrace();
+                                                    LOGGER.error("Unexpected error in ElementFactory.load (factory=" + factory + ")", throwable);
                                                 }
                                                 @Override
                                                 public void execute(AsyncReadGraph graph, IElement result) {
                                                 }
                                                 @Override
                                                 public void execute(AsyncReadGraph graph, IElement result) {
index c00820ba31a233ff92394213b51f731e21ff364d..fd1603d393682211fea293a25c8f92a17c744f33 100644 (file)
@@ -97,7 +97,7 @@ public class RouteGraphConnectionClassFactory extends SyncElementFactory {
         Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);
 
         Set<BackendConnection> backendConnections = new THashSet<>();
         Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);
 
         Set<BackendConnection> backendConnections = new THashSet<>();
-        RouteGraph rg = RouteGraphUtils.load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, backendConnections);
+        RouteGraph rg = RouteGraphUtils.load(graph, diagramRuntime, connection, canvas, diagram, element, modelingRules, backendConnections);
 
         // Load connection line style.
         ConnectionStyle style = RouteGraphUtils.readConnectionStyle(graph, modelingRules, connection, STR);
 
         // Load connection line style.
         ConnectionStyle style = RouteGraphUtils.readConnectionStyle(graph, modelingRules, connection, STR);
index 60f7a09dbb93d50a9ef7896b1e3703cd47a1945a..3127b431c7ce7178c5e656d1a745b79e13539a86 100644 (file)
@@ -39,6 +39,7 @@ import org.simantics.diagram.connection.RouteGraphConnectionClass;
 import org.simantics.diagram.connection.RouteLine;
 import org.simantics.diagram.connection.RouteNode;
 import org.simantics.diagram.connection.RouteTerminal;
 import org.simantics.diagram.connection.RouteLine;
 import org.simantics.diagram.connection.RouteNode;
 import org.simantics.diagram.connection.RouteTerminal;
+import org.simantics.diagram.connection.RouteTerminalPosition;
 import org.simantics.diagram.connection.rendering.BasicConnectionStyle;
 import org.simantics.diagram.connection.rendering.ConnectionStyle;
 import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;
 import org.simantics.diagram.connection.rendering.BasicConnectionStyle;
 import org.simantics.diagram.connection.rendering.ConnectionStyle;
 import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;
@@ -46,6 +47,7 @@ import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle;
 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
 import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;
 import org.simantics.diagram.content.EdgeResource;
 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
 import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;
 import org.simantics.diagram.content.EdgeResource;
+import org.simantics.diagram.content.ResourceTerminal;
 import org.simantics.diagram.content.TerminalMap;
 import org.simantics.diagram.query.DiagramRequests;
 import org.simantics.diagram.stubs.DiagramResource;
 import org.simantics.diagram.content.TerminalMap;
 import org.simantics.diagram.query.DiagramRequests;
 import org.simantics.diagram.stubs.DiagramResource;
@@ -55,11 +57,14 @@ import org.simantics.diagram.synchronization.graph.RouteGraphConnection;
 import org.simantics.g2d.canvas.ICanvasContext;
 import org.simantics.g2d.canvas.impl.CanvasContext;
 import org.simantics.g2d.diagram.IDiagram;
 import org.simantics.g2d.canvas.ICanvasContext;
 import org.simantics.g2d.canvas.impl.CanvasContext;
 import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.DataElementMap;
 import org.simantics.g2d.diagram.handler.Topology.Connection;
 import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.diagram.handler.Topology.Terminal;
 import org.simantics.g2d.diagram.impl.ElementDiagram;
 import org.simantics.g2d.element.ElementUtils;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
 import org.simantics.g2d.diagram.impl.ElementDiagram;
 import org.simantics.g2d.element.ElementUtils;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
+import org.simantics.g2d.element.handler.TerminalLayout;
 import org.simantics.g2d.elementclass.FlagClass.Type;
 import org.simantics.layer0.Layer0;
 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;
 import org.simantics.g2d.elementclass.FlagClass.Type;
 import org.simantics.layer0.Layer0;
 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;
@@ -93,10 +98,10 @@ public class RouteGraphUtils {
         Layer0 L0 = Layer0.getInstance(graph);
         Resource diagramResource = graph.getPossibleObject(connection, L0.PartOf);
         IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagramResource, null), TransientCacheListener.<IModelingRules>instance());
         Layer0 L0 = Layer0.getInstance(graph);
         Resource diagramResource = graph.getPossibleObject(connection, L0.PartOf);
         IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagramResource, null), TransientCacheListener.<IModelingRules>instance());
-        return load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, null);
+        return load(graph, diagramRuntime, connection, canvas, diagram, null, modelingRules, null);
     }
 
     }
 
-    public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram, IModelingRules modelingRules, Set<BackendConnection> backendConnections) throws DatabaseException {
+    public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram, IElement element, IModelingRules modelingRules, Set<BackendConnection> backendConnections) throws DatabaseException {
 
         DiagramResource DIA = DiagramResource.getInstance(graph);
         StructuralResource2 STR = StructuralResource2.getInstance(graph);
 
         DiagramResource DIA = DiagramResource.getInstance(graph);
         StructuralResource2 STR = StructuralResource2.getInstance(graph);
@@ -171,7 +176,7 @@ public class RouteGraphUtils {
                         connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
                     connectorToModeledAttachment.put(connector, attachment);
                     if (DEBUG)
                         connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
                     connectorToModeledAttachment.put(connector, attachment);
                     if (DEBUG)
-                        System.out.println("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
+                        LOGGER.debug("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
                 } else if (graph.isInstanceOf(terminalElement, DIA.Flag)) {
                     // Secondary: believe flag type
                     attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules, DIA);
                 } else if (graph.isInstanceOf(terminalElement, DIA.Flag)) {
                     // Secondary: believe flag type
                     attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules, DIA);
@@ -180,7 +185,7 @@ public class RouteGraphUtils {
                             connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
                         connectorToModeledAttachment.put(connector, attachment);
                         if (DEBUG)
                             connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
                         connectorToModeledAttachment.put(connector, attachment);
                         if (DEBUG)
-                            System.out.println("flag type decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
+                            LOGGER.debug("flag type decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
                     }
                 }
             }
                     }
                 }
             }
@@ -193,17 +198,18 @@ public class RouteGraphUtils {
         if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) {
             forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next(), DIA);
             if (DEBUG)
         if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) {
             forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next(), DIA);
             if (DEBUG)
-                System.out.println("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation));
+                LOGGER.debug("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation));
         }
 
         Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); 
         }
 
         Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); 
+        DataElementMap diagramDataElementMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
 
         // Load all node terminal connections as RouteTerminals
         for (Statement toConnector : toConnectorStatements) {
             Resource connector = toConnector.getObject();
             Resource attachmentRelation = toConnector.getPredicate();
             if (DEBUG)
 
         // Load all node terminal connections as RouteTerminals
         for (Statement toConnector : toConnectorStatements) {
             Resource connector = toConnector.getObject();
             Resource attachmentRelation = toConnector.getPredicate();
             if (DEBUG)
-                System.out.println("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+                LOGGER.debug("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation));
 
             Statement terminalStm = findTerminalStatement(graph, connection, connector, STR);
             if (terminalStm == null)
 
             Statement terminalStm = findTerminalStatement(graph, connection, connector, STR);
             if (terminalStm == null)
@@ -237,19 +243,18 @@ public class RouteGraphUtils {
             if (position.length != 2)
                 position = new double[] { 0, 0 };
 
             if (position.length != 2)
                 position = new double[] { 0, 0 };
 
+            AffineTransform terminalTr = DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement); 
+            final AffineTransform terminalElementTransform = new AffineTransform(terminalTr);
+
             if (DEBUG) {
             if (DEBUG) {
-                System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm));
-                System.out.println("terminal: " + graph.getURI(terminalStm.getPredicate()));
+                LOGGER.debug("terminalStm: " + NameUtils.toString(graph, terminalStm));
+                LOGGER.debug("terminal: " + NameUtils.getURIOrSafeNameInternal(graph, terminalStm.getPredicate()));
+                LOGGER.debug("terminalElement: " + NameUtils.getURIOrSafeNameInternal(graph, terminalElement) + " : " + NameUtils.getURIOrSafeNameInternal(graph, terminalElementType));
+                LOGGER.debug("terminalElementTr: " + terminalTr);
             }
             }
-            AffineTransform terminalElementTr = diagramRuntime != null ?
-                    DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement) : 
-                    DiagramGraphUtil.getWorldTransform(graph, terminalElement);
-
-            if (DEBUG)
-                System.out.println("terminalElementTr: " + terminalElementTr);
 
 
-            double x = terminalElementTr.getTranslateX();
-            double y = terminalElementTr.getTranslateY();
+            double x = terminalTr.getTranslateX();
+            double y = terminalTr.getTranslateY();
             double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1;
             int direction = 0x0;
 
             double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1;
             int direction = 0x0;
 
@@ -259,20 +264,20 @@ public class RouteGraphUtils {
             if (att != null) {
                 attachmentRelation = att;
                 if (DEBUG)
             if (att != null) {
                 attachmentRelation = att;
                 if (DEBUG)
-                    System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+                    LOGGER.debug("modeling rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
             } else if (forcedAttachmentRelation != null) {
                 attachmentRelation = forcedAttachmentRelation;
                 if (DEBUG)
             } else if (forcedAttachmentRelation != null) {
                 attachmentRelation = forcedAttachmentRelation;
                 if (DEBUG)
-                    System.out.println("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+                    LOGGER.debug("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
             }
             if (DEBUG)
             }
             if (DEBUG)
-                System.out.println("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+                LOGGER.debug("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
 
             // Get element bounds to decide allowed terminal direction(s)
             IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));
             ElementUtils.getElementBounds(te, bounds);
             {
 
             // Get element bounds to decide allowed terminal direction(s)
             IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));
             ElementUtils.getElementBounds(te, bounds);
             {
-                Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr);
+                Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalTr);
                 bounds.setFrame(shp.getBounds2D());
             }
 
                 bounds.setFrame(shp.getBounds2D());
             }
 
@@ -285,28 +290,26 @@ public class RouteGraphUtils {
             maxx = bounds.getMaxX();
             maxy = bounds.getMaxY();
 
             maxx = bounds.getMaxX();
             maxy = bounds.getMaxY();
 
-            AffineTransform terminalPos = DiagramGraphUtil.getDynamicAffineTransform(graph, terminalElement, terminal);
-            //AffineTransform terminalPos2 = DiagramGraphUtil.getAffineTransform(graph, terminal);
+            final ResourceTerminal rt = new ResourceTerminal(terminal);
+            final TerminalLayout tl = te.getElementClass().getSingleItem(TerminalLayout.class);
+            AffineTransform terminalPos = tl.getTerminalPosition(element, rt);
+
             if (terminalPos != null) {
             if (terminalPos != null) {
-                if (DEBUG) {
-                    System.out.println("terminalPos: " + terminalPos);
-                    //System.out.println("terminalPos2: " + terminalPos2);
-                }
-                terminalElementTr.concatenate(terminalPos);
+                terminalTr.concatenate(terminalPos);
+                x = terminalTr.getTranslateX();
+                y = terminalTr.getTranslateY();
                 if (DEBUG)
                 if (DEBUG)
-                    System.out.println("terminalElementTr: " + terminalElementTr);
-                x = terminalElementTr.getTranslateX();
-                y = terminalElementTr.getTranslateY();
+                    LOGGER.debug("terminalPos/Tr: " + terminalPos + ", " + terminalTr);
             }
 
             Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER);
             if (allowedDirections != null) {
                 direction |= allowedDirections;
             }
 
             Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER);
             if (allowedDirections != null) {
                 direction |= allowedDirections;
-                direction = rotateDirection(direction, terminalElementTr);
+                direction = rotateDirection(direction, terminalTr);
             } else {
                 direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds);
             }
             } else {
                 direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds);
             }
-            //System.out.println("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction));
+            //LOGGER.debug("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction));
 
             if (backendConnections != null) {
                 backendConnections.add(
 
             if (backendConnections != null) {
                 backendConnections.add(
@@ -326,10 +329,11 @@ public class RouteGraphUtils {
             // FIXME: routegraph crashes if this is done for all terminals regardless of the amount of terminals.
 
             if (DEBUG)
             // FIXME: routegraph crashes if this is done for all terminals regardless of the amount of terminals.
 
             if (DEBUG)
-                System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+                LOGGER.debug("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));
             ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL);
 
             ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL);
 
-            RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle);
+            RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle,
+                    new RouteTerminalPositionImpl(diagram, diagramDataElementMap, terminalElement, terminalElementTransform, tl, rt));
             routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );
 
             nodeByData.put( connector, routeTerminal );
             routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );
 
             nodeByData.put( connector, routeTerminal );
@@ -705,4 +709,50 @@ public class RouteGraphUtils {
         }
     }
 
         }
     }
 
+    private static class RouteTerminalPositionImpl implements RouteTerminalPosition {
+
+        private IDiagram diagram;
+        private DataElementMap dataElementMap;
+        private Resource element;
+        private AffineTransform elementTransform;
+        private TerminalLayout terminalLayout;
+        private Terminal elementTerminal;
+
+        private transient AffineTransform lastTerminalTr;
+        private transient AffineTransform transform;
+
+        public RouteTerminalPositionImpl(IDiagram diagram, DataElementMap dem, Resource element, AffineTransform elementTransform, TerminalLayout terminalLayout, Terminal terminal) {
+            this.diagram = diagram;
+            this.dataElementMap = dem;
+            this.element = element;
+            this.elementTransform = elementTransform;
+            this.terminalLayout = terminalLayout;
+            this.elementTerminal = terminal;
+        }
+
+        @Override
+        public AffineTransform getTransform() {
+            IElement actualElement = dataElementMap.getElement(diagram, element);
+            AffineTransform terminalTr = actualElement != null ? terminalLayout.getTerminalPosition(actualElement, elementTerminal) : null;
+            if (terminalTr == null)
+                return elementTransform;
+
+            // Return cached transform if terminal transform has not changed.
+            AffineTransform result = this.transform;
+            AffineTransform lastTerminalTr = this.lastTerminalTr;
+            if (lastTerminalTr != null) {
+                if (terminalTr.equals(lastTerminalTr))
+                    return result;
+                lastTerminalTr.setTransform(terminalTr);
+            } else {
+                lastTerminalTr = this.lastTerminalTr = new AffineTransform(terminalTr);
+                result = this.transform = new AffineTransform();
+            }
+
+            result.setTransform(elementTransform);
+            result.concatenate(terminalTr);
+            return result;
+        }
+
+    }
 }
 }
index 65df67acbc0c86a40b03ca0355f0f0705e3cd59b..94d783f2dfcaf4d78a00b2fe2ec1d68f25a747fb 100644 (file)
@@ -21,7 +21,6 @@ import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
 import org.simantics.db.common.procedure.guarded.GuardedAsyncProcedureWrapper;
 import org.simantics.db.Resource;
 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
 import org.simantics.db.common.procedure.guarded.GuardedAsyncProcedureWrapper;
-import org.simantics.db.common.request.BinaryAsyncRead;
 import org.simantics.db.common.request.ReadRequest;
 import org.simantics.db.common.request.UnaryAsyncRead;
 import org.simantics.db.common.utils.NameUtils;
 import org.simantics.db.common.request.ReadRequest;
 import org.simantics.db.common.request.UnaryAsyncRead;
 import org.simantics.db.common.utils.NameUtils;
index 2c6c9961a822ea5d539e75a216b2807226b67346..4cccea938270b6b8ac0b50baf6ac91d2b190528a 100644 (file)
@@ -94,7 +94,6 @@ import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
 import org.simantics.scenegraph.utils.GeometryUtils;
 import org.simantics.structural2.modelingRules.ConnectionJudgement;
 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
 import org.simantics.scenegraph.utils.GeometryUtils;
 import org.simantics.structural2.modelingRules.ConnectionJudgement;
-import org.simantics.utils.datastructures.Callback;
 import org.simantics.utils.datastructures.Pair;
 import org.simantics.utils.datastructures.Triple;
 import org.simantics.utils.logging.TimeLogger;
 import org.simantics.utils.datastructures.Pair;
 import org.simantics.utils.datastructures.Triple;
 import org.simantics.utils.logging.TimeLogger;
@@ -765,12 +764,9 @@ public class RouteGraphConnectTool extends AbstractMode {
                 Resource attachToLine = RouteGraphConnection.deserialize(graph, attachedToRouteLine.getData());
                 builder.attachToRouteGraph(graph, judgment, connection, attachToLine, controlPoints, endTerminal, FlagClass.Type.Out);
             }
                 Resource attachToLine = RouteGraphConnection.deserialize(graph, attachedToRouteLine.getData());
                 builder.attachToRouteGraph(graph, judgment, connection, attachToLine, controlPoints, endTerminal, FlagClass.Type.Out);
             }
-        }, new Callback<DatabaseException>() {
-            @Override
-            public void run(DatabaseException parameter) {
-                if (parameter != null)
-                    ExceptionUtils.logAndShowError(parameter);
-            }
+        }, e -> {
+            if (e != null)
+                ExceptionUtils.logAndShowError(e);
         });
     }
 
         });
     }
 
index 85f75a06638150f83af0fc4f413836a7f6a6d0b9..677c97f0f4fa0062779d9688ea718e39218f4508 100644 (file)
@@ -44,7 +44,7 @@ public class TerminalUtil {
     private static final ThreadLocal<ArrayList<Terminal>> TERMINALS = new ThreadLocal<ArrayList<Terminal>>() {
         @Override
         protected ArrayList<Terminal> initialValue() {
     private static final ThreadLocal<ArrayList<Terminal>> TERMINALS = new ThreadLocal<ArrayList<Terminal>>() {
         @Override
         protected ArrayList<Terminal> initialValue() {
-            return new ArrayList<Terminal>();
+            return new ArrayList<>();
         }
     };
 
         }
     };
 
@@ -54,7 +54,7 @@ public class TerminalUtil {
     private static final ThreadLocal<ArrayList<IElement>> ELEMENTS = new ThreadLocal<ArrayList<IElement>>() {
         @Override
         protected ArrayList<IElement> initialValue() {
     private static final ThreadLocal<ArrayList<IElement>> ELEMENTS = new ThreadLocal<ArrayList<IElement>>() {
         @Override
         protected ArrayList<IElement> initialValue() {
-            return new ArrayList<IElement>();
+            return new ArrayList<>();
         }
     };
 
         }
     };
 
@@ -144,7 +144,7 @@ public class TerminalUtil {
             pickCenterY = bounds.getCenterY();
         }
 
             pickCenterY = bounds.getCenterY();
         }
 
-        List<TerminalInfo> result = new ArrayList<TerminalInfo>();
+        List<TerminalInfo> result = new ArrayList<>();
         ArrayList<Terminal> terminals = TERMINALS.get();
         for (IElement e : elements)
         {
         ArrayList<Terminal> terminals = TERMINALS.get();
         for (IElement e : elements)
         {
@@ -160,8 +160,10 @@ public class TerminalUtil {
             {
                 Shape terminalShape = getTerminalShape(tls, e, t);
                 if ( terminalShape==null /* point terminal */ && !pickPointTerminals ) continue;
             {
                 Shape terminalShape = getTerminalShape(tls, e, t);
                 if ( terminalShape==null /* point terminal */ && !pickPointTerminals ) continue;
-                if ( terminalShape!=null /* are terminal */ && !pickAreaTerminals ) continue;
-                AffineTransform terminalToDiagram = getTerminalPosOnDiagram(e, t);
+                if ( terminalShape!=null /* area terminal */ && !pickAreaTerminals ) continue;
+
+                AffineTransform terminalToElement = getTerminalPosOnElement0(e, t);
+                AffineTransform terminalToDiagram = concatenate(ElementUtils.getTransform(e), terminalToElement);
 
                 // Pick distance will is set to 0 if there was no pick shape,
                 // i.e. everything is picked.
 
                 // Pick distance will is set to 0 if there was no pick shape,
                 // i.e. everything is picked.
@@ -175,11 +177,10 @@ public class TerminalUtil {
                     pickDist = Point2D.distance(pickCenterX, pickCenterY, terminalToDiagram.getTranslateX(), terminalToDiagram.getTranslateY());
                 }
 
                     pickDist = Point2D.distance(pickCenterX, pickCenterY, terminalToDiagram.getTranslateX(), terminalToDiagram.getTranslateY());
                 }
 
-                AffineTransform terminalToElement = getTerminalPosOnElement(e, t);
                 TerminalInfo ti = new TerminalInfo();
                 ti.e = e;
                 ti.posDia = terminalToDiagram;
                 TerminalInfo ti = new TerminalInfo();
                 ti.e = e;
                 ti.posDia = terminalToDiagram;
-                ti.posElem = terminalToElement;
+                ti.posElem = terminalToElement != null ? new AffineTransform(terminalToElement) : new AffineTransform();
                 ti.t = t;
                 ti.shape = terminalShape;
                 ti.distance = pickDist;
                 ti.t = t;
                 ti.shape = terminalShape;
                 ti.distance = pickDist;
@@ -224,8 +225,10 @@ public class TerminalUtil {
             tt.getTerminals(e, terminals);
             for (Terminal t : terminals)
             {
             tt.getTerminals(e, terminals);
             for (Terminal t : terminals)
             {
+                AffineTransform        terminalToElement = getTerminalPosOnElement0(e, t);
+                AffineTransform terminalToDiagram = concatenate(ElementUtils.getTransform(e), terminalToElement);
+
                 Shape terminalShape = getTerminalShape(e, t);
                 Shape terminalShape = getTerminalShape(e, t);
-                AffineTransform terminalToDiagram = getTerminalPosOnDiagram(e, t);
                 Shape pickTargetShape = terminalShape != null ? terminalShape : POINT_PICK_SHAPE;
                 pickTargetShape = GeometryUtils.transformShape(pickTargetShape, terminalToDiagram);
                 if (!GeometryUtils.intersects(pickShape, pickTargetShape)) continue;
                 Shape pickTargetShape = terminalShape != null ? terminalShape : POINT_PICK_SHAPE;
                 pickTargetShape = GeometryUtils.transformShape(pickTargetShape, terminalToDiagram);
                 if (!GeometryUtils.intersects(pickShape, pickTargetShape)) continue;
@@ -235,7 +238,7 @@ public class TerminalUtil {
 
                 result.e = e;
                 result.posDia = terminalToDiagram;
 
                 result.e = e;
                 result.posDia = terminalToDiagram;
-                result.posElem = getTerminalPosOnElement(e, t);
+                result.posElem = terminalToElement != null ? new AffineTransform(terminalToElement) : new AffineTransform();
                 result.t = t;
                 result.shape = terminalShape;
                 result.distance = Math.sqrt(pickDist);
                 result.t = t;
                 result.shape = terminalShape;
                 result.distance = Math.sqrt(pickDist);
@@ -300,11 +303,8 @@ public class TerminalUtil {
      */
     public static AffineTransform getTerminalPosOnDiagram(IElement e, Terminal t)
     {
      */
     public static AffineTransform getTerminalPosOnDiagram(IElement e, Terminal t)
     {
-        AffineTransform        pos     = getTerminalPosOnElement(e, t);
-        AffineTransform        at      = ElementUtils.getTransform(e);
-        AffineTransform result = new AffineTransform(at);
-        result.concatenate(pos);
-        return result;
+        AffineTransform        pos     = getTerminalPosOnElement0(e, t);
+        return concatenate(ElementUtils.getTransform(e), pos);
     }
 
     /**
     }
 
     /**
@@ -314,6 +314,18 @@ public class TerminalUtil {
      * @return Transform of a terminal
      */
     public static AffineTransform getTerminalPosOnElement(IElement e, Terminal t)
      * @return Transform of a terminal
      */
     public static AffineTransform getTerminalPosOnElement(IElement e, Terminal t)
+    {
+        AffineTransform tr = getTerminalPosOnElement0(e, t);
+        return tr != null ? new AffineTransform(tr) : null;
+    }
+
+    /**
+     * Get position of a terminal in element
+     * @param e element
+     * @param t terminal
+     * @return Transform of a terminal
+     */
+    private static AffineTransform getTerminalPosOnElement0(IElement e, Terminal t)
     {
         List<TerminalLayout>   tls = e.getElementClass().getItemsByClass(TerminalLayout.class);
         AffineTransform                        result = null;
     {
         List<TerminalLayout>   tls = e.getElementClass().getItemsByClass(TerminalLayout.class);
         AffineTransform                        result = null;
@@ -363,7 +375,7 @@ public class TerminalUtil {
         PickRequest req = new PickRequest(pickShape);
         DiagramUtils.pick(diagram, req, elements);
 
         PickRequest req = new PickRequest(pickShape);
         DiagramUtils.pick(diagram, req, elements);
 
-        ArrayList<Bend> bends = new ArrayList<Bend>();
+        ArrayList<Bend> bends = new ArrayList<>();
         Point2D bendPos = new Point2D.Double();
         for (IElement e : diagram.getElements())
         {
         Point2D bendPos = new Point2D.Double();
         for (IElement e : diagram.getElements())
         {
@@ -430,7 +442,7 @@ public class TerminalUtil {
             }
         }
 
             }
         }
 
-        ArrayList<TerminalInfo> result = new ArrayList<TerminalInfo>(len);
+        ArrayList<TerminalInfo> result = new ArrayList<>(len);
         for (int i = 0; i < len; ++i) {
             TerminalInfo ti = tis.get(i);
             if (ti.distance == nearest.distance
         for (int i = 0; i < len; ++i) {
             TerminalInfo ti = tis.get(i);
             if (ti.distance == nearest.distance
@@ -444,4 +456,11 @@ public class TerminalUtil {
         return result;
     }
 
         return result;
     }
 
+    private static AffineTransform concatenate(AffineTransform a, AffineTransform b) {
+        AffineTransform result = new AffineTransform(a);
+        if (b != null)
+            result.concatenate(b);
+        return result;
+    }
+
 }
 }
index 038e68dc2ca275d01e4d17f4a600c88a7fd7abfd..8d6f9ab586416082c7a15b4850dca8b9284eb54e 100644 (file)
@@ -28,10 +28,14 @@ import org.simantics.g2d.utils.geom.DirectionSet;
 public interface TerminalLayout extends ElementHandler {
        
        /**
 public interface TerminalLayout extends ElementHandler {
        
        /**
-        * Get a copy of terminal transformation (on element)
+        * Get specified terminal transformation on specified element
+        * 
         * @param node
         * @param t
         * @param node
         * @param t
-        * @return position of a terminal or null
+        * @return position of a terminal or null if specified terminal does not
+        *         belong to the specified element. The returned AffineTransform
+        *         must be treated as if it were immutable since the implementation
+        *         is allowed to returned singleton/cached values.
         */
        AffineTransform getTerminalPosition(IElement node, Terminal t);
        
         */
        AffineTransform getTerminalPosition(IElement node, Terminal t);
        
index f38e0a6c5910d08555783168e777b2f574bc224b..786e07e0a045321d04d349a33864221cab53ab26 100644 (file)
@@ -48,7 +48,7 @@ public class Terminals implements TerminalLayout, TerminalTopology {
         ObjectTerminal ti = terminalMap.get(t);
         if (ti == null)
             return null;
         ObjectTerminal ti = terminalMap.get(t);
         if (ti == null)
             return null;
-        return new AffineTransform(ti.getTransform());
+        return ti.getTransform();
     }
 
     @Override
     }
 
     @Override
index 5e0e830752c5a3869655a6c3101b4311e4c182cf..92738469719e2a0a165a89378879701ab1146d7f 100644 (file)
@@ -95,3 +95,26 @@ getNodeCount diagram = do
     provider = getICanvasSceneGraphProvider model composite diagramRVI
     context = getCanvasContext provider 
     getCount context
     provider = getICanvasSceneGraphProvider model composite diagramRVI
     context = getCanvasContext provider 
     getCount context
+
+importJava "org.simantics.scenegraph.g2d.G2DNodeModification" where
+    data G2DNodeModification
+    @JavaName "<init>"
+    createG2DNodeModification :: [SVGNodeAssignment] -> [TransformationAssignment] -> G2DNodeModification 
+    
+importJava "org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment" where
+    data SVGNodeAssignment
+    
+importJava "org.simantics.scenegraph.g2d.nodes.TransformationAssignment" where
+    data TransformationAssignment
+    @JavaName "<init>"
+    createTransformationAssignment :: a -> AffineTransform -> TransformationAssignment
+    
+importJava "java.awt.geom.AffineTransform" where
+    data AffineTransform
+    @JavaName "<init>"
+    createAffineTransform :: Double -> Double -> Double -> Double -> Double -> Double -> AffineTransform
+
+importJava "org.simantics.scenegraph.g2d.AffineTransformFunctions" where
+    transform :: AffineTransform -> (Double,Double) -> (Double,Double)
+    inverseTransform :: AffineTransform -> (Double,Double) -> (Double,Double)
+    
\ No newline at end of file
index fb584331e8667d5efaf77829e0b1960e2b12ec12..edc7aa9e1e5757a7d822ba96c75bb83ca7506aa6 100644 (file)
  *******************************************************************************/
 package org.simantics.modeling.adapters;
 
  *******************************************************************************/
 package org.simantics.modeling.adapters;
 
+import java.awt.geom.AffineTransform;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
 
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
@@ -22,8 +26,12 @@ import org.simantics.db.layer0.variable.Variables;
 import org.simantics.diagram.profile.StyleBase;
 import org.simantics.diagram.stubs.DiagramResource;
 import org.simantics.scenegraph.INode;
 import org.simantics.diagram.profile.StyleBase;
 import org.simantics.diagram.stubs.DiagramResource;
 import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.G2DNodeModification;
+import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.scenegraph.g2d.nodes.SVGNode;
-import org.simantics.scenegraph.g2d.nodes.SVGNode.SVGNodeAssignment;
+import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment;
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
+import org.simantics.scenegraph.g2d.nodes.TransformationAssignment;
 import org.simantics.scenegraph.profile.EvaluationContext;
 import org.simantics.scenegraph.utils.NodeUtil;
 import org.simantics.utils.datastructures.Pair;
 import org.simantics.scenegraph.profile.EvaluationContext;
 import org.simantics.scenegraph.utils.NodeUtil;
 import org.simantics.utils.datastructures.Pair;
@@ -32,39 +40,63 @@ import org.simantics.utils.datastructures.Pair;
  * @author Antti Villberg
  * @since 1.29.0
  */
  * @author Antti Villberg
  * @since 1.29.0
  */
-public class SymbolCodeStyle extends StyleBase<Pair<List<String>, Object>> {
+public class SymbolCodeStyle extends StyleBase<Pair<G2DNodeModification, Object>> {
 
     @Override
 
     @Override
-    public Pair<List<String>, Object> calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException {
+    public Pair<G2DNodeModification, Object> calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException {
         DiagramResource DIA = DiagramResource.getInstance(graph);
 
         // Find a component for the element 
         Variable elementVariable = Variables.getPossibleVariable(graph, element);
         if (elementVariable == null)
             return null;
         DiagramResource DIA = DiagramResource.getInstance(graph);
 
         // Find a component for the element 
         Variable elementVariable = Variables.getPossibleVariable(graph, element);
         if (elementVariable == null)
             return null;
-        List<String> styles = elementVariable.getPossiblePropertyValue(graph, DIA.symbolCode);
-        if (styles == null)
+        Object modi = elementVariable.getPossiblePropertyValue(graph, DIA.symbolCode);
+        if (modi == null)
             return null;
 
         // If element is moved, recalculate style
         Object transform = graph.getPossibleRelatedValue(element, DIA.HasTransform);
 
             return null;
 
         // If element is moved, recalculate style
         Object transform = graph.getPossibleRelatedValue(element, DIA.HasTransform);
 
-        return Pair.make(styles, transform);
+        if (modi instanceof G2DNodeModification) {
+            return Pair.make((G2DNodeModification)modi, transform);
+        } else if (modi instanceof List) {
+            @SuppressWarnings("unchecked")
+            List<String> styles = (List<String>)modi;
+            List<SVGNodeAssignment> assignments = new ArrayList<>(styles.size()/3);
+            for (int i = 0; i < styles.size()/3; i++)
+                assignments.add(new SVGNodeAssignment(styles.get(3*i), styles.get(3*i+1), styles.get(3*i+2)));
+            return Pair.make(new G2DNodeModification(assignments, Collections.emptyList()), transform);
+        } else {
+            throw new DatabaseException("Invalid symbolCode value: " + modi);
+        }
     }
 
     @Override
     }
 
     @Override
-    public void applyStyleForNode(EvaluationContext evaluationContext, INode node, Pair<List<String>, Object> result) {
-        if (result == null || result.first == null || result.first.isEmpty())
+    public void applyStyleForNode(EvaluationContext evaluationContext, INode node, Pair<G2DNodeModification, Object> result) {
+        if (result == null || result.first == null)
             return;
 
             return;
 
-        List<String> styles = result.first;
-        List<SVGNodeAssignment> assignments = new ArrayList<>(styles.size()/3);
-        for (int i = 0; i < styles.size()/3; i++)
-            assignments.add(new SVGNodeAssignment(styles.get(3*i), styles.get(3*i+1), styles.get(3*i+2)));
-
-        for (SVGNode p : NodeUtil.collectNodes(node, SVGNode.class)) {
-            p.setAssignments(assignments);
-            p.cleanDiagramCache();
+        G2DNodeModification modification = result.first;
+        if (modification.svgAssignments != null && !modification.svgAssignments.isEmpty()) {
+            for (SVGNode p : NodeUtil.collectNodes(node, SVGNode.class)) {
+                p.setAssignments(modification.svgAssignments);
+                p.cleanDiagramCache();
+            }
+        }
+        if (modification.transformAssignments != null) {
+            Map<Object,AffineTransform> trs = new HashMap<>();
+            for (TransformationAssignment ass : modification.transformAssignments) 
+                trs.put(ass.key, ass.transform);
+            NodeUtil.forChildrenDeep(node, SingleElementNode.class, n -> {
+                Object key = n.getKey();
+                AffineTransform tr = trs.get(key);
+                if (tr != null) {
+                    IG2DNode[] children = n.getSortedNodes();
+                    if (children.length > 0)
+                        children[0].setTransform(tr);
+                }
+                return null;
+            });
         }
     }
 
         }
     }
 
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/AffineTransformFunctions.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/AffineTransformFunctions.java
new file mode 100644 (file)
index 0000000..40e58e9
--- /dev/null
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.scenegraph.g2d;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Point2D;
+
+import org.simantics.scl.runtime.tuple.Tuple2;
+
+/**
+ * @author Antti Villberg
+ * @since 1.29.0
+ */
+public class AffineTransformFunctions {
+
+       public static Tuple2 transform(AffineTransform transform, Tuple2 point) {
+               Point2D result = new Point2D.Double((Double) point.c0, (Double) point.c1);
+               transform.transform(result, result);
+               return new Tuple2(result.getX(), result.getY());
+       }
+
+       public static Tuple2 inverseTransform(AffineTransform transform, Tuple2 point) throws NoninvertibleTransformException {
+               Point2D result = new Point2D.Double((Double)point.c0, (Double)point.c1);
+               transform.inverseTransform(result, result);
+               return new Tuple2(result.getX(), result.getY());
+       }
+
+}
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DNodeModification.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DNodeModification.java
new file mode 100644 (file)
index 0000000..4b68e17
--- /dev/null
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.scenegraph.g2d;
+
+import java.util.List;
+
+import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment;
+import org.simantics.scenegraph.g2d.nodes.TransformationAssignment;
+
+/**
+ * @author Antti Villberg
+ * @since 1.29.0
+ */
+public class G2DNodeModification {
+       public List<SVGNodeAssignment> svgAssignments;
+       public List<TransformationAssignment> transformAssignments;
+       public G2DNodeModification(List<SVGNodeAssignment> svgAssignments, List<TransformationAssignment> transformAssignments) {
+               this.svgAssignments = svgAssignments;
+               this.transformAssignments = transformAssignments;
+       }
+}
index 2dd285fd89d966387d6df759cb365af31740dca5..2fc37b7d59ab6bc9179943ab80b60246118e08e3 100644 (file)
@@ -56,53 +56,7 @@ import com.kitfox.svg.animation.AnimationElement;
 @RasterOutputWidget
 public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
 
 @RasterOutputWidget
 public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
 
-       public static class SVGNodeAssignment {
-               public String elementId;
-               public String attributeNameOrId;
-               public String value;
-               public SVGNodeAssignment(String elementId, String attributeNameOrId, String value) {
-                       this.elementId = elementId;
-                       this.attributeNameOrId = attributeNameOrId;
-                       this.value = value;
-               }
-               @Override
-               public int hashCode() {
-                       final int prime = 31;
-                       int result = 1;
-                       result = prime * result + ((attributeNameOrId == null) ? 0 : attributeNameOrId.hashCode());
-                       result = prime * result + ((elementId == null) ? 0 : elementId.hashCode());
-                       result = prime * result + ((value == null) ? 0 : value.hashCode());
-                       return result;
-               }
-               @Override
-               public boolean equals(Object obj) {
-                       if (this == obj)
-                               return true;
-                       if (obj == null)
-                               return false;
-                       if (getClass() != obj.getClass())
-                               return false;
-                       SVGNodeAssignment other = (SVGNodeAssignment) obj;
-                       if (attributeNameOrId == null) {
-                               if (other.attributeNameOrId != null)
-                                       return false;
-                       } else if (!attributeNameOrId.equals(other.attributeNameOrId))
-                               return false;
-                       if (elementId == null) {
-                               if (other.elementId != null)
-                                       return false;
-                       } else if (!elementId.equals(other.elementId))
-                               return false;
-                       if (value == null) {
-                               if (other.value != null)
-                                       return false;
-                       } else if (!value.equals(other.value))
-                               return false;
-                       return true;
-               }
-       }
-       
-    private static final long serialVersionUID = 8508750881358776559L;
+       private static final long serialVersionUID = 8508750881358776559L;
 
     protected String          data             = null;
     protected String          defaultData      = null;
 
     protected String          data             = null;
     protected String          defaultData      = null;
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNodeAssignment.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNodeAssignment.java
new file mode 100644 (file)
index 0000000..ac292bc
--- /dev/null
@@ -0,0 +1,51 @@
+package org.simantics.scenegraph.g2d.nodes;
+
+/**
+ * @author Antti Villberg
+ * @since 1.29.0
+ */
+public class SVGNodeAssignment {
+       public String elementId;
+       public String attributeNameOrId;
+       public String value;
+       public SVGNodeAssignment(String elementId, String attributeNameOrId, String value) {
+               this.elementId = elementId;
+               this.attributeNameOrId = attributeNameOrId;
+               this.value = value;
+       }
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((attributeNameOrId == null) ? 0 : attributeNameOrId.hashCode());
+               result = prime * result + ((elementId == null) ? 0 : elementId.hashCode());
+               result = prime * result + ((value == null) ? 0 : value.hashCode());
+               return result;
+       }
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               SVGNodeAssignment other = (SVGNodeAssignment) obj;
+               if (attributeNameOrId == null) {
+                       if (other.attributeNameOrId != null)
+                               return false;
+               } else if (!attributeNameOrId.equals(other.attributeNameOrId))
+                       return false;
+               if (elementId == null) {
+                       if (other.elementId != null)
+                               return false;
+               } else if (!elementId.equals(other.elementId))
+                       return false;
+               if (value == null) {
+                       if (other.value != null)
+                               return false;
+               } else if (!value.equals(other.value))
+                       return false;
+               return true;
+       }
+}
\ No newline at end of file
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/TransformationAssignment.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/TransformationAssignment.java
new file mode 100644 (file)
index 0000000..ce39db5
--- /dev/null
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.scenegraph.g2d.nodes;
+
+import java.awt.geom.AffineTransform;
+
+/**
+ * @author Antti Villberg
+ * @since 1.29.0
+ */
+public class TransformationAssignment {
+       public Object key;
+       public AffineTransform transform;
+       public TransformationAssignment(Object key, AffineTransform transform) {
+               this.key = key;
+               this.transform = transform;
+       }
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((key == null) ? 0 : key.hashCode());
+               result = prime * result + ((transform == null) ? 0 : transform.hashCode());
+               return result;
+       }
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               TransformationAssignment other = (TransformationAssignment) obj;
+               if (key == null) {
+                       if (other.key != null)
+                               return false;
+               } else if (!key.equals(other.key))
+                       return false;
+               if (transform == null) {
+                       if (other.transform != null)
+                               return false;
+               } else if (!transform.equals(other.transform))
+                       return false;
+               return true;
+       }
+}
\ No newline at end of file
index 084c0d6582874a7f7cb64439a58e2495e4da12f8..69268bad0644919f05032b7fdf84429d29bcbe37 100644 (file)
@@ -349,6 +349,8 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
 
         boolean selected = NodeUtil.isSelected(this, 1);
 
 
         boolean selected = NodeUtil.isSelected(this, 1);
 
+        rg.updateTerminals();
+
         if (currentAction != null) {
             currentAction.render(g, renderer, mouseX, mouseY);
         } else {
         if (currentAction != null) {
             currentAction.render(g, renderer, mouseX, mouseY);
         } else {