]> 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;
 
+    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
@@ -78,21 +85,31 @@ public class RouteGraph implements Serializable {
      * </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.
      */
+    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) {
-       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,
-            ILineEndStyle style, ILineEndStyle dynamicStyle) {
+            ILineEndStyle style, ILineEndStyle dynamicStyle, RouteTerminalPosition position) {
         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,
-                maxX, maxY, allowedDirections, false, style); 
+                maxX, maxY, allowedDirections, false, style, position); 
         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, 
-                0xf, true, style); 
+                0xf, true, style, null); 
         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(), 
-                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(),
-                terminal.getAllowedDirections(), terminal.getStyle(), terminal.getDynamicStyle());
+                terminal.getAllowedDirections(), terminal.getStyle(), terminal.getDynamicStyle(), terminal.getDynamicPosition());
         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, 
-                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 java.awt.geom.AffineTransform;
 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 RouteTerminalPosition dynamicPosition;
 
     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;
@@ -54,8 +56,9 @@ public class RouteTerminal extends RoutePoint implements RouteNode, Serializable
         this.allowedDirections = allowedDirections;
         this.routeToBounds = routeToBounds;
         this.style = style;
+        this.dynamicPosition = dynamicPosition;
     }
-    
+
     @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, 
-                               allowedDirections, routeToBounds, style);
+                               allowedDirections, routeToBounds, style, dynamicPosition);
                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 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.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
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.scenegraph.INode;
 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;
-        return new AffineTransform(ti.getTransform());
+        return ti.getTransform();
     }
 
 }
\ 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 org.simantics.databoard.Bindings;
 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.exception.DatabaseException;
 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;
@@ -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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 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) {
-                // 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) {
-        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) {
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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * @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;
 
@@ -101,7 +105,7 @@ public class NodeRequest extends BaseRequest2<Resource, IElement> {
 
                             @Override
                             public void exception(AsyncReadGraph graph, Throwable throwable) {
-                                throwable.printStackTrace();
+                                LOGGER.error("Unexpected error in GetElementClassRequest", throwable);
                                 procedure.execute(graph, null);
                             }
 
@@ -112,7 +116,7 @@ public class NodeRequest extends BaseRequest2<Resource, IElement> {
 
                                     @Override
                                     public void exception(AsyncReadGraph graph, Throwable throwable) {
-                                        throwable.printStackTrace();
+                                        LOGGER.error("Unexpected error in SpawnRequest", throwable);
                                         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) {
-                                                    // TODO: proper logging
-                                                    throwable.printStackTrace();
+                                                    LOGGER.error("Unexpected error in ElementFactory.load (factory=" + factory + ")", throwable);
                                                 }
                                                 @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<>();
-        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);
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.RouteTerminalPosition;
 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.content.ResourceTerminal;
 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.diagram.handler.DataElementMap;
 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.element.handler.TerminalLayout;
 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());
-        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);
@@ -171,7 +176,7 @@ public class RouteGraphUtils {
                         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);
@@ -180,7 +185,7 @@ public class RouteGraphUtils {
                             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)
-                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); 
+        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)
-                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)
@@ -237,19 +243,18 @@ public class RouteGraphUtils {
             if (position.length != 2)
                 position = new double[] { 0, 0 };
 
+            AffineTransform terminalTr = DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement); 
+            final AffineTransform terminalElementTransform = new AffineTransform(terminalTr);
+
             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;
 
@@ -259,20 +264,20 @@ public class RouteGraphUtils {
             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)
-                    System.out.println("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+                    LOGGER.debug("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
             }
             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);
             {
-                Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr);
+                Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalTr);
                 bounds.setFrame(shp.getBounds2D());
             }
 
@@ -285,28 +290,26 @@ public class RouteGraphUtils {
             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 (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)
-                    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;
-                direction = rotateDirection(direction, terminalElementTr);
+                direction = rotateDirection(direction, terminalTr);
             } 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(
@@ -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)
-                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);
 
-            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 );
@@ -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.common.request.BinaryAsyncRead;
 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.utils.datastructures.Callback;
 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);
             }
-        }, 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() {
-            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() {
-            return new ArrayList<IElement>();
+            return new ArrayList<>();
         }
     };
 
@@ -144,7 +144,7 @@ public class TerminalUtil {
             pickCenterY = bounds.getCenterY();
         }
 
-        List<TerminalInfo> result = new ArrayList<TerminalInfo>();
+        List<TerminalInfo> result = new ArrayList<>();
         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;
-                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.
@@ -175,11 +177,10 @@ public class TerminalUtil {
                     pickDist = Point2D.distance(pickCenterX, pickCenterY, terminalToDiagram.getTranslateX(), terminalToDiagram.getTranslateY());
                 }
 
-                AffineTransform terminalToElement = getTerminalPosOnElement(e, t);
                 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;
@@ -224,8 +225,10 @@ public class TerminalUtil {
             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);
-                AffineTransform terminalToDiagram = getTerminalPosOnDiagram(e, t);
                 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.posElem = getTerminalPosOnElement(e, t);
+                result.posElem = terminalToElement != null ? new AffineTransform(terminalToElement) : new AffineTransform();
                 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)
     {
-        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)
+    {
+        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;
@@ -363,7 +375,7 @@ public class TerminalUtil {
         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())
         {
@@ -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
@@ -444,4 +456,11 @@ public class TerminalUtil {
         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 {
        
        /**
-        * Get a copy of terminal transformation (on element)
+        * Get specified terminal transformation on specified element
+        * 
         * @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);
        
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;
-        return new AffineTransform(ti.getTransform());
+        return ti.getTransform();
     }
 
     @Override
index 5e0e830752c5a3869655a6c3101b4311e4c182cf..92738469719e2a0a165a89378879701ab1146d7f 100644 (file)
@@ -95,3 +95,26 @@ getNodeCount diagram = do
     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;
 
+import java.awt.geom.AffineTransform;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 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.scenegraph.g2d.G2DNodeModification;
+import org.simantics.scenegraph.g2d.IG2DNode;
 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;
@@ -32,39 +40,63 @@ import org.simantics.utils.datastructures.Pair;
  * @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
-    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;
-        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 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
-    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;
 
-        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 {
 
-       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;
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);
 
+        rg.updateTerminals();
+
         if (currentAction != null) {
             currentAction.render(g, renderer, mouseX, mouseY);
         } else {