]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/profile/ShowRelatedElements.java
a18191a671e9497151ad3c105e6f10da37821912
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / profile / ShowRelatedElements.java
1 /*******************************************************************************
2  * Copyright (c) 2011 Association for Decentralized Information Management in
3  * 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 - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.diagram.profile;
13
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;
25
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;
56
57 /**
58  * @author Tuukka Lehtonen
59  */
60 public class ShowRelatedElements extends StyleBase<Object> {
61
62     protected Collection<Connection> terminalConnections = new ArrayList<Connection>(4);
63
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);
67
68     protected Line2D line = new Line2D.Double();
69
70     /**
71      * One possible result of
72      * {@link ShowRelatedElements#calculateStyle(ReadGraph, Resource, Resource, Variable)}
73      */
74     public static class Relation extends Tuple {
75         public Relation(String relation,
76                 Resource element, AffineTransform elementTransform,
77                 Resource otherElement, AffineTransform otherElementTransform)
78         {
79             super(relation, element, elementTransform, otherElement, otherElementTransform);
80         }
81         public String getRelation() {
82             return (String) getField(0);
83         }
84         public Resource getElement() {
85             return (Resource) getField(1);
86         }
87         public AffineTransform getTransform() {
88             return (AffineTransform) getField(2);
89         }
90         public Resource getOtherElement() {
91             return (Resource) getField(3);
92         }
93         public AffineTransform getOtherTransform() {
94             return (AffineTransform) getField(4);
95         }
96     }
97
98     /**
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.
102      */
103     @Override
104     public Object calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)
105     throws DatabaseException {
106         DiagramResource DIA = DiagramResource.getInstance(graph);
107
108         if (graph.isInstanceOf(element, DIA.Monitor)) {
109             return calculateFromComponent(graph, runtimeDiagram, element, DIA.HasMonitorComponent);
110         }
111         if (graph.isInstanceOf(element, DIA.Flag)) {
112             return calculateFromFlag(graph, element);
113         }
114
115         // Just return anything, no relationship to visualize.
116         return Boolean.TRUE;
117     }
118
119     protected Object calculateFromFlag(ReadGraph graph, Resource flag) throws DatabaseException {
120         DiagramResource DIA = DiagramResource.getInstance(graph);
121
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);
130             }
131         }
132
133         return Boolean.TRUE;
134     }
135
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);
139
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));
148
149                 AffineTransform parentAt = DiagramGraphUtil.getTransform(graph, parentElement);
150                 return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement, parentAt);
151             }
152         }
153
154         return Boolean.TRUE;
155     }
156
157     protected AffineTransform getConnectionOutputNodePosition(ReadGraph graph, Resource runtime, Resource connection) throws DatabaseException {
158         Statement toTail = ConnectionUtil.getConnectionTailNodeStatement(graph, connection);
159         if (toTail == null)
160             return null;
161
162         AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, toTail.getObject());
163
164         Resource connectionPoint = graph.getPossibleInverse(toTail.getPredicate());
165         if (connectionPoint == null)
166             return at;
167
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);
172         }
173
174         return at;
175     }
176
177     @Override
178     public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Object value) {
179         INode node = map.getNode(item);
180         if (node == null) {
181             evaluationContext.update();
182             return;
183         }
184
185         if (!(value instanceof Relation)) {
186             cleanupStyleForNode(node);
187             return;
188         }
189
190         IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);
191
192         Relation r = (Relation) value;
193
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);
199     }
200
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);
206     }
207
208     /**
209      * @param parentTransform
210      * @param e 
211      * @return origin and bounding box of the specified element in canvas
212      *         coordinate system
213      */
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.
218
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)
224                     continue;
225
226                 BendsHandler bh = c.edge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
227                 if (bh != null) {
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);
232                 } else {
233                     AffineTransform at = parentTransform;
234                     Point2D point = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
235                     return Pair.make(point, null);
236                 }
237             }
238
239             // Fall back to the default logic if connection output terminal
240             // search fails.
241         }
242
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);
250     }
251
252     protected void addCorrespondence(G2DParentNode p, String id,
253             Pair<Point2D, Rectangle2D> p1,
254             Pair<Point2D, Rectangle2D> p2,
255             Stroke lineStroke, Color lineColor)
256     {
257         RelationshipNode2 node = p.getOrCreateNode(id, RelationshipNode2.class);
258         node.setZIndex(-10);
259
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());
264         }
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());
269         }
270
271         node.init(lineStroke, lineColor, p1.first, p2.first);
272     }
273
274     @Override
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()); 
279     }
280
281     @Override
282     public String toString() {
283         return "show related elements";
284     }
285
286 }