]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/EdgeNode.java
G2DParentNode handles "undefined" child bounds separately
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / EdgeNode.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in 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.scenegraph.g2d.nodes;
13
14 import java.awt.BasicStroke;
15 import java.awt.Color;
16 import java.awt.Composite;
17 import java.awt.Graphics2D;
18 import java.awt.Shape;
19 import java.awt.Stroke;
20 import java.awt.geom.AffineTransform;
21 import java.awt.geom.GeneralPath;
22 import java.awt.geom.Point2D;
23 import java.awt.geom.Rectangle2D;
24
25 import org.simantics.scenegraph.g2d.G2DNode;
26 import org.simantics.scenegraph.utils.InitValueSupport;
27
28 public class EdgeNode extends G2DNode implements InitValueSupport {
29
30     private static final long serialVersionUID = 1294351381209071074L;
31
32     public static enum ArrowType { None, Stroke, Fill, Both }
33
34     protected Color color = null;
35     protected Stroke stroke = null;
36     protected Shape shape = null;
37     protected Point2D firstdir = null;
38     protected Point2D lastdir = null;
39     protected Point2D first = null;
40     protected Point2D last = null;
41     protected double firstsize = 0;
42     protected double lastsize = 0;
43     protected ArrowType first_at = null;
44     protected ArrowType last_at = null;
45     protected Shape firstShape = null;
46     protected Shape lastShape = null;
47
48     private transient Rectangle2D bounds;
49
50     @SyncField({"color", "stroke", "shape", "firstdir", "lastdir", "first", "last", "firstsize", "lastsize", "first_at", "last_at"})
51     public void init(Shape shape, Stroke stroke, Color color, Point2D firstdir, Point2D lastdir, Point2D first, Point2D last, double firstsize, double lastsize, ArrowType first_at, ArrowType last_at, Shape firstShape, Shape lastShape) {
52         this.color = color;
53         this.stroke = stroke;
54         this.shape = shape;
55         this.firstdir = firstdir;
56         this.lastdir = lastdir;
57         this.first = first;
58         this.last = last;
59         this.firstsize = firstsize;
60         this.lastsize = lastsize;
61         this.first_at = first_at;
62         this.last_at = last_at;
63         this.firstShape = firstShape;
64         this.lastShape = lastShape;
65
66         if (shape != null) {
67             this.bounds = shape.getBounds2D();
68         }
69     }
70
71     @Override
72     public void render(Graphics2D g) {
73         if(color != null) g.setColor(color);
74         if(stroke == null || shape == null)  return;
75
76         if(alphaComposite != null) {
77             g.setComposite(alphaComposite);
78         }
79
80         Stroke effectiveStroke = stroke;
81         if(dynamicStroke != null) {
82             effectiveStroke = dynamicStroke;
83         }
84
85         Color effectiveColor = color;
86         if(dynamicColor != null) {
87             effectiveColor = dynamicColor;
88         }
89
90         g.setStroke(effectiveStroke);
91
92         // NICENESS
93         //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
94
95         //g.draw(shape);
96
97         // Draw line "halo"
98 //        float f = ((BasicStroke)effectiveStroke).getLineWidth() * 3f;
99 //        Color background = Color.WHITE; // FIXME
100 //        g.setColor(background);
101 //        g.setStroke(new BasicStroke(f));
102 //        g.draw(shape);
103
104         // Draw line
105         g.setColor(effectiveColor);
106         g.setStroke(effectiveStroke);
107         g.draw(shape);
108
109         // Draw line ends if necessary.
110         boolean drawArrows = first_at != ArrowType.None || last_at != ArrowType.None;
111         if (!drawArrows)
112             return;
113
114         AffineTransform at = g.getTransform();
115
116         g.setStroke(ARROW_STROKE);
117
118         if (first_at != ArrowType.None) {
119             double theta = Math.atan2(firstdir.getY(), firstdir.getX()) - Math.PI/2;
120             g.translate(first.getX(), first.getY());
121             g.rotate(theta);
122             g.scale(firstsize, firstsize);
123
124             if (first_at == ArrowType.Fill)
125                 g.fill(FILLED_ARROW);
126             else if (first_at == ArrowType.Stroke)
127                 g.draw(NORMAL_ARROW);
128
129             g.setTransform(at);
130         }
131
132         if (last_at != ArrowType.None) {
133             double theta = Math.atan2(lastdir.getY(), lastdir.getX()) - Math.PI/2;
134             g.translate(last.getX(), last.getY());
135             g.rotate(theta);
136             g.scale(lastsize, lastsize);
137
138             if (last_at == ArrowType.Fill)
139                 g.fill(FILLED_ARROW);
140             else if (last_at == ArrowType.Stroke)
141                 g.draw(NORMAL_ARROW);
142
143             g.setTransform(at);
144         }
145     }
146
147
148     public transient final static GeneralPath NORMAL_ARROW;
149     public transient final static GeneralPath FILLED_ARROW;
150     public transient static final Stroke ARROW_STROKE = new BasicStroke(1.0f);
151
152     static {
153         FILLED_ARROW = new GeneralPath();
154         FILLED_ARROW.moveTo(-0.5f, 1f);
155         FILLED_ARROW.lineTo(   0f, 0f);
156         FILLED_ARROW.lineTo( 0.5f, 1f);
157         FILLED_ARROW.closePath();
158
159         NORMAL_ARROW = new GeneralPath();
160         NORMAL_ARROW.moveTo(-0.5f, 1f);
161         NORMAL_ARROW.lineTo(   0f, 0f);
162         NORMAL_ARROW.lineTo( 0.5f, 1f);
163     }
164
165     @Override
166     public Rectangle2D getBoundsInLocal() {
167         return bounds;
168     }
169
170     protected Composite alphaComposite = null;
171     protected Stroke dynamicStroke = null;
172     protected Color dynamicColor = null;
173
174     @PropertySetter("alpha")
175     @SyncField("alphaComposite")
176     public void setAlphaComposite(Composite alphaComposite) {
177         this.alphaComposite = alphaComposite;
178     }
179
180     @PropertySetter("width")
181     @SyncField("dynamicStroke")
182     public void setDynamicStroke(Stroke stroke) {
183         this.dynamicStroke = stroke;
184     }
185
186     @PropertySetter("color")
187     @SyncField("dynamicColor")
188     public void setDynamicColor(Color color) {
189         this.dynamicColor = color;
190     }
191
192 //    public void setValue(String key, Object value) {
193 //
194 //        if ("alpha".equals(key)) {
195 //              Float val = Float.parseFloat((String)value);
196 //              alphaComposite = AlphaComposite.getInstance(AlphaComposite. SRC_OVER, val);
197 //        } else if ("width".equals(key)) {
198 //            dynamicStroke = new BasicStroke(Float.parseFloat((String)value));
199 //        } else if ("color".equals(key)) {
200 //            try {
201 //              dynamicColor = new Color(Integer.parseInt(value.toString(), 16));
202 //            } catch (Throwable t) {
203 //                t.printStackTrace();
204 //            }
205 //        }
206 //    }
207
208     @Override
209     public void initValues() {
210         dynamicStroke = null;
211         dynamicColor = null;
212         alphaComposite = null;
213     }
214
215 }