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