1 /*******************************************************************************
2 * Copyright (c) 2017 Association for Decentralized Information Management
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
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;
15 import java.awt.Color;
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;
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;
48 * @author Teemu Mätäsniemi
49 * @author Tuukka Lehtonen
52 public class ConnectionPointNameStyle extends StyleBase<List<ConnectionPointNameStyle.Result>> {
54 protected static class Result extends Tuple {
55 public Result(String string, AffineTransform tr, Integer direction) {
56 super(string, tr, direction);
58 public String getString() {
59 return (String) getField(0);
61 public AffineTransform getTransform() {
62 return (AffineTransform) getField(1);
64 public Integer getAllowedDirections() {
65 return (Integer) getField(2);
69 protected static final String PARENT_NODE_NAME_PREFIX = "_tNames";
70 protected static final String NODE_NAME_PREFIX = "_";
72 protected static final Font FONT = Font.decode("Arial 6");
74 protected static final double DEFAULT_SCALE = 0.05;
76 private Color backgroundColor = Color.WHITE;
77 private Color textColor = Color.BLACK;
79 private double textScale;
81 public ConnectionPointNameStyle() {
85 public ConnectionPointNameStyle(double textScale) {
86 this.textScale = textScale;
90 public List<Result> calculateStyle(
92 Resource runtimeDiagram,
95 Variable configuration)
96 throws DatabaseException
98 BasicResources BR = BasicResources.getInstance(graph);
100 ModelingResources MOD = ModelingResources.getInstance(graph);
102 Resource comp = graph.getPossibleObject(element, MOD.ElementToComponent);
104 return Collections.emptyList();
105 String compName = graph.getPossibleRelatedValue(comp, L0.HasName, Bindings.STRING);
106 if (compName == null)
107 return Collections.emptyList();
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();
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));
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);
125 Resource cpRel = graph.getPossibleObject(gcp, MOD.DiagramConnectionRelationToConnectionRelation);
128 String name = graph.getPossibleRelatedValue(cpRel, L0.HasName, Bindings.STRING);
130 Integer allowedDirections = graph.getPossibleRelatedValue(el, BR.DIA.Terminal_AllowedDirections, Bindings.INTEGER);
131 result.add(new Result(name, DiagramGraphUtil.getAffineTransform(graph, el), allowedDirections));
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);
145 if (offsetX != 0 || offsetY != 0)
146 tr.translate(offsetX, offsetY);
148 tr.scale(scale, scale);
153 protected AffineTransform getTerminalTransform(AffineTransform transform, double rotation, double offsetX, double offsetY, double scale) {
154 return translateAndScaleIfNeeded(transform, rotation, offsetX, offsetY, scale);
158 public void applyStyleForNode(EvaluationContext observer, INode _node, List<Result> resultList) {
159 // always clean up old items before drawing new items
160 cleanupStyleForNode(_node);
162 int count = resultList != null ? resultList.size() : 0;
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);
170 Rectangle2D eBounds = NodeUtil.getLocalElementBounds(_node);
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);
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);
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;
189 Integer dir = result.getAllowedDirections();
190 int directions = dir != null
191 ? RouteGraphUtils.rotateDirection(dir, tr)
192 : RouteGraphConnectionClass.shortestDirectionOutOfBounds(trX, trY, eBounds);
194 //System.out.format("%24s: DIR %d (%s)%n", result.getString(), directions, tr.toString());
196 if (trX == 0 && trY == 0) {
197 hAlign = Alignment.CENTER;
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;
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);
209 if (left && ldx <= rdx && ldx <= tdy && ldx <= bdy) {
211 hAlign = Alignment.TRAILING;
212 } else if (right && rdx <= ldx && rdx <= tdy && rdx <= bdy) {
214 hAlign = Alignment.LEADING;
215 } else if (up && tdy <= ldx && tdy <= rdx && tdy <= bdy) {
218 hAlign = Alignment.TRAILING;
219 } else if (down && bdy <= ldx && bdy <= rdx && bdy <= tdy) {
222 hAlign = Alignment.LEADING;
226 node.setHorizontalAlignment((byte) hAlign.ordinal());
227 node.setTransform(getTerminalTransform(tr, r, dx, dy, textScale));
232 protected void cleanupStyleForNode(INode node) {
233 if (node instanceof SingleElementNode) {
234 ProfileVariables.denyChild(node, "", PARENT_NODE_NAME_PREFIX);