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