I18 changes
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / FreeformFinSet.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import java.util.Arrays;
4 import java.util.Collections;
5 import java.util.List;
6
7 import net.sf.openrocket.logging.LogHelper;
8 import net.sf.openrocket.startup.Application;
9 import net.sf.openrocket.util.ArrayList;
10 import net.sf.openrocket.util.BugException;
11 import net.sf.openrocket.util.Coordinate;
12
13
14 public class FreeformFinSet extends FinSet {
15         private static final LogHelper log = Application.getLogger();
16         
17         private ArrayList<Coordinate> points = new ArrayList<Coordinate>();
18         
19         public FreeformFinSet() {
20                 points.add(Coordinate.NUL);
21                 points.add(new Coordinate(0.025, 0.05));
22                 points.add(new Coordinate(0.075, 0.05));
23                 points.add(new Coordinate(0.05, 0));
24                 
25                 this.length = 0.05;
26         }
27         
28         
29         public FreeformFinSet(Coordinate[] finpoints) throws IllegalFinPointException {
30                 setPoints(finpoints);
31         }
32         
33         /*
34         public FreeformFinSet(FinSet finset) {
35                 Coordinate[] finpoints = finset.getFinPoints();
36                 this.copyFrom(finset);
37                 
38                 points.clear();
39                 for (Coordinate c: finpoints) {
40                         points.add(c);
41                 }
42                 this.length = points.get(points.size()-1).x - points.get(0).x;
43         }
44         */
45
46
47         /**
48          * Convert an existing fin set into a freeform fin set.  The specified
49          * fin set is taken out of the rocket tree (if any) and the new component
50          * inserted in its stead.
51          * <p>
52          * The specified fin set should not be used after the call!
53          * 
54          * @param finset        the fin set to convert.
55          * @return                      the new freeform fin set.
56          */
57         public static FreeformFinSet convertFinSet(FinSet finset) {
58                 log.info("Converting " + finset.getComponentName() + " into freeform fin set");
59                 final RocketComponent root = finset.getRoot();
60                 FreeformFinSet freeform;
61                 List<RocketComponent> toInvalidate = Collections.emptyList();
62                 
63                 try {
64                         if (root instanceof Rocket) {
65                                 ((Rocket) root).freeze();
66                         }
67                         
68                         // Get fin set position and remove fin set
69                         final RocketComponent parent = finset.getParent();
70                         final int position;
71                         if (parent != null) {
72                                 position = parent.getChildPosition(finset);
73                                 parent.removeChild(position);
74                         } else {
75                                 position = -1;
76                         }
77                         
78
79                         // Create the freeform fin set
80                         Coordinate[] finpoints = finset.getFinPoints();
81                         try {
82                                 freeform = new FreeformFinSet(finpoints);
83                         } catch (IllegalFinPointException e) {
84                                 throw new BugException("Illegal fin points when converting existing fin to " +
85                                                 "freeform fin, fin=" + finset + " points=" + Arrays.toString(finpoints),
86                                                 e);
87                         }
88                         
89                         // Copy component attributes
90                         toInvalidate = freeform.copyFrom(finset);
91                         
92                         // Set name
93                         final String componentTypeName = finset.getComponentName();
94                         final String name = freeform.getName();
95                         
96                         if (name.startsWith(componentTypeName)) {
97                                 freeform.setName(freeform.getComponentName() +
98                                                 name.substring(componentTypeName.length()));
99                         }
100                         
101                         // Add freeform fin set to parent
102                         if (parent != null) {
103                                 parent.addChild(freeform, position);
104                         }
105                         
106                 } finally {
107                         if (root instanceof Rocket) {
108                                 ((Rocket) root).thaw();
109                         }
110                         // Invalidate components after events have been fired
111                         for (RocketComponent c : toInvalidate) {
112                                 c.invalidate();
113                         }
114                 }
115                 return freeform;
116         }
117         
118         
119
120         /**
121          * Add a fin point between indices <code>index-1</code> and <code>index</code>.
122          * The point is placed at the midpoint of the current segment.
123          * 
124          * @param index   the fin point before which to add the new point.
125          */
126         public void addPoint(int index) {
127                 double x0, y0, x1, y1;
128                 
129                 x0 = points.get(index - 1).x;
130                 y0 = points.get(index - 1).y;
131                 x1 = points.get(index).x;
132                 y1 = points.get(index).y;
133                 
134                 points.add(index, new Coordinate((x0 + x1) / 2, (y0 + y1) / 2));
135                 // adding a point within the segment affects neither mass nor aerodynamics
136                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
137         }
138         
139         
140         /**
141          * Remove the fin point with the given index.  The first and last fin points
142          * cannot be removed, and will cause an <code>IllegalFinPointException</code>
143          * if attempted.
144          * 
145          * @param index   the fin point index to remove
146          * @throws IllegalFinPointException if removing would result in invalid fin planform
147          */
148         public void removePoint(int index) throws IllegalFinPointException {
149                 if (index == 0 || index == points.size() - 1) {
150                         throw new IllegalFinPointException("cannot remove first or last point");
151                 }
152                 
153                 ArrayList<Coordinate> copy = this.points.clone();
154                 copy.remove(index);
155                 validate(copy);
156                 this.points = copy;
157                 
158                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
159         }
160         
161         
162         public int getPointCount() {
163                 return points.size();
164         }
165         
166         public void setPoints(Coordinate[] points) throws IllegalFinPointException {
167                 ArrayList<Coordinate> list = new ArrayList<Coordinate>(points.length);
168                 for (Coordinate p : points) {
169                         list.add(p);
170                 }
171                 validate(list);
172                 this.points = list;
173                 
174                 this.length = points[points.length - 1].x;
175                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
176         }
177         
178         
179         /**
180          * Set the point at position <code>i</code> to coordinates (x,y).
181          * <p>
182          * Note that this method enforces basic fin shape restrictions (non-negative y,
183          * first and last point locations) silently, but throws an 
184          * <code>IllegalFinPointException</code> if the point causes fin segments to
185          * intersect.
186          * <p>
187          * Moving of the first point in the X-axis is allowed, but this actually moves
188          * all of the other points the corresponding distance back.
189          * 
190          * @param index the point index to modify.
191          * @param x             the x-coordinate.
192          * @param y             the y-coordinate.
193          * @throws IllegalFinPointException     if the specified fin point would cause intersecting
194          *                                                                      segments
195          */
196         public void setPoint(int index, double x, double y) throws IllegalFinPointException {
197                 if (y < 0)
198                         y = 0;
199                 
200                 double x0, y0, x1, y1;
201                 
202                 if (index == 0) {
203                         
204                         // Restrict point
205                         x = Math.min(x, points.get(points.size() - 1).x);
206                         y = 0;
207                         x0 = Double.NaN;
208                         y0 = Double.NaN;
209                         x1 = points.get(1).x;
210                         y1 = points.get(1).y;
211                         
212                 } else if (index == points.size() - 1) {
213                         
214                         // Restrict point
215                         x = Math.max(x, 0);
216                         y = 0;
217                         x0 = points.get(index - 1).x;
218                         y0 = points.get(index - 1).y;
219                         x1 = Double.NaN;
220                         y1 = Double.NaN;
221                         
222                 } else {
223                         
224                         x0 = points.get(index - 1).x;
225                         y0 = points.get(index - 1).y;
226                         x1 = points.get(index + 1).x;
227                         y1 = points.get(index + 1).y;
228                         
229                 }
230                 
231
232
233                 // Check for intersecting
234                 double px0, py0, px1, py1;
235                 px0 = 0;
236                 py0 = 0;
237                 for (int i = 1; i < points.size(); i++) {
238                         px1 = points.get(i).x;
239                         py1 = points.get(i).y;
240                         
241                         if (i != index - 1 && i != index && i != index + 1) {
242                                 if (intersects(x0, y0, x, y, px0, py0, px1, py1)) {
243                                         throw new IllegalFinPointException("segments intersect");
244                                 }
245                         }
246                         if (i != index && i != index + 1 && i != index + 2) {
247                                 if (intersects(x, y, x1, y1, px0, py0, px1, py1)) {
248                                         throw new IllegalFinPointException("segments intersect");
249                                 }
250                         }
251                         
252                         px0 = px1;
253                         py0 = py1;
254                 }
255                 
256                 if (index == 0) {
257                         
258                         System.out.println("Set point zero to x:" + x);
259                         for (int i = 1; i < points.size(); i++) {
260                                 Coordinate c = points.get(i);
261                                 points.set(i, c.setX(c.x - x));
262                         }
263                         
264                 } else {
265                         
266                         points.set(index, new Coordinate(x, y));
267                         
268                 }
269                 if (index == 0 || index == points.size() - 1) {
270                         this.length = points.get(points.size() - 1).x;
271                 }
272                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
273         }
274         
275         
276
277         private boolean intersects(double ax0, double ay0, double ax1, double ay1,
278                         double bx0, double by0, double bx1, double by1) {
279                 
280                 double d = ((by1 - by0) * (ax1 - ax0) - (bx1 - bx0) * (ay1 - ay0));
281                 
282                 double ua = ((bx1 - bx0) * (ay0 - by0) - (by1 - by0) * (ax0 - bx0)) / d;
283                 double ub = ((ax1 - ax0) * (ay0 - by0) - (ay1 - ay0) * (ax0 - bx0)) / d;
284                 
285                 return (ua >= 0) && (ua <= 1) && (ub >= 0) && (ub <= 1);
286         }
287         
288         
289         @Override
290         public Coordinate[] getFinPoints() {
291                 return points.toArray(new Coordinate[0]);
292         }
293         
294         @Override
295         public double getSpan() {
296                 double max = 0;
297                 for (Coordinate c : points) {
298                         if (c.y > max)
299                                 max = c.y;
300                 }
301                 return max;
302         }
303         
304         @Override
305         public String getComponentName() {
306                 return "Freeform fin set";
307         }
308         
309         
310         @Override
311         protected RocketComponent copyWithOriginalID() {
312                 RocketComponent c = super.copyWithOriginalID();
313                 ((FreeformFinSet) c).points = this.points.clone();
314                 return c;
315         }
316         
317         /**
318          * Accept a visitor to this FreeformFinSet in the component hierarchy.
319          * 
320          * @param theVisitor  the visitor that will be called back with a reference to this FreeformFinSet
321          */
322         @Override
323         public void accept(ComponentVisitor theVisitor) {
324                 theVisitor.visit(this);
325         }
326         
327         private void validate(ArrayList<Coordinate> points) throws IllegalFinPointException {
328                 final int n = points.size();
329                 if (points.get(0).x != 0 || points.get(0).y != 0 ||
330                                 points.get(n - 1).x < 0 || points.get(n - 1).y != 0) {
331                         throw new IllegalFinPointException("Start or end point illegal.");
332                 }
333                 for (int i = 0; i < n - 1; i++) {
334                         for (int j = i + 2; j < n - 1; j++) {
335                                 if (intersects(points.get(i).x, points.get(i).y, points.get(i + 1).x, points.get(i + 1).y,
336                                                                 points.get(j).x, points.get(j).y, points.get(j + 1).x, points.get(j + 1).y)) {
337                                         throw new IllegalFinPointException("segments intersect");
338                                 }
339                         }
340                         if (points.get(i).z != 0) {
341                                 throw new IllegalFinPointException("z-coordinate not zero");
342                         }
343                 }
344         }
345         
346 }