added svn:ignores
[debian/openrocket] / src / net / sf / openrocket / rocketcomponent / FreeformFinSet.java
1 package net.sf.openrocket.rocketcomponent;
2
3 import net.sf.openrocket.logging.LogHelper;
4 import net.sf.openrocket.startup.Application;
5 import net.sf.openrocket.util.ArrayList;
6 import net.sf.openrocket.util.BugException;
7 import net.sf.openrocket.util.Coordinate;
8
9 import java.util.Arrays;
10 import java.util.Collections;
11 import java.util.List;
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         private void validate(ArrayList<Coordinate> points) throws IllegalFinPointException {
318                 final int n = points.size();
319                 if (points.get(0).x != 0 || points.get(0).y != 0 ||
320                                 points.get(n - 1).x < 0 || points.get(n - 1).y != 0) {
321                         throw new IllegalFinPointException("Start or end point illegal.");
322                 }
323                 for (int i = 0; i < n - 1; i++) {
324                         for (int j = i + 2; j < n - 1; j++) {
325                                 if (intersects(points.get(i).x, points.get(i).y, points.get(i + 1).x, points.get(i + 1).y,
326                                                                 points.get(j).x, points.get(j).y, points.get(j + 1).x, points.get(j + 1).y)) {
327                                         throw new IllegalFinPointException("segments intersect");
328                                 }
329                         }
330                         if (points.get(i).z != 0) {
331                                 throw new IllegalFinPointException("z-coordinate not zero");
332                         }
333                 }
334         }
335
336 }