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