Initial commit
[debian/openrocket] / src / net / sf / openrocket / gui / scalefigure / FinPointFigure.java
1 package net.sf.openrocket.gui.scalefigure;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Dimension;
6 import java.awt.Graphics;
7 import java.awt.Graphics2D;
8 import java.awt.Rectangle;
9 import java.awt.RenderingHints;
10 import java.awt.geom.AffineTransform;
11 import java.awt.geom.Line2D;
12 import java.awt.geom.NoninvertibleTransformException;
13 import java.awt.geom.Path2D;
14 import java.awt.geom.Point2D;
15 import java.awt.geom.Rectangle2D;
16
17 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
18 import net.sf.openrocket.unit.Tick;
19 import net.sf.openrocket.unit.Unit;
20 import net.sf.openrocket.unit.UnitGroup;
21 import net.sf.openrocket.util.Coordinate;
22 import net.sf.openrocket.util.MathUtil;
23
24
25 // TODO: MEDIUM:  the figure jumps and bugs when using automatic fitting
26
27 public class FinPointFigure extends AbstractScaleFigure {
28
29         private static final int BOX_SIZE = 4;
30         
31         private final FreeformFinSet finset;
32         private int modID = -1;
33         
34         private double minX, maxX, maxY;
35         private double figureWidth = 0;
36         private double figureHeight = 0;
37         private double translateX = 0;
38         private double translateY = 0;
39         
40         private AffineTransform transform;
41         private Rectangle2D.Double[] handles = null;
42         
43         
44         public FinPointFigure(FreeformFinSet finset) {
45                 this.finset = finset;
46         }
47         
48
49         @Override
50         public void paintComponent(Graphics g) {
51                 super.paintComponent(g);
52                 Graphics2D g2 = (Graphics2D) g;
53
54                 if (modID != finset.getRocket().getAerodynamicModID()) {
55                         modID = finset.getRocket().getAerodynamicModID();
56                         calculateDimensions();
57                 }
58
59                 
60                 double tx, ty;
61                 // Calculate translation for figure centering
62                 if (figureWidth*scale + 2*BORDER_PIXELS_WIDTH < getWidth()) {
63
64                         // Figure fits in the viewport
65                         tx = (getWidth()-figureWidth*scale)/2 - minX*scale;
66
67                 } else {
68
69                         // Figure does not fit in viewport
70                         tx = BORDER_PIXELS_WIDTH - minX*scale;
71                         
72                 }
73                 
74
75                 if (figureHeight*scale + 2*BORDER_PIXELS_HEIGHT < getHeight()) {
76                         ty = getHeight() - BORDER_PIXELS_HEIGHT;
77                 } else {
78                         ty = BORDER_PIXELS_HEIGHT + figureHeight*scale;
79                 }
80                 
81                 if (Math.abs(translateX - tx)>1 || Math.abs(translateY - ty)>1) {
82                         // Origin has changed, fire event
83                         translateX = tx;
84                         translateY = ty;
85                         fireChangeEvent();
86                 }
87                 
88                 
89                 if (Math.abs(translateX - tx)>1 || Math.abs(translateY - ty)>1) {
90                         // Origin has changed, fire event
91                         translateX = tx;
92                         translateY = ty;
93                         fireChangeEvent();
94                 }
95                 
96
97                 // Calculate and store the transformation used
98                 transform = new AffineTransform();
99                 transform.translate(translateX, translateY);
100                 transform.scale(scale/EXTRA_SCALE, -scale/EXTRA_SCALE);
101                 
102                 // TODO: HIGH:  border Y-scale upwards
103
104                 g2.transform(transform);
105                 
106                 // Set rendering hints appropriately
107                 g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, 
108                                 RenderingHints.VALUE_STROKE_NORMALIZE);
109                 g2.setRenderingHint(RenderingHints.KEY_RENDERING, 
110                                 RenderingHints.VALUE_RENDER_QUALITY);
111                 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
112                                 RenderingHints.VALUE_ANTIALIAS_ON);
113
114
115                 
116                 Rectangle visible = g2.getClipBounds();
117                 double x0 = ((double)visible.x-3)/EXTRA_SCALE;
118                 double x1 = ((double)visible.x+visible.width+4)/EXTRA_SCALE;
119                 double y0 = ((double)visible.y-3)/EXTRA_SCALE;
120                 double y1 = ((double)visible.y+visible.height+4)/EXTRA_SCALE;
121                 
122                 
123                 // Background grid
124                 
125                 g2.setStroke(new BasicStroke((float)(1.0*EXTRA_SCALE/scale),
126                                 BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
127                 g2.setColor(new Color(0,0,255,30));
128
129                 Unit unit;
130                 if (this.getParent() != null &&
131                                 this.getParent().getParent() instanceof ScaleScrollPane) {
132                         unit = ((ScaleScrollPane)this.getParent().getParent()).getCurrentUnit();
133                 } else {
134                         unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
135                 }
136                 
137                 // vertical
138         Tick[] ticks = unit.getTicks(x0, x1, 
139                         ScaleScrollPane.MINOR_TICKS/scale, 
140                         ScaleScrollPane.MAJOR_TICKS/scale);
141         Line2D.Double line = new Line2D.Double();
142         for (Tick t: ticks) {
143                 if (t.major) {
144                         line.setLine(t.value*EXTRA_SCALE, y0*EXTRA_SCALE, 
145                                         t.value*EXTRA_SCALE, y1*EXTRA_SCALE);
146                         g2.draw(line);
147                 }
148         }
149         
150         // horizontal
151         ticks = unit.getTicks(y0, y1, 
152                         ScaleScrollPane.MINOR_TICKS/scale, 
153                         ScaleScrollPane.MAJOR_TICKS/scale);
154         for (Tick t: ticks) {
155                 if (t.major) {
156                         line.setLine(x0*EXTRA_SCALE, t.value*EXTRA_SCALE, 
157                                         x1*EXTRA_SCALE, t.value*EXTRA_SCALE);
158                         g2.draw(line);
159                 }
160         }
161                 
162         
163         
164         
165
166                 // Base rocket line
167                 g2.setStroke(new BasicStroke((float)(3.0*EXTRA_SCALE/scale),
168                                 BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL));
169                 g2.setColor(Color.GRAY);
170                 
171                 g2.drawLine((int)(x0*EXTRA_SCALE), 0, (int)(x1*EXTRA_SCALE), 0);
172                 
173                 
174                 // Fin shape
175                 Coordinate[] points = finset.getFinPoints();
176                 Path2D.Double shape = new Path2D.Double();
177                 shape.moveTo(0, 0);
178                 for (int i=1; i < points.length; i++) {
179                         shape.lineTo(points[i].x*EXTRA_SCALE, points[i].y*EXTRA_SCALE);
180                 }
181                 
182                 g2.setStroke(new BasicStroke((float)(1.0*EXTRA_SCALE/scale),
183                                 BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL));
184                 g2.setColor(Color.BLACK);
185                 g2.draw(shape);
186
187                 
188                 // Fin point boxes
189                 g2.setColor(new Color(150,0,0));
190                 double s = BOX_SIZE*EXTRA_SCALE/scale;
191                 handles = new Rectangle2D.Double[points.length];
192                 for (int i=0; i < points.length; i++) {
193                         Coordinate c = points[i];
194                         handles[i] = new Rectangle2D.Double(c.x*EXTRA_SCALE-s, c.y*EXTRA_SCALE-s, 2*s, 2*s);
195                         g2.draw(handles[i]);
196                 }
197                 
198         }
199
200         
201         
202         public int getIndexByPoint(double x, double y) {
203                 if (handles == null)
204                         return -1;
205                 
206                 // Calculate point in shapes' coordinates
207                 Point2D.Double p = new Point2D.Double(x,y);
208                 try {
209                         transform.inverseTransform(p,p);
210                 } catch (NoninvertibleTransformException e) {
211                         return -1;
212                 }
213
214                 for (int i=0; i < handles.length; i++) {
215                         if (handles[i].contains(p))
216                                 return i;
217                 }
218                 return -1;
219         }
220         
221         
222         public int getSegmentByPoint(double x, double y) {
223                 if (handles == null)
224                         return -1;
225                 
226                 // Calculate point in shapes' coordinates
227                 Point2D.Double p = new Point2D.Double(x,y);
228                 try {
229                         transform.inverseTransform(p,p);
230                 } catch (NoninvertibleTransformException e) {
231                         return -1;
232                 }
233                 
234                 double x0 = p.x / EXTRA_SCALE;
235                 double y0 = p.y / EXTRA_SCALE;
236                 double delta = BOX_SIZE / scale;
237
238                 System.out.println("Point: "+x0+","+y0);
239                 System.out.println("delta: "+(BOX_SIZE/scale));
240                 
241                 Coordinate[] points = finset.getFinPoints();
242                 for (int i=1; i < points.length; i++) {
243                         double x1 = points[i-1].x;
244                         double y1 = points[i-1].y;
245                         double x2 = points[i].x;
246                         double y2 = points[i].y;
247                         
248 //                      System.out.println("point1:"+x1+","+y1+" point2:"+x2+","+y2);
249                         
250                         double u = Math.abs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / 
251                                                 MathUtil.hypot(x2-x1, y2-y1);
252                         System.out.println("Distance of segment "+i+" is "+u);
253                         if (u < delta)
254                                 return i;
255                 }
256                 
257                 return -1;
258         }
259         
260         
261         public Point2D.Double convertPoint(double x, double y) {
262                 Point2D.Double p = new Point2D.Double(x,y);
263                 try {
264                         transform.inverseTransform(p,p);
265                 } catch (NoninvertibleTransformException e) {
266                         assert(false): "Should not occur";
267                         return new Point2D.Double(0,0);
268                 }
269         
270                 p.setLocation(p.x / EXTRA_SCALE, p.y / EXTRA_SCALE);
271                 return p;
272         }
273         
274         
275
276         @Override
277         public Dimension getOrigin() {
278                 if (modID != finset.getRocket().getAerodynamicModID()) {
279                         modID = finset.getRocket().getAerodynamicModID();
280                         calculateDimensions();
281                 }
282                 return new Dimension((int)translateX, (int)translateY);
283         }
284
285         @Override
286         public double getFigureWidth() {
287                 if (modID != finset.getRocket().getAerodynamicModID()) {
288                         modID = finset.getRocket().getAerodynamicModID();
289                         calculateDimensions();
290                 }
291                 return figureWidth;
292         }
293
294         @Override
295         public double getFigureHeight() {
296                 if (modID != finset.getRocket().getAerodynamicModID()) {
297                         modID = finset.getRocket().getAerodynamicModID();
298                         calculateDimensions();
299                 }
300                 return figureHeight;
301         }
302
303         
304         private void calculateDimensions() {
305                 minX = 0;
306                 maxX = 0;
307                 maxY = 0;
308                 
309                 for (Coordinate c: finset.getFinPoints()) {
310                         if (c.x < minX)
311                                 minX = c.x;
312                         if (c.x > maxX)
313                                 maxX = c.x;
314                         if (c.y > maxY)
315                                 maxY = c.y;
316                 }
317                 
318                 if (maxX < 0.01)
319                         maxX = 0.01;
320                 
321                 figureWidth = maxX - minX;
322                 figureHeight = maxY;
323                 
324
325                 Dimension d = new Dimension((int)(figureWidth*scale+2*BORDER_PIXELS_WIDTH),
326                                 (int)(figureHeight*scale+2*BORDER_PIXELS_HEIGHT));
327                 
328                 if (!d.equals(getPreferredSize()) || !d.equals(getMinimumSize())) {
329                         setPreferredSize(d);
330                         setMinimumSize(d);
331                         revalidate();
332                 }
333         }
334         
335         
336
337         @Override
338         public void updateFigure() {
339                 repaint();
340         }
341         
342         
343
344 }