From 7f206f186acf9bb44f86f5b191c38cddb4508c48 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Tue, 28 Mar 2017 18:02:18 +0300 Subject: [PATCH] Added base diagram profile style for showing terminal names on diagrams Previously there has been an implementation for showing terminal names in the symbol editor (SymbolTerminalNameStyle). This implementation is for showing symbol instance terminal names on modelling diagrams containing symbol instances. The names are positioned outside the current symbol's bounding box. ConnectionPointNameStyle also employs the new TextNode automatic text flipping feature added in #7112. refs #7107 Change-Id: Icf4709bf115a9c25b894192d186c6ce514cc0d29 --- .../style/ConnectionPointNameStyle.java | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 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 index 000000000..a4f32e811 --- /dev/null +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/ConnectionPointNameStyle.java @@ -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> { + + 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 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 = 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 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); + } + } + +} -- 2.43.2