]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java
NPE fix for district scene graph nodes
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / nodes / DistrictNetworkEdgeNode.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.RenderingHints;
7 import java.awt.geom.AffineTransform;
8 import java.awt.geom.Line2D;
9 import java.awt.geom.Path2D;
10 import java.awt.geom.Point2D;
11 import java.awt.geom.Rectangle2D;
12
13 import org.simantics.district.network.ModelledCRS;
14 import org.simantics.district.network.ui.DistrictNetworkEdge;
15 import org.simantics.district.network.ui.adapters.DistrictNetworkEdgeElementFactory;
16 import org.simantics.scenegraph.INode;
17 import org.simantics.scenegraph.ISelectionPainterNode;
18 import org.simantics.scenegraph.g2d.G2DNode;
19 import org.simantics.scenegraph.g2d.G2DParentNode;
20 import org.simantics.scenegraph.g2d.G2DRenderingHints;
21 import org.simantics.scenegraph.g2d.nodes.SVGNode;
22 import org.simantics.scenegraph.utils.GeometryUtils;
23 import org.simantics.scenegraph.utils.NodeUtil;
24
25 public class DistrictNetworkEdgeNode extends G2DParentNode implements ISelectionPainterNode {
26
27     private static final long serialVersionUID = 8049769475036519806L;
28
29     private static final BasicStroke STROKE           = new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
30     private static final Color       SELECTION_COLOR  = new Color(255, 0, 255, 96);
31
32     private DistrictNetworkEdge edge;
33     private Rectangle2D bounds;
34     private transient Path2D path;
35
36     private static boolean scaleStroke = true;
37     private Color color;
38     private Double stroke;
39     private transient Color dynamicColor = null;
40
41     // Dimensions for shut-off valve symbol
42     private static final double left = -0.25;
43     private static final double top = -0.25;
44     private static final double width = 0.5;
45     private static final double height = 0.5;
46
47     private static final Rectangle2D NORMAL = new Rectangle2D.Double(left, top, width, height);
48
49     private transient Point2D centerPoint;
50     private transient Rectangle2D symbolRect;
51     private transient AffineTransform symbolTransform;
52
53     private boolean hidden = false;
54     
55     private Double arrowLength;
56
57     private static double startX;
58     private static double startY;
59     private static double endX;
60     private static double endY;
61
62     @Override
63     public void init() {
64     }
65
66     @Override
67     public void render(Graphics2D g2d) {
68         AffineTransform ot = null;
69         AffineTransform t = getTransform();
70         double scale = scaleStroke ? (Double) g2d.getRenderingHint(DistrictRenderingHints.KEY_VIEW_SCALE_UNDER_SPATIAL_ROOT) : 1.0;
71         if (t != null && !t.isIdentity()) {
72             //ot = g2d.getTransform();
73             ot = (AffineTransform) g2d.getRenderingHint(G2DRenderingHints.KEY_TRANSFORM_UNDER_SPATIAL_ROOT);
74             if (ot == null)
75                 ot = g2d.getTransform();
76             g2d.transform(t);
77             if (scaleStroke) {
78                 AffineTransform work = DistrictNetworkNodeUtils.sharedTransform.get();
79                 work.setTransform(ot);
80                 work.concatenate(t);
81                 scale = DistrictNetworkNodeUtils.getScale(work);
82             }
83         }
84
85         if (!hidden) {
86             Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
87             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
88     
89             Color oldColor = g2d.getColor();
90             BasicStroke oldStroke = (BasicStroke) g2d.getStroke();
91     
92             BasicStroke bs = null;
93             if (scaleStroke) {
94                 bs = GeometryUtils.scaleStroke(STROKE, getStrokeWidth(scale));
95             } else {
96                 bs = STROKE;
97             }
98
99             int zoomLevel = (Integer) g2d.getRenderingHint(DistrictRenderingHints.KEY_VIEW_ZOOM_LEVEL);
100             path = calculatePath(edge, path, zoomLevel > 13);
101     
102             if (isSelected()) {
103                 g2d.setColor(SELECTION_COLOR);
104                 g2d.setStroke(GeometryUtils.scaleAndOffsetStrokeWidth(bs, 1.f, (float)(2 * STROKE.getLineWidth() / scale)));
105                 g2d.draw(path);
106             }
107     
108             g2d.setColor(dynamicColor != null ? dynamicColor : color);
109             g2d.setStroke(bs);
110             g2d.draw(path);
111     
112             // Draw arrow
113             if (arrowLength != null) {
114                 g2d.setColor(Color.BLACK);
115                 float lw = STROKE.getLineWidth() / (float)scale;
116                 g2d.setStroke(new BasicStroke(lw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
117                 
118                 double l = arrowLength;
119                 double w = 2 * (double) lw * Math.signum(l);
120                 if (Math.abs(w) > Math.abs(l)) w = l;
121                 double offset = 2 * (double) lw;
122                 
123                 double centerX = (startX + endX) / 2, centerY = (startY + endY) / 2;
124                 double deltaX = endX - startX, deltaY = endY - startY;
125                 double length = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
126                 deltaX /= length;
127                 deltaY /= length;
128                 
129                 double x0 = centerX - l/2 * deltaX + offset * deltaY;
130                 double y0 = centerY - l/2 * deltaY - offset * deltaX;
131                 double x1 = centerX + (l/2 - w) * deltaX + offset * deltaY;
132                 double y1 = centerY + (l/2 - w) * deltaY - offset * deltaX;
133                 
134                 g2d.draw(new Line2D.Double(x0, y0, x1, y1));
135                 
136                 Path2D path = new Path2D.Double();
137                 path.moveTo(x1 + w * deltaX, y1 + w * deltaY);
138                 path.lineTo(x1 + w * deltaY, y1 - w * deltaX);
139                 path.lineTo(x1 - w * deltaY, y1 + w * deltaX);
140                 path.closePath();
141                 g2d.fill(path);
142             }
143             
144             // Reset
145             g2d.setStroke(oldStroke);
146             g2d.setColor(oldColor);
147             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint);
148         }
149
150         for (INode nn : getNodes()) {
151             G2DNode g2dNode = (G2DNode)nn;
152             if (g2dNode instanceof SVGNode) {
153                 // Render SVG symbol
154                 double viewScaleRecip = 10;
155                 if (scaleStroke) {
156                     viewScaleRecip /= scale;
157                 }
158
159                 // If the node is hidden from the start, then the center point cannot be calculated yet.
160                 Point2D p = getCenterPoint();
161                 if (p == null)
162                     break;
163
164                 symbolRect = DistrictNetworkNodeUtils.calculateDrawnGeometry(p, NORMAL, symbolRect, viewScaleRecip);
165                 symbolTransform = DistrictNetworkNodeUtils.getTransformToRectangle(symbolRect, symbolTransform);
166
167                 g2dNode.setTransform(symbolTransform);
168             }
169             g2dNode.render(g2d);
170         }
171
172         if (ot != null)
173             g2d.setTransform(ot);
174     }
175
176     public float getStrokeWidth(AffineTransform tr, boolean selection) {
177         double scale = DistrictNetworkNodeUtils.getScale(tr);
178         float width = STROKE.getLineWidth() * getStrokeWidth(scale);
179         if (selection) width = width + (float) (2 * STROKE.getLineWidth() / scale);
180         return width;
181     }
182     
183     private float getStrokeWidth(double scale) {
184         if (scaleStroke) {
185             double str = stroke != null ? Math.abs(stroke) : 1.0;
186             float strokeWidth = (float) (str / scale);
187             return strokeWidth;
188         }
189         else {
190             return 1.f;
191         }
192     }
193
194     public Path2D getPath() {
195         return path;
196     }
197     
198     private Point2D getCenterPoint() {
199         if (path == null)
200             return null;
201         if (centerPoint == null)
202             centerPoint = new Point2D.Double();
203         Rectangle2D bounds = path.getBounds2D();
204         centerPoint.setLocation(bounds.getCenterX(), bounds.getCenterY());
205         return centerPoint;
206     }
207
208 //    public static Line2D calculateLine(DistrictNetworkEdge edge, Line2D result) {
209 //        // Convert to screen coordinates
210 //        double startX = ModelledCRS.longitudeToX(edge.getStartPoint().getX());
211 //        double startY = ModelledCRS.latitudeToY(-edge.getStartPoint().getY()); // Invert for Simantics
212 //        double endX = ModelledCRS.longitudeToX(edge.getEndPoint().getX());
213 //        double endY = ModelledCRS.latitudeToY(-edge.getEndPoint().getY());// Invert for Simantics
214 //
215 //        if (result == null)
216 //            result = new Line2D.Double();
217 //        result.setLine(startX, startY, endX, endY);
218 //        return result;
219 //    }
220
221     public static Path2D calculatePath(DistrictNetworkEdge edge, Path2D result, boolean detailed) {
222         startX = ModelledCRS.longitudeToX(edge.getStartPoint().getX());
223         startY = ModelledCRS.latitudeToY(-edge.getStartPoint().getY());
224         endX = ModelledCRS.longitudeToX(edge.getEndPoint().getX());
225         endY = ModelledCRS.latitudeToY(-edge.getEndPoint().getY());
226
227         if (result == null) {
228              result = new Path2D.Double();
229         } else {
230              result.reset();
231         }
232         result.moveTo(startX, startY);
233         if (detailed) {
234             double[] detailedGeometry = edge.getGeometry();
235             if (detailedGeometry != null && !DistrictNetworkEdgeElementFactory.EMPTY.equals(detailedGeometry)) {
236                 // ok, lets do this
237                 
238                 for (int i = 0; i < detailedGeometry.length; i += 2) {
239                     double x = ModelledCRS.longitudeToX(detailedGeometry[i]);
240                     double y = ModelledCRS.latitudeToY(-detailedGeometry[i+1]);// Invert for Simantics
241                     result.lineTo(x, y);
242                 }
243             }
244         }
245         result.lineTo(endX, endY);
246         return result;
247     }
248
249     private boolean isSelected() {
250         return NodeUtil.isSelected(this, 1);
251     }
252
253     @Override
254     public Rectangle2D getBoundsInLocal() {
255         return bounds;
256     }
257
258     private void updateBounds() {
259         Rectangle2D oldBounds = bounds;
260         if (oldBounds == null)
261             oldBounds = new Rectangle2D.Double();
262         bounds = calculateBounds(oldBounds);
263     }
264
265     private Rectangle2D calculateBounds(Rectangle2D rect) {
266         return calculatePath(edge, null, false).getBounds2D();
267     }
268
269     public void setDNEdge(DistrictNetworkEdge edge) {
270         this.edge = edge;
271         updateBounds();
272     }
273
274     public void setColor(Color color) {
275         this.color = color;
276     }
277
278     public Color getColor() {
279         return color;
280     }
281
282     @PropertySetter(value = "stroke")
283     public void setStroke(Double stroke) {
284         this.stroke = stroke;
285     }
286
287     @PropertySetter(value = "dynamicColor")
288     public void setDynamicColor(Color color) {
289         this.dynamicColor = color;
290     }
291     
292     @PropertySetter(value = "arrowLength")
293     public void setArrowLength(Double length) {
294         arrowLength = length;
295     }
296
297     @PropertySetter(value = "SVG")
298     public void setSVG(String value) {
299         for (INode nn : this.getNodes())
300             if (nn instanceof SVGNode)
301                 ((SVGNode)nn).setData(value);
302     }
303     
304
305     @PropertySetter(value = "hidden")
306     public void setHidden(Boolean value) {
307         this.hidden = value;
308     }
309 }