]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/NetworkDrawingNode.java
Support copying of attributes from DN.MappedComponent
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / nodes / NetworkDrawingNode.java
1 package org.simantics.district.network.ui.nodes;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Graphics2D;
6 import java.awt.Stroke;
7 import java.awt.geom.AffineTransform;
8 import java.awt.geom.Path2D;
9 import java.awt.geom.Point2D;
10 import java.awt.geom.Rectangle2D;
11 import java.util.ArrayList;
12 import java.util.Iterator;
13 import java.util.List;
14
15 import org.simantics.Simantics;
16 import org.simantics.db.Resource;
17 import org.simantics.db.WriteGraph;
18 import org.simantics.db.common.request.WriteRequest;
19 import org.simantics.db.exception.DatabaseException;
20 import org.simantics.db.request.Write;
21 import org.simantics.diagram.elements.DiagramNodeUtil;
22 import org.simantics.diagram.ui.DiagramModelHints;
23 import org.simantics.district.network.DistrictNetworkUtil;
24 import org.simantics.district.network.ModelledCRS;
25 import org.simantics.district.network.ontology.DistrictNetworkResource;
26 import org.simantics.district.network.ui.DNEdgeBuilder;
27 import org.simantics.district.network.ui.NetworkDrawingParticipant;
28 import org.simantics.g2d.canvas.Hints;
29 import org.simantics.g2d.canvas.ICanvasContext;
30 import org.simantics.g2d.canvas.IToolMode;
31 import org.simantics.g2d.diagram.IDiagram;
32 import org.simantics.scenegraph.g2d.G2DNode;
33 import org.simantics.scenegraph.g2d.events.EventTypes;
34 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
35 import org.simantics.scenegraph.g2d.events.MouseEvent;
36 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
37 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;
38 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
39 import org.simantics.scenegraph.utils.GeometryUtils;
40 import org.simantics.scenegraph.utils.NodeUtil;
41
42 public class NetworkDrawingNode extends G2DNode {
43
44     static class DrawingNode {
45
46         private List<Point2D> routeNodes = new ArrayList<>();
47     }
48
49     private static final long serialVersionUID = -3475301184009620573L;
50     
51     private Point2D currentMousePos = null;
52
53     private List<DrawingNode> nodes = new ArrayList<>();
54     private DrawingNode currentRouteNode = null;
55
56     private Resource diagramResource;
57
58     private NetworkDrawingParticipant participant;
59
60     private IDiagram diagram;
61
62     private static final Stroke DASHED_STROKE = new BasicStroke(2.0f,
63             BasicStroke.CAP_ROUND,
64             BasicStroke.JOIN_ROUND,
65             4.0f, new float[]{4.0f}, 0.0f);
66
67     private static final Color BLUE_ALPHA = new Color(0, 0, 255, 100);
68     private static final Color RED_ALPHA = new Color(255, 0, 0, 100);
69
70     private boolean scaleStroke = true;
71     
72     @Override
73     public void init() {
74         super.init();
75         addEventHandler(this);
76     }
77     
78     public void setNetworkDrawingParticipant(NetworkDrawingParticipant participant) {
79         this.participant = participant;
80     }
81     
82     public void setDiagram(IDiagram diagram) {
83         if (diagram != null) {
84             this.diagram = diagram;
85             this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
86         }
87     }
88
89     @Override
90     public void render(Graphics2D g2d) {
91         if (nodes.isEmpty())
92             return;
93
94         Color old = g2d.getColor();
95         Stroke oldStroke = g2d.getStroke();
96
97         Iterator<DrawingNode> dnodeIterator = nodes.iterator();
98         while (dnodeIterator.hasNext()) {
99             Path2D path = new Path2D.Double();
100             DrawingNode dnode = dnodeIterator.next();
101             Iterator<Point2D> nodeIter = dnode.routeNodes.iterator();
102             if (nodeIter.hasNext()) {
103                 Point2D node = nodeIter.next();
104                 path.moveTo(node.getX(), node.getY());
105             }
106             while (nodeIter.hasNext()) {
107                 Point2D node = nodeIter.next();
108                 path.lineTo(node.getX(), node.getY());
109             }
110             if (!dnodeIterator.hasNext()) {
111                 if (currentMousePos != null)
112                     path.lineTo(currentMousePos.getX(), currentMousePos.getY());
113             }
114             
115             if (DASHED_STROKE != null) {
116                 if (scaleStroke && DASHED_STROKE instanceof BasicStroke) {
117                     BasicStroke bs = GeometryUtils.scaleStroke(DASHED_STROKE, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform())));
118                     g2d.setStroke(bs);
119                 } else {
120                     g2d.setStroke(DASHED_STROKE);
121                 }
122             }
123             
124             g2d.setColor(BLUE_ALPHA);
125             g2d.draw(path);
126             
127             g2d.setColor(RED_ALPHA);
128             BasicStroke stroke = GeometryUtils.scaleStroke(DASHED_STROKE, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform())));
129             g2d.setStroke(stroke);
130             Point2D currentPoint = path.getCurrentPoint();
131             g2d.draw(new Rectangle2D.Double(currentPoint.getX() - 0.0001 / 2, currentPoint.getY() - 0.0001 / 2, 0.0001, 0.0001));
132         }
133         
134         g2d.setStroke(oldStroke);
135         g2d.setColor(old);
136     }
137
138     @Override
139     public Rectangle2D getBoundsInLocal() {
140         return null;
141     }
142     
143     @Override
144     public int getEventMask() {
145         return EventTypes.AnyMask;
146     }
147     
148     @Override
149     protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {
150         // nodes to path2d
151         IToolMode mode = getToolMode();
152         if (mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) {
153             // ok, new routenode starts from here
154             Point2D localPos = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
155             Point2D.Double pos = new Point2D.Double(localPos.getX(), localPos.getY());
156             if (currentRouteNode != null) {
157                 //currentRouteNode.routeNodes.add(pos);
158                 currentRouteNode = new DrawingNode();
159                 currentRouteNode.routeNodes.add(pos);
160                 nodes.add(currentRouteNode);
161             } else {
162                 // ok, this must be creation of dh_point
163                 double scale = getTransform().getScaleY();
164                 double x = ModelledCRS.xToLongitude(pos.getX() / scale);
165                 double y = ModelledCRS.yToLatitude(-pos.getY() / scale);
166                 Simantics.getSession().asyncRequest(new Write() {
167                     
168                     @Override
169                     public void perform(WriteGraph graph) throws DatabaseException {
170                         graph.markUndoPoint();
171                         Resource defaultMapping = graph.getSingleObject(diagramResource, DistrictNetworkResource.getInstance(graph).VertexDefaultMapping);
172                         DistrictNetworkUtil.createVertex(graph, diagramResource, new double[] { x, y }, 0, defaultMapping); // TODO: elevation can be fetched from e.g. elevation API
173                     }
174                 });
175             }
176             repaint();
177             return true;
178         }
179         return super.mouseDoubleClicked(e);
180     }
181
182     private void createEdge(DrawingNode node) {
183         
184         Point2D start = node.routeNodes.get(0);
185         Point2D end = node.routeNodes.get(node.routeNodes.size() - 1);
186         
187         double currentPadding = DistrictNetworkVertexNode.width;
188         AffineTransform test = getTransform();
189         ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(this);
190         AffineTransform tr = ctx.getHintStack().getHint(Hints.KEY_CANVAS_TRANSFORM);
191         AffineTransform testing = new AffineTransform(tr); 
192         testing.concatenate(test);
193         double calculateScaleRecip = DistrictNetworkNodeUtils.calculateScaleRecip(testing);
194         double padding = currentPadding * calculateScaleRecip;
195         /*
196          *  To convert y-coordinates to map coordinates in ruler, use:
197          *    double val = (y-offsetY)/scaleY;
198          *    val = Math.toDegrees(Math.atan(Math.sinh(Math.toRadians(val))));
199          *    String str = formatValue(val);
200          */
201         // TODO: fix scale
202         double scaleY = getTransform().getScaleY();
203         double scaleX = getTransform().getScaleX();
204         
205         double startLat = ModelledCRS.yToLatitude(-start.getY() / scaleY);
206         double startLon = ModelledCRS.xToLongitude(start.getX() / scaleX);
207         
208         double endLat = ModelledCRS.yToLatitude(-end.getY() / scaleY);
209         double endLon = ModelledCRS.xToLongitude(end.getX() / scaleX);
210         
211         double[] startCoords = new double[] { startLon, startLat };
212         double[] endCoords = new double[] { endLon, endLat };
213         
214         double[] detailedGeometryCoords = new double[node.routeNodes.size() * 2];
215         int i = 0;
216         for (Point2D p : node.routeNodes) {
217             double lat = ModelledCRS.yToLatitude(-p.getY() / scaleY);
218             double lon = ModelledCRS.xToLongitude(p.getX() / scaleX);
219             detailedGeometryCoords[i++] = lon;
220             detailedGeometryCoords[i++] = lat;
221         }
222         
223         DNEdgeBuilder builder = new DNEdgeBuilder(diagramResource, diagram);
224         Simantics.getSession().asyncRequest(new WriteRequest() {
225             
226             @Override
227             public void perform(WriteGraph graph) throws DatabaseException {
228                 builder.create(graph, startCoords, 0, endCoords, 0, detailedGeometryCoords, padding);
229             }
230         });
231         
232     }
233
234     @Override
235     protected boolean mouseClicked(MouseClickEvent e) {
236         // check ToolMode
237         IToolMode mode = getToolMode();
238         if (mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) {
239             if (e.button == MouseEvent.RIGHT_BUTTON && !nodes.isEmpty()) {
240                 nodes.remove(nodes.size() - 1);
241             } else if (e.button == MouseEvent.LEFT_BUTTON) {
242                 Point2D localPos = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
243                 if (currentRouteNode == null && canStartEdge(localPos)) {
244                     // ok, we can start from here
245                     currentRouteNode = new DrawingNode();
246                     currentRouteNode.routeNodes.add(new Point2D.Double(localPos.getX(), localPos.getY()));
247                     nodes.add(currentRouteNode);
248                 } else if (currentRouteNode != null && canStartEdge(localPos)) {
249                     // let's commit our new routenode
250                     currentRouteNode.routeNodes.add(new Point2D.Double(localPos.getX(), localPos.getY()));
251                     Iterator<DrawingNode> nodeIter = nodes.iterator();
252                     while (nodeIter.hasNext()) {
253                         createEdge(nodeIter.next());
254                     }
255                     currentRouteNode = null;
256                     nodes.clear();
257                 } else if (currentRouteNode != null) {
258                     currentRouteNode.routeNodes.add(new Point2D.Double(localPos.getX(), localPos.getY()));
259                 }
260             }
261             repaint();
262             return true;
263         }
264         return super.mouseClicked(e);
265     }
266
267     private boolean canStartEdge(Point2D currentPos) {
268         return participant.isHoveringOverNode(currentPos);
269     }
270
271     private IToolMode getToolMode() {
272         return participant.getHint(Hints.KEY_TOOL);
273     }
274
275     @Override
276     protected boolean mouseMoved(MouseMovedEvent e) {
277         IToolMode mode = getToolMode();
278         boolean repaint = false;
279         Point2D p = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
280         boolean isConnectionTool = mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK);
281         if (participant.pickHoveredElement(p, isConnectionTool)) {
282             repaint = true;
283         }
284         if (!nodes.isEmpty()) {
285             currentMousePos = p;
286             
287             repaint();
288             return true;
289         }
290         currentMousePos = null;
291         if (repaint == true)
292             repaint();
293         return super.mouseMoved(e);
294     }
295     
296     @Override
297     protected boolean keyPressed(KeyPressedEvent e) {
298         if (e.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {
299             currentRouteNode = null;
300             nodes.clear();
301             repaint();
302             return true;
303         }
304         return super.keyPressed(e);
305             
306     }
307 }