1312799fd36c1a8ebf51a0aa2498f3e0908aec86
[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.List;
5
6 import net.sf.openrocket.util.Coordinate;
7
8
9 public class FreeformFinSet extends FinSet {
10
11         private final List<Coordinate> points = new ArrayList<Coordinate>();
12         
13         public FreeformFinSet() {
14                 points.add(Coordinate.NUL);
15                 points.add(new Coordinate(0.025,0.05));
16                 points.add(new Coordinate(0.075,0.05));
17                 points.add(new Coordinate(0.05,0));
18                 
19                 this.length = 0.05;
20         }
21         
22         
23         public FreeformFinSet(FinSet finset) {
24                 Coordinate[] finpoints = finset.getFinPoints();
25                 this.copyFrom(finset);
26                 
27                 points.clear();
28                 for (Coordinate c: finpoints) {
29                         points.add(c);
30                 }
31                 this.length = points.get(points.size()-1).x - points.get(0).x;
32         }
33         
34         
35         
36         /**
37          * Add a fin point between indices <code>index-1</code> and <code>index</code>.
38          * The point is placed at the midpoint of the current segment.
39          * 
40          * @param index   the fin point before which to add the new point.
41          */
42         public void addPoint(int index) {
43                 double x0, y0, x1, y1;
44                 
45                 x0 = points.get(index-1).x;
46                 y0 = points.get(index-1).y;
47                 x1 = points.get(index).x;
48                 y1 = points.get(index).y;
49                 
50                 points.add(index, new Coordinate((x0+x1)/2, (y0+y1)/2));
51                 // adding a point within the segment affects neither mass nor aerodynamics
52                 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
53         }
54         
55         
56         /**
57          * Remove the fin point with the given index.  The first and last fin points
58          * cannot be removed, and will cause an <code>IllegalArgumentException</code>
59          * if attempted.
60          * 
61          * @param index   the fin point index to remove
62          * @throws IllegalFinPointException if removing the first or last fin point was attempted.
63          */
64         public void removePoint(int index) throws IllegalFinPointException {
65                 if (index == 0  ||  index == points.size()-1) {
66                         throw new IllegalFinPointException("cannot remove first or last point");
67                 }
68                 points.remove(index);
69                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
70         }
71         
72         
73         public int getPointCount() {
74                 return points.size();
75         }
76         
77         public void setPoints(Coordinate[] p) throws IllegalFinPointException {
78                 if (p[0].x != 0 || p[0].y != 0 || p[p.length-1].y != 0) {
79                         throw new IllegalFinPointException("Start or end point illegal.");
80                 }
81                 for (int i=0; i < p.length-1; i++) {
82                         for (int j=i+2; j < p.length-1; j++) {
83                                 if (intersects(p[i].x, p[i].y, p[i+1].x, p[i+1].y,
84                                                        p[j].x, p[j].y, p[j+1].x, p[j+1].y)) {
85                                         throw new IllegalFinPointException("segments intersect");
86                                 }
87                         }
88                         if (p[i].z != 0) {
89                                 throw new IllegalFinPointException("z-coordinate not zero");
90                         }
91                 }
92                 
93                 points.clear();
94                 for (Coordinate c: p) {
95                         points.add(c);
96                 }
97                 this.length = p[p.length-1].x;
98                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
99         }
100         
101
102         /**
103          * Set the point at position <code>i</code> to coordinates (x,y).
104          * <p>
105          * Note that this method enforces basic fin shape restrictions (non-negative y,
106          * first and last point locations) silently, but throws an 
107          * <code>IllegalArgumentException</code> if the point causes fin segments to
108          * intersect.  The calling method should always catch this exception.
109          * <p>
110          * Moving of the first point in the X-axis is allowed, but this actually moves
111          * all of the other points the corresponding distance back.
112          * 
113          * @param index the point index to modify.
114          * @param x             the x-coordinate.
115          * @param y             the y-coordinate.
116          * @throws IllegalFinPointException     if the specified fin point would cause intersecting
117          *                                                                      segments
118          */
119         public void setPoint(int index, double x, double y) throws IllegalFinPointException {
120                 if (y < 0)
121                         y = 0;
122                 
123                 double x0,y0,x1,y1;
124                 
125                 if (index == 0) {
126                         
127                         // Restrict point
128                         x = Math.min(x, points.get(points.size()-1).x);
129                         y = 0;
130                         x0 = Double.NaN;
131                         y0 = Double.NaN;
132                         x1 = points.get(1).x;
133                         y1 = points.get(1).y;
134                         
135                 } else if (index == points.size()-1) {
136                         
137                         // Restrict point
138                         x = Math.max(x, 0);
139                         y = 0;
140                         x0 = points.get(index-1).x;
141                         y0 = points.get(index-1).y;
142                         x1 = Double.NaN;
143                         y1 = Double.NaN;
144                         
145                 } else {
146                         
147                         x0 = points.get(index-1).x;
148                         y0 = points.get(index-1).y;
149                         x1 = points.get(index+1).x;
150                         y1 = points.get(index+1).y;
151                         
152                 }
153                 
154                 
155                 
156                 // Check for intersecting
157                 double px0, py0, px1, py1;
158                 px0 = 0;
159                 py0 = 0;
160                 for (int i=1; i < points.size(); i++) {
161                         px1 = points.get(i).x;
162                         py1 = points.get(i).y;
163                         
164                         if (i != index-1 && i != index && i != index+1) {
165                                 if (intersects(x0,y0,x,y,px0,py0,px1,py1)) {
166                                         throw new IllegalFinPointException("segments intersect");
167                                 }
168                         }
169                         if (i != index && i != index+1 && i != index+2) {
170                                 if (intersects(x,y,x1,y1,px0,py0,px1,py1)) {
171                                         throw new IllegalFinPointException("segments intersect");
172                                 }
173                         }
174                         
175                         px0 = px1;
176                         py0 = py1;
177                 }
178                 
179                 if (index == 0) {
180                         
181                         System.out.println("Set point zero to x:"+x);
182                         for (int i=1; i < points.size(); i++) {
183                                 Coordinate c = points.get(i);
184                                 points.set(i, c.setX(c.x - x));
185                         }
186                         
187                 } else {
188                         
189                         points.set(index,new Coordinate(x,y));
190                         
191                 }
192                 if (index == 0 || index == points.size()-1) {
193                         this.length = points.get(points.size()-1).x;
194                 }
195                 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
196         }
197         
198         
199         
200         private boolean intersects(double ax0, double ay0, double ax1, double ay1,
201                         double bx0, double by0, double bx1, double by1) {
202                 
203                 double d = ((by1-by0)*(ax1-ax0) - (bx1-bx0)*(ay1-ay0));
204                 
205                 double ua = ((bx1-bx0)*(ay0-by0) - (by1-by0)*(ax0-bx0)) / d;
206                 double ub = ((ax1-ax0)*(ay0-by0) - (ay1-ay0)*(ax0-bx0)) / d;
207                 
208                 return (ua >= 0) && (ua <= 1) && (ub >= 0) && (ub <= 1);
209         }
210         
211
212         @Override
213         public Coordinate[] getFinPoints() {
214                 return points.toArray(new Coordinate[0]);
215         }
216
217         @Override
218         public double getSpan() {
219                 double max = 0;
220                 for (Coordinate c: points) {
221                         if (c.y > max)
222                                 max = c.y;
223                 }
224                 return max;
225         }
226
227         @Override
228         public String getComponentName() {
229                 return "Freeform fin set";
230         }
231
232 }