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