1 /*******************************************************************************
2 * Copyright (c) 2011 Association for Decentralized Information Management in
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 - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.profile;
14 import java.awt.BasicStroke;
15 import java.awt.Color;
16 import java.awt.Stroke;
17 import java.awt.geom.AffineTransform;
18 import java.awt.geom.Line2D;
19 import java.awt.geom.Point2D;
20 import java.awt.geom.Rectangle2D;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.List;
26 import org.simantics.db.ReadGraph;
27 import org.simantics.db.Resource;
28 import org.simantics.db.Statement;
29 import org.simantics.db.common.utils.OrderedSetUtils;
30 import org.simantics.db.exception.DatabaseException;
31 import org.simantics.db.layer0.variable.Variable;
32 import org.simantics.diagram.content.ConnectionUtil;
33 import org.simantics.diagram.flag.FlagUtil;
34 import org.simantics.diagram.stubs.DiagramResource;
35 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
36 import org.simantics.g2d.connection.handler.ConnectionHandler;
37 import org.simantics.g2d.diagram.IDiagram;
38 import org.simantics.g2d.diagram.handler.DataElementMap;
39 import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;
40 import org.simantics.g2d.diagram.handler.Relationship;
41 import org.simantics.g2d.diagram.handler.Topology.Connection;
42 import org.simantics.g2d.element.ElementUtils;
43 import org.simantics.g2d.element.IElement;
44 import org.simantics.g2d.element.handler.BendsHandler;
45 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
46 import org.simantics.g2d.utils.GeometryUtils;
47 import org.simantics.modeling.ModelingResources;
48 import org.simantics.scenegraph.INode;
49 import org.simantics.scenegraph.g2d.G2DParentNode;
50 import org.simantics.scenegraph.g2d.nodes.RelationshipNode2;
51 import org.simantics.scenegraph.profile.DataNodeMap;
52 import org.simantics.scenegraph.profile.EvaluationContext;
53 import org.simantics.scenegraph.profile.common.ProfileVariables;
54 import org.simantics.utils.datastructures.Pair;
55 import org.simantics.utils.datastructures.map.Tuple;
58 * @author Tuukka Lehtonen
60 public class ShowRelatedElements extends StyleBase<Object> {
62 protected Collection<Connection> terminalConnections = new ArrayList<Connection>(4);
64 protected final BasicStroke highlightStroke = new BasicStroke(1.0f,
65 BasicStroke.CAP_SQUARE, BasicStroke.CAP_SQUARE, 10.0f,
66 new float[] { 4.0f, 6.0f }, 0);
68 protected Line2D line = new Line2D.Double();
71 * One possible result of
72 * {@link ShowRelatedElements#calculateStyle(ReadGraph, Resource, Resource, Variable)}
74 public static class Relation extends Tuple {
75 public Relation(String relation,
76 Resource element, AffineTransform elementTransform,
77 Resource otherElement, AffineTransform otherElementTransform)
79 super(relation, element, elementTransform, otherElement, otherElementTransform);
81 public String getRelation() {
82 return (String) getField(0);
84 public Resource getElement() {
85 return (Resource) getField(1);
87 public AffineTransform getTransform() {
88 return (AffineTransform) getField(2);
90 public Resource getOtherElement() {
91 return (Resource) getField(3);
93 public AffineTransform getOtherTransform() {
94 return (AffineTransform) getField(4);
99 * Calculates a result that is contains the styled element, its parent and
100 * transforms of both. This takes care of the style updating the visuals
101 * properly every time the related components move.
104 public Object calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)
105 throws DatabaseException {
106 DiagramResource DIA = DiagramResource.getInstance(graph);
108 if (graph.isInstanceOf(element, DIA.Monitor)) {
109 return calculateFromComponent(graph, runtimeDiagram, element, DIA.HasMonitorComponent);
111 if (graph.isInstanceOf(element, DIA.Flag)) {
112 return calculateFromFlag(graph, element);
115 // Just return anything, no relationship to visualize.
119 protected Object calculateFromFlag(ReadGraph graph, Resource flag) throws DatabaseException {
120 DiagramResource DIA = DiagramResource.getInstance(graph);
122 Resource relatedFlag = FlagUtil.getPossibleCounterpart(graph, flag);
123 if (relatedFlag != null) {
124 Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, flag, DIA.Diagram);
125 Collection<Resource> relatedDiagrams = OrderedSetUtils.getOwnerLists(graph, relatedFlag, DIA.Diagram);
126 if (!Collections.disjoint(flagDiagrams, relatedDiagrams)) {
127 AffineTransform flagAt = DiagramGraphUtil.getTransform(graph, flag);
128 AffineTransform relatedFlagAt = DiagramGraphUtil.getTransform(graph, relatedFlag);
129 return new Relation(Relationship.RELATED_TO.toString(), flag, flagAt, relatedFlag, relatedFlagAt);
136 protected Object calculateFromComponent(ReadGraph graph, Resource runtime, Resource element, Resource elementToComponentRelation) throws DatabaseException {
137 DiagramResource DIA = DiagramResource.getInstance(graph);
138 ModelingResources MOD = ModelingResources.getInstance(graph);
140 Resource parentComponent = graph.getPossibleObject(element, elementToComponentRelation);
141 if (parentComponent != null) {
142 Resource parentElement = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
143 if (parentElement != null) {
144 AffineTransform at = DiagramGraphUtil.getTransform(graph, element);
145 if (graph.isInstanceOf(parentElement, DIA.Connection))
146 return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement,
147 getConnectionOutputNodePosition(graph, runtime, parentElement));
149 AffineTransform parentAt = DiagramGraphUtil.getTransform(graph, parentElement);
150 return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement, parentAt);
157 protected AffineTransform getConnectionOutputNodePosition(ReadGraph graph, Resource runtime, Resource connection) throws DatabaseException {
158 Statement toTail = ConnectionUtil.getConnectionTailNodeStatement(graph, connection);
162 AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, toTail.getObject());
164 Resource connectionPoint = graph.getPossibleInverse(toTail.getPredicate());
165 if (connectionPoint == null)
168 DiagramResource DIA = DiagramResource.getInstance(graph);
169 for (Resource terminal : graph.getObjects(connectionPoint, DIA.HasConnectionPoint_Inverse)) {
170 AffineTransform terminalAt = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, terminal);
171 at.concatenate(terminalAt);
178 public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Object value) {
179 INode node = map.getNode(item);
181 evaluationContext.update();
185 if (!(value instanceof Relation)) {
186 cleanupStyleForNode(node);
190 IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);
192 Relation r = (Relation) value;
194 DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
195 IElement element = emap.getElement(diagram, item);
196 IElement otherElement = emap.getElement(diagram, r.getOtherElement());
197 if (otherElement != null)
198 addCorrespondence(r, (G2DParentNode) node, r.getRelation(), element, otherElement, highlightStroke, Color.GRAY);
201 protected void addCorrespondence(Relation r, G2DParentNode p, String id, IElement from, IElement to, Stroke lineStroke, Color lineColor) {
202 Pair<Point2D, Rectangle2D> p1 = toPoint(r.getTransform(), from);
203 Pair<Point2D, Rectangle2D> p2 = toPoint(r.getOtherTransform(), to);
204 if (p1 != null && p2 != null)
205 addCorrespondence(p, id, p1, p2, lineStroke, lineColor);
209 * @param parentTransform
211 * @return origin and bounding box of the specified element in canvas
214 protected Pair<Point2D, Rectangle2D> toPoint(AffineTransform parentTransform, IElement e) {
215 if (PickFilter.FILTER_CONNECTIONS.accept(e)) {
216 // For connections select the first found output terminal position as
217 // the correspondence visualization point.
219 ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);
220 terminalConnections.clear();
221 ch.getTerminalConnections(e, terminalConnections);
222 for (Connection c : terminalConnections) {
223 if (c.end != EdgeEnd.Begin)
226 BendsHandler bh = c.edge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
228 List<Point2D> points = new ArrayList<Point2D>();
229 GeometryUtils.getPoints(bh.getPath(c.edge), points);
230 if (!points.isEmpty())
231 return Pair.make(points.get(0), null);
233 AffineTransform at = parentTransform;
234 Point2D point = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
235 return Pair.make(point, null);
239 // Fall back to the default logic if connection output terminal
243 // Using element origin prevents the relationship rubber band target
244 // from changing every time the content of the node changes, e.g. the
245 // text of a monitor.
246 AffineTransform at = ElementUtils.getTransform(e);
247 Point2D p = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
248 Rectangle2D r = ElementUtils.getElementBoundsOnDiagram(e).getBounds2D();
249 return Pair.make(p, r);
252 protected void addCorrespondence(G2DParentNode p, String id,
253 Pair<Point2D, Rectangle2D> p1,
254 Pair<Point2D, Rectangle2D> p2,
255 Stroke lineStroke, Color lineColor)
257 RelationshipNode2 node = p.getOrCreateNode(id, RelationshipNode2.class);
260 if (p1.second != null) {
261 line.setLine(p1.first, p2.first);
262 if (LineUtilities.clipLine(line, p1.second))
263 p1.first.setLocation(line.getX2(), line.getY2());
265 if (p2.second != null) {
266 line.setLine(p1.first, p2.first);
267 if (LineUtilities.clipLine(line, p2.second))
268 p2.first.setLocation(line.getX1(), line.getY1());
271 node.init(lineStroke, lineColor, p1.first, p2.first);
275 protected void cleanupStyleForNode(INode node) {
276 ProfileVariables.denyChild(node, "", Relationship.CHILD_OF.toString());
277 //ProfileVariables.denyChild(node, "", Relationship.PARENT_OF.toString());
278 ProfileVariables.denyChild(node, "", Relationship.RELATED_TO.toString());
282 public String toString() {
283 return "show related elements";