]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/ConnectionPointNameStyle.java
Added base diagram profile style for showing terminal names on diagrams
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagram / style / ConnectionPointNameStyle.java
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/ConnectionPointNameStyle.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/ConnectionPointNameStyle.java
new file mode 100644 (file)
index 0000000..a4f32e8
--- /dev/null
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * 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:
+ *     VTT Technical Research Centre of Finland - #7107 original implementation
+ *     Semantum Oy - #7107 adaptation for general use
+ *******************************************************************************/
+package org.simantics.modeling.ui.diagram.style;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.primitiverequest.OrderedSet;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.diagram.adapter.RouteGraphUtils;
+import org.simantics.diagram.connection.RouteGraphConnectionClass;
+import org.simantics.diagram.connection.RouteTerminal;
+import org.simantics.diagram.elements.TextNode;
+import org.simantics.diagram.profile.StyleBase;
+import org.simantics.diagram.synchronization.graph.BasicResources;
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
+import org.simantics.g2d.utils.Alignment;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
+import org.simantics.scenegraph.profile.EvaluationContext;
+import org.simantics.scenegraph.profile.common.ProfileVariables;
+import org.simantics.scenegraph.utils.NodeUtil;
+import org.simantics.utils.datastructures.map.Tuple;
+
+/**
+ * @author Teemu Mätäsniemi
+ * @author Tuukka Lehtonen
+ * @since 1.28.0
+ */
+public class ConnectionPointNameStyle extends StyleBase<List<ConnectionPointNameStyle.Result>> {
+
+    protected static class Result extends Tuple {
+        public Result(String string, AffineTransform tr, Integer direction) {
+            super(string, tr, direction);
+        }
+        public String getString() {
+            return (String) getField(0);
+        }
+        public AffineTransform getTransform() {
+            return (AffineTransform) getField(1);
+        }
+        public Integer getAllowedDirections() {
+            return (Integer) getField(2);
+        }
+    }
+
+    protected static final String PARENT_NODE_NAME_PREFIX = "_tNames";
+    protected static final String NODE_NAME_PREFIX        = "_";
+
+    protected static final Font   FONT = Font.decode("Arial 6");
+
+    protected static final double DEFAULT_SCALE = 0.05;
+
+    private Color backgroundColor = Color.WHITE;
+    private Color textColor = Color.BLACK;
+
+    private double textScale;
+
+    public ConnectionPointNameStyle() {
+        this(DEFAULT_SCALE);
+    }
+
+    public ConnectionPointNameStyle(double textScale) {
+        this.textScale = textScale;
+    }
+
+    @Override
+    public List<Result> calculateStyle(
+            ReadGraph graph,
+            Resource runtimeDiagram,
+            Resource entry,
+            Resource element,
+            Variable configuration)
+                    throws DatabaseException
+    {
+        BasicResources BR = BasicResources.getInstance(graph);
+        Layer0 L0 = BR.L0;
+        ModelingResources MOD = ModelingResources.getInstance(graph);
+
+        Resource comp = graph.getPossibleObject(element, MOD.ElementToComponent);
+        if (comp == null)
+            return Collections.emptyList();
+        String compName = graph.getPossibleRelatedValue(comp, L0.HasName, Bindings.STRING);
+        if (compName == null)
+            return Collections.emptyList();
+
+        // Only process defined elements since those can contain terminal definitions
+        Resource elementType = graph.getPossibleType(element, BR.DIA.DefinedElement);
+        if (elementType == null)
+            return Collections.emptyList();
+
+        // Need parent information to calculate absolute positions of terminals
+        // and to make the result unique for instances of the same symbol.
+        AffineTransform parentTransform = DiagramGraphUtil.getAffineTransform(graph, element);
+        List<Result> result = new ArrayList<>();
+        result.add(new Result(compName, parentTransform, null));
+
+        Resource orderedSet = graph.getPossibleObject(elementType, BR.STR.IsDefinedBy);
+        if (orderedSet != null) {
+            for (Resource el : graph.syncRequest(new OrderedSet(orderedSet))) {
+                Resource gcp = graph.getPossibleObject(el, BR.DIA.HasConnectionPoint);
+                if (gcp != null) {
+                    Resource cpRel = graph.getPossibleObject(gcp, MOD.DiagramConnectionRelationToConnectionRelation);
+                    if (cpRel == null)
+                        continue;
+                    String name = graph.getPossibleRelatedValue(cpRel, L0.HasName, Bindings.STRING);
+                    if (name != null) {
+                        Integer allowedDirections = graph.getPossibleRelatedValue(el, BR.DIA.Terminal_AllowedDirections, Bindings.INTEGER);
+                        result.add(new Result(name, DiagramGraphUtil.getAffineTransform(graph, el), allowedDirections));
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    protected static AffineTransform translateAndScaleIfNeeded(AffineTransform tr, double rotation, double offsetX, double offsetY, double scale) {
+        if (rotation != 0 || offsetX != 0.0 || offsetY != 0.0 || scale != 1.0) {
+            tr = new AffineTransform(tr);
+            if (rotation != 0)
+                tr.rotate(rotation);
+            if (offsetX != 0 || offsetY != 0)
+                tr.translate(offsetX, offsetY);
+            if (scale != 1.0)
+                tr.scale(scale, scale);
+        }
+        return tr;
+    }
+
+    protected AffineTransform getTerminalTransform(AffineTransform transform, double rotation, double offsetX, double offsetY, double scale) {
+        return translateAndScaleIfNeeded(transform, rotation, offsetX, offsetY, scale);
+    }
+
+    @Override
+    public void applyStyleForNode(EvaluationContext observer, INode _node, List<Result> resultList) {
+        // always clean up old items before drawing new items
+        cleanupStyleForNode(_node);
+
+        int count = resultList.size();
+        if (resultList == null || count < 2)
+            return;
+
+        G2DParentNode parentNode = ProfileVariables.claimChild(_node, "", PARENT_NODE_NAME_PREFIX, G2DParentNode.class, observer);
+        parentNode.setTransform(resultList.get(0).getTransform());
+        parentNode.setZIndex(Integer.MAX_VALUE >> 1);
+
+        Rectangle2D eBounds = NodeUtil.getLocalElementBounds(_node);
+
+        for (int i = 1; i < count; ++i) {
+            Result result = resultList.get(i);
+            TextNode node = ProfileVariables.claimChild(parentNode, "", NODE_NAME_PREFIX + i, TextNode.class, observer);
+            node.setZIndex(i);
+            node.setBackgroundColor(backgroundColor);
+            node.setColor(textColor);
+            node.setText(result.getString());
+            node.setVerticalAlignment((byte) Alignment.CENTER.ordinal());
+            node.setAutomaticTextFlipping(TextNode.TextFlipping.VerticalTextDownwards);
+
+            Alignment hAlign = Alignment.LEADING;
+            AffineTransform tr = result.getTransform();
+            double trX = tr.getTranslateX(),
+                   trY = tr.getTranslateY();
+            double dx = 0, dy = 0, r = 0;
+            double ts = 0.6;
+
+            Integer dir = result.getAllowedDirections();
+            int directions = dir != null
+                    ? RouteGraphUtils.rotateDirection(dir, tr)
+                    : RouteGraphConnectionClass.shortestDirectionOutOfBounds(trX, trY, eBounds);
+
+            //System.out.format("%24s: DIR %d (%s)%n", result.getString(), directions, tr.toString());
+
+            if (trX == 0 && trY == 0) {
+                hAlign = Alignment.CENTER;
+            } else {
+                boolean up = (directions & RouteTerminal.DIR_UP) != 0;
+                boolean down = (directions & RouteTerminal.DIR_DOWN) != 0;
+                boolean left = (directions & RouteTerminal.DIR_LEFT) != 0;
+                boolean right = (directions & RouteTerminal.DIR_RIGHT) != 0;
+
+                double ldx = Math.abs(eBounds.getMinX() - trX);
+                double rdx = Math.abs(eBounds.getMaxX() - trX);
+                double tdy = Math.abs(eBounds.getMinY() - trY);
+                double bdy = Math.abs(eBounds.getMaxY() - trY);
+
+                if (left && ldx <= rdx && ldx <= tdy && ldx <= bdy) {
+                    dx = -ts;
+                    hAlign = Alignment.TRAILING;
+                } else if (right && rdx <= ldx && rdx <= tdy && rdx <= bdy) {
+                    dx = ts;
+                    hAlign = Alignment.LEADING;
+                } else if (up && tdy <= ldx && tdy <= rdx && tdy <= bdy) {
+                    dx = -ts;
+                    r = Math.PI/2;
+                    hAlign = Alignment.TRAILING;
+                } else if (down && bdy <= ldx && bdy <= rdx && bdy <= tdy) {
+                    dx = ts;
+                    r = Math.PI/2;
+                    hAlign = Alignment.LEADING;
+                }
+            }
+
+            node.setHorizontalAlignment((byte) hAlign.ordinal());
+            node.setTransform(getTerminalTransform(tr, r, dx, dy, textScale));
+        }
+    }
+
+    @Override
+    protected void cleanupStyleForNode(INode node) {
+        if (node instanceof SingleElementNode) {
+            ProfileVariables.denyChild(node, "", PARENT_NODE_NAME_PREFIX);
+        }
+    }
+
+}