]> gerrit.simantics Code Review - simantics/platform.git/blob
d86925e485d187ef8d48e25535a091ffbcb5facd
[simantics/platform.git] /
1 /*******************************************************************************
2  * Copyright (c) 2017 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - #7107 original implementation
11  *     Semantum Oy - #7107 adaptation for general use
12  *******************************************************************************/
13 package org.simantics.modeling.ui.diagram.style;
14
15 import java.awt.Color;
16 import java.awt.Font;
17 import java.awt.geom.AffineTransform;
18 import java.awt.geom.Rectangle2D;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.simantics.databoard.Bindings;
24 import org.simantics.db.ReadGraph;
25 import org.simantics.db.Resource;
26 import org.simantics.db.common.primitiverequest.OrderedSet;
27 import org.simantics.db.exception.DatabaseException;
28 import org.simantics.db.layer0.variable.Variable;
29 import org.simantics.diagram.adapter.RouteGraphUtils;
30 import org.simantics.diagram.connection.RouteGraphConnectionClass;
31 import org.simantics.diagram.connection.RouteTerminal;
32 import org.simantics.diagram.elements.TextNode;
33 import org.simantics.diagram.profile.StyleBase;
34 import org.simantics.diagram.synchronization.graph.BasicResources;
35 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
36 import org.simantics.g2d.utils.Alignment;
37 import org.simantics.layer0.Layer0;
38 import org.simantics.modeling.ModelingResources;
39 import org.simantics.scenegraph.INode;
40 import org.simantics.scenegraph.g2d.G2DParentNode;
41 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
42 import org.simantics.scenegraph.profile.EvaluationContext;
43 import org.simantics.scenegraph.profile.common.ProfileVariables;
44 import org.simantics.scenegraph.utils.NodeUtil;
45 import org.simantics.utils.datastructures.map.Tuple;
46
47 /**
48  * @author Teemu Mätäsniemi
49  * @author Tuukka Lehtonen
50  * @since 1.28.0
51  */
52 public class ConnectionPointNameStyle extends StyleBase<List<ConnectionPointNameStyle.Result>> {
53
54     protected static class Result extends Tuple {
55         public Result(String string, AffineTransform tr, Integer direction) {
56             super(string, tr, direction);
57         }
58         public String getString() {
59             return (String) getField(0);
60         }
61         public AffineTransform getTransform() {
62             return (AffineTransform) getField(1);
63         }
64         public Integer getAllowedDirections() {
65             return (Integer) getField(2);
66         }
67     }
68
69     protected static final String PARENT_NODE_NAME_PREFIX = "_tNames";
70     protected static final String NODE_NAME_PREFIX        = "_";
71
72     protected static final Font   FONT = Font.decode("Arial 6");
73
74     protected static final double DEFAULT_SCALE = 0.05;
75
76     private Color backgroundColor = Color.WHITE;
77     private Color textColor = Color.BLACK;
78
79     private double textScale;
80
81     public ConnectionPointNameStyle() {
82         this(DEFAULT_SCALE);
83     }
84
85     public ConnectionPointNameStyle(double textScale) {
86         this.textScale = textScale;
87     }
88
89     @Override
90     public List<Result> calculateStyle(
91             ReadGraph graph,
92             Resource runtimeDiagram,
93             Resource entry,
94             Resource element,
95             Variable configuration)
96                     throws DatabaseException
97     {
98         BasicResources BR = BasicResources.getInstance(graph);
99         Layer0 L0 = BR.L0;
100         ModelingResources MOD = ModelingResources.getInstance(graph);
101
102         Resource comp = graph.getPossibleObject(element, MOD.ElementToComponent);
103         if (comp == null)
104             return Collections.emptyList();
105         String compName = graph.getPossibleRelatedValue(comp, L0.HasName, Bindings.STRING);
106         if (compName == null)
107             return Collections.emptyList();
108
109         // Only process defined elements since those can contain terminal definitions
110         Resource elementType = graph.getPossibleType(element, BR.DIA.DefinedElement);
111         if (elementType == null)
112             return Collections.emptyList();
113
114         // Need parent information to calculate absolute positions of terminals
115         // and to make the result unique for instances of the same symbol.
116         AffineTransform parentTransform = DiagramGraphUtil.getAffineTransform(graph, element);
117         List<Result> result = new ArrayList<>();
118         result.add(new Result(compName, parentTransform, null));
119
120         Resource orderedSet = graph.getPossibleObject(elementType, BR.STR.IsDefinedBy);
121         if (orderedSet != null) {
122             for (Resource el : graph.syncRequest(new OrderedSet(orderedSet))) {
123                 Resource gcp = graph.getPossibleObject(el, BR.DIA.HasConnectionPoint);
124                 if (gcp != null) {
125                     Resource cpRel = graph.getPossibleObject(gcp, MOD.DiagramConnectionRelationToConnectionRelation);
126                     if (cpRel == null)
127                         continue;
128                     String name = graph.getPossibleRelatedValue(cpRel, L0.HasName, Bindings.STRING);
129                     if (name != null) {
130                         Integer allowedDirections = graph.getPossibleRelatedValue(el, BR.DIA.Terminal_AllowedDirections, Bindings.INTEGER);
131                         result.add(new Result(name, DiagramGraphUtil.getAffineTransform(graph, el), allowedDirections));
132                     }
133                 }
134             }
135         }
136
137         return result;
138     }
139
140     protected static AffineTransform translateAndScaleIfNeeded(AffineTransform tr, double rotation, double offsetX, double offsetY, double scale) {
141         if (rotation != 0 || offsetX != 0.0 || offsetY != 0.0 || scale != 1.0) {
142             tr = new AffineTransform(tr);
143             if (rotation != 0)
144                 tr.rotate(rotation);
145             if (offsetX != 0 || offsetY != 0)
146                 tr.translate(offsetX, offsetY);
147             if (scale != 1.0)
148                 tr.scale(scale, scale);
149         }
150         return tr;
151     }
152
153     protected AffineTransform getTerminalTransform(AffineTransform transform, double rotation, double offsetX, double offsetY, double scale) {
154         return translateAndScaleIfNeeded(transform, rotation, offsetX, offsetY, scale);
155     }
156
157     @Override
158     public void applyStyleForNode(EvaluationContext observer, INode _node, List<Result> resultList) {
159         // always clean up old items before drawing new items
160         cleanupStyleForNode(_node);
161
162         int count = resultList != null ? resultList.size() : 0;
163         if (count < 2)
164             return;
165
166         G2DParentNode parentNode = ProfileVariables.claimChild(_node, "", PARENT_NODE_NAME_PREFIX, G2DParentNode.class, observer);
167         parentNode.setTransform(resultList.get(0).getTransform());
168         parentNode.setZIndex(Integer.MAX_VALUE >> 1);
169
170         Rectangle2D eBounds = NodeUtil.getLocalElementBounds(_node);
171
172         for (int i = 1; i < count; ++i) {
173             Result result = resultList.get(i);
174             TextNode node = ProfileVariables.claimChild(parentNode, "", NODE_NAME_PREFIX + i, TextNode.class, observer);
175             node.setZIndex(i);
176             node.setBackgroundColor(backgroundColor);
177             node.setColor(textColor);
178             node.setText(result.getString());
179             node.setVerticalAlignment((byte) Alignment.CENTER.ordinal());
180             node.setAutomaticTextFlipping(TextNode.TextFlipping.VerticalTextDownwards);
181
182             Alignment hAlign = Alignment.LEADING;
183             AffineTransform tr = result.getTransform();
184             double trX = tr.getTranslateX(),
185                    trY = tr.getTranslateY();
186             double dx = 0, dy = 0, r = 0;
187             double ts = 0.6;
188
189             Integer dir = result.getAllowedDirections();
190             int directions = dir != null
191                     ? RouteGraphUtils.rotateDirection(dir, tr)
192                     : RouteGraphConnectionClass.shortestDirectionOutOfBounds(trX, trY, eBounds);
193
194             //System.out.format("%24s: DIR %d (%s)%n", result.getString(), directions, tr.toString());
195
196             if (trX == 0 && trY == 0) {
197                 hAlign = Alignment.CENTER;
198             } else {
199                 boolean up = (directions & RouteTerminal.DIR_UP) != 0;
200                 boolean down = (directions & RouteTerminal.DIR_DOWN) != 0;
201                 boolean left = (directions & RouteTerminal.DIR_LEFT) != 0;
202                 boolean right = (directions & RouteTerminal.DIR_RIGHT) != 0;
203
204                 double ldx = Math.abs(eBounds.getMinX() - trX);
205                 double rdx = Math.abs(eBounds.getMaxX() - trX);
206                 double tdy = Math.abs(eBounds.getMinY() - trY);
207                 double bdy = Math.abs(eBounds.getMaxY() - trY);
208
209                 if (left && ldx <= rdx && ldx <= tdy && ldx <= bdy) {
210                     dx = -ts;
211                     hAlign = Alignment.TRAILING;
212                 } else if (right && rdx <= ldx && rdx <= tdy && rdx <= bdy) {
213                     dx = ts;
214                     hAlign = Alignment.LEADING;
215                 } else if (up && tdy <= ldx && tdy <= rdx && tdy <= bdy) {
216                     dx = -ts;
217                     r = Math.PI/2;
218                     hAlign = Alignment.TRAILING;
219                 } else if (down && bdy <= ldx && bdy <= rdx && bdy <= tdy) {
220                     dx = ts;
221                     r = Math.PI/2;
222                     hAlign = Alignment.LEADING;
223                 }
224             }
225
226             node.setHorizontalAlignment((byte) hAlign.ordinal());
227             node.setTransform(getTerminalTransform(tr, r, dx, dy, textScale));
228         }
229     }
230
231     @Override
232     protected void cleanupStyleForNode(INode node) {
233         if (node instanceof SingleElementNode) {
234             ProfileVariables.denyChild(node, "", PARENT_NODE_NAME_PREFIX);
235         }
236     }
237
238 }