1 package org.simantics.district.network.ui.nodes;
3 import java.awt.BasicStroke;
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;
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;
42 public class NetworkDrawingNode extends G2DNode {
44 static class DrawingNode {
46 private List<Point2D> routeNodes = new ArrayList<>();
49 private static final long serialVersionUID = -3475301184009620573L;
51 private Point2D currentMousePos = null;
53 private List<DrawingNode> nodes = new ArrayList<>();
54 private DrawingNode currentRouteNode = null;
56 private Resource diagramResource;
58 private boolean committed;
60 private NetworkDrawingParticipant participant;
62 private IDiagram diagram;
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);
69 private static final Color BLUE_ALPHA = new Color(0, 0, 255, 100);
71 private boolean scaleStroke = true;
76 addEventHandler(this);
79 public void setNetworkDrawingParticipant(NetworkDrawingParticipant participant) {
80 this.participant = participant;
83 public void setDiagram(IDiagram diagram) {
84 if (diagram != null) {
85 this.diagram = diagram;
86 this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
91 public void render(Graphics2D g2d) {
95 Color old = g2d.getColor();
96 Stroke oldStroke = g2d.getStroke();
98 Iterator<DrawingNode> dnodeIterator = nodes.iterator();
99 while (dnodeIterator.hasNext()) {
100 Path2D path = new Path2D.Double();
101 DrawingNode dnode = dnodeIterator.next();
102 Iterator<Point2D> nodeIter = dnode.routeNodes.iterator();
103 if (nodeIter.hasNext()) {
104 Point2D node = nodeIter.next();
105 path.moveTo(node.getX(), node.getY());
107 while (nodeIter.hasNext()) {
108 Point2D node = nodeIter.next();
109 path.lineTo(node.getX(), node.getY());
111 if (!dnodeIterator.hasNext()) {
112 if (currentMousePos != null)
113 path.lineTo(currentMousePos.getX(), currentMousePos.getY());
116 if (DASHED_STROKE != null) {
117 if (scaleStroke && DASHED_STROKE instanceof BasicStroke) {
118 BasicStroke bs = GeometryUtils.scaleStroke(DASHED_STROKE, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform())));
121 g2d.setStroke(DASHED_STROKE);
125 g2d.setColor(BLUE_ALPHA);
130 g2d.setStroke(oldStroke);
135 public Rectangle2D getBoundsInLocal() {
140 public int getEventMask() {
141 return EventTypes.AnyMask;
145 protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {
147 IToolMode mode = getToolMode();
148 if (mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) {
149 // ok, new routenode starts from here
150 Point2D localPos = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
151 Point2D.Double pos = new Point2D.Double(localPos.getX(), localPos.getY());
152 if (currentRouteNode != null) {
153 //currentRouteNode.routeNodes.add(pos);
154 currentRouteNode = new DrawingNode();
155 currentRouteNode.routeNodes.add(pos);
156 nodes.add(currentRouteNode);
158 // ok, this must be creation of dh_point
159 double scale = getTransform().getScaleY();
160 double x = ModelledCRS.xToLongitude(pos.getX() / scale);
161 double y = ModelledCRS.yToLatitude(-pos.getY() / scale);
162 Simantics.getSession().asyncRequest(new Write() {
165 public void perform(WriteGraph graph) throws DatabaseException {
166 graph.markUndoPoint();
167 Resource defaultMapping = graph.getSingleObject(diagramResource, DistrictNetworkResource.getInstance(graph).VertexDefaultMapping);
168 DistrictNetworkUtil.createVertex(graph, diagramResource, new double[] { x, y }, 0, defaultMapping); // TODO: elevation can be fetched from e.g. elevation API
175 return super.mouseDoubleClicked(e);
178 private void createEdge(DrawingNode node) {
180 Point2D start = node.routeNodes.get(0);
181 Point2D end = node.routeNodes.get(node.routeNodes.size() - 1);
183 double currentPadding = DistrictNetworkVertexNode.width;
184 AffineTransform test = getTransform();
185 ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(this);
186 AffineTransform tr = ctx.getHintStack().getHint(Hints.KEY_CANVAS_TRANSFORM);
187 AffineTransform testing = new AffineTransform(tr);
188 testing.concatenate(test);
189 double calculateScaleRecip = DistrictNetworkNodeUtils.calculateScaleRecip(testing);
190 double padding = currentPadding * calculateScaleRecip;
192 * To convert y-coordinates to map coordinates in ruler, use:
193 * double val = (y-offsetY)/scaleY;
194 * val = Math.toDegrees(Math.atan(Math.sinh(Math.toRadians(val))));
195 * String str = formatValue(val);
198 double scaleY = getTransform().getScaleY();
199 double scaleX = getTransform().getScaleX();
201 double startLat = ModelledCRS.yToLatitude(-start.getY() / scaleY);
202 double startLon = ModelledCRS.xToLongitude(start.getX() / scaleX);
204 double endLat = ModelledCRS.yToLatitude(-end.getY() / scaleY);
205 double endLon = ModelledCRS.xToLongitude(end.getX() / scaleX);
207 double[] startCoords = new double[] { startLon, startLat };
208 double[] endCoords = new double[] { endLon, endLat };
210 double[] detailedGeometryCoords = new double[node.routeNodes.size() * 2];
212 for (Point2D p : node.routeNodes) {
213 double lat = ModelledCRS.yToLatitude(-p.getY() / scaleY);
214 double lon = ModelledCRS.xToLongitude(p.getX() / scaleX);
215 detailedGeometryCoords[i++] = lon;
216 detailedGeometryCoords[i++] = lat;
219 DNEdgeBuilder builder = new DNEdgeBuilder(diagramResource, diagram);
220 Simantics.getSession().asyncRequest(new WriteRequest() {
223 public void perform(WriteGraph graph) throws DatabaseException {
224 builder.create(graph, startCoords, 0, endCoords, 0, detailedGeometryCoords, padding);
231 protected boolean mouseClicked(MouseClickEvent e) {
233 IToolMode mode = getToolMode();
234 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());
255 currentRouteNode = null;
258 } else if (currentRouteNode != null) {
259 currentRouteNode.routeNodes.add(new Point2D.Double(localPos.getX(), localPos.getY()));
265 return super.mouseClicked(e);
268 private boolean canStartEdge(Point2D currentPos) {
269 return participant.isHoveringOverNode(currentPos);
272 private IToolMode getToolMode() {
273 return participant.getHint(Hints.KEY_TOOL);
277 protected boolean mouseMoved(MouseMovedEvent e) {
278 IToolMode mode = getToolMode();
279 boolean repaint = false;
280 Point2D p = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
281 boolean isConnectionTool = mode == Hints.CONNECTTOOL || e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK);
282 if (participant.pickHoveredElement(p, isConnectionTool)) {
285 if (!nodes.isEmpty()) {
291 currentMousePos = null;
294 return super.mouseMoved(e);
298 protected boolean keyPressed(KeyPressedEvent e) {
299 if (e.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {
300 currentRouteNode = null;
305 return super.keyPressed(e);