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