1 package net.sf.openrocket.rocketcomponent;
3 import net.sf.openrocket.util.BugException;
4 import net.sf.openrocket.util.Coordinate;
6 import java.util.ArrayList;
7 import java.util.Arrays;
10 public class FreeformFinSet extends FinSet {
12 private ArrayList<Coordinate> points = new ArrayList<Coordinate>();
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));
24 public FreeformFinSet(Coordinate[] finpoints) throws IllegalFinPointException {
29 public FreeformFinSet(FinSet finset) {
30 Coordinate[] finpoints = finset.getFinPoints();
31 this.copyFrom(finset);
34 for (Coordinate c: finpoints) {
37 this.length = points.get(points.size()-1).x - points.get(0).x;
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.
47 * The specified fin set should not be used after the call!
49 * @param finset the fin set to convert.
50 * @return the new freeform fin set.
52 public static FreeformFinSet convertFinSet(FinSet finset) {
53 final RocketComponent root = finset.getRoot();
54 FreeformFinSet freeform;
57 if (root instanceof Rocket) {
58 ((Rocket)root).freeze();
61 // Get fin set position and remove fin set
62 final RocketComponent parent = finset.getParent();
65 position = parent.getChildPosition(finset);
66 parent.removeChild(position);
72 // Create the freeform fin set
73 Coordinate[] finpoints = finset.getFinPoints();
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),
82 // Copy component attributes
83 freeform.copyFrom(finset);
86 final String componentTypeName = finset.getComponentName();
87 final String name = freeform.getName();
89 if (name.startsWith(componentTypeName)) {
90 freeform.setName(freeform.getComponentName() +
91 name.substring(componentTypeName.length()));
94 // Add freeform fin set to parent
96 parent.addChild(freeform, position);
100 if (root instanceof Rocket) {
101 ((Rocket)root).thaw();
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.
113 * @param index the fin point before which to add the new point.
115 public void addPoint(int index) {
116 double x0, y0, x1, y1;
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;
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);
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>
134 * @param index the fin point index to remove
135 * @throws IllegalFinPointException if removing would result in invalid fin planform
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");
143 ArrayList<Coordinate> copy = (ArrayList<Coordinate>) this.points.clone();
148 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
152 public int getPointCount() {
153 return points.size();
156 public void setPoints(Coordinate[] points) throws IllegalFinPointException {
157 ArrayList<Coordinate> list = new ArrayList<Coordinate>(points.length);
158 for (Coordinate p: points) {
164 this.length = points[points.length-1].x;
165 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
170 * Set the point at position <code>i</code> to coordinates (x,y).
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
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.
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
186 public void setPoint(int index, double x, double y) throws IllegalFinPointException {
195 x = Math.min(x, points.get(points.size()-1).x);
199 x1 = points.get(1).x;
200 y1 = points.get(1).y;
202 } else if (index == points.size()-1) {
207 x0 = points.get(index-1).x;
208 y0 = points.get(index-1).y;
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;
223 // Check for intersecting
224 double px0, py0, px1, py1;
227 for (int i=1; i < points.size(); i++) {
228 px1 = points.get(i).x;
229 py1 = points.get(i).y;
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");
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");
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));
256 points.set(index,new Coordinate(x,y));
259 if (index == 0 || index == points.size()-1) {
260 this.length = points.get(points.size()-1).x;
262 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
267 private boolean intersects(double ax0, double ay0, double ax1, double ay1,
268 double bx0, double by0, double bx1, double by1) {
270 double d = ((by1-by0)*(ax1-ax0) - (bx1-bx0)*(ay1-ay0));
272 double ua = ((bx1-bx0)*(ay0-by0) - (by1-by0)*(ax0-bx0)) / d;
273 double ub = ((ax1-ax0)*(ay0-by0) - (ay1-ay0)*(ax0-bx0)) / d;
275 return (ua >= 0) && (ua <= 1) && (ub >= 0) && (ub <= 1);
280 public Coordinate[] getFinPoints() {
281 return points.toArray(new Coordinate[0]);
285 public double getSpan() {
287 for (Coordinate c: points) {
295 public String getComponentName() {
296 return "Freeform fin set";
300 @SuppressWarnings("unchecked")
302 public RocketComponent copy() {
303 RocketComponent c = super.copy();
304 ((FreeformFinSet)c).points = (ArrayList<Coordinate>) this.points.clone();
309 * Accept a visitor to this FreeformFinSet in the component hierarchy.
311 * @param theVisitor the visitor that will be called back with a reference to this FreeformFinSet
314 public void accept(ComponentVisitor theVisitor) {
315 theVisitor.visit(this);
318 private void validate(ArrayList<Coordinate> points) throws IllegalFinPointException {
319 final int n = points.size();
320 if (points.get(0).x != 0 || points.get(0).y != 0 ||
321 points.get(n-1).x < 0 || points.get(n-1).y != 0) {
322 throw new IllegalFinPointException("Start or end point illegal.");
324 for (int i=0; i < n-1; i++) {
325 for (int j=i+2; j < n-1; j++) {
326 if (intersects(points.get(i).x, points.get(i).y, points.get(i+1).x, points.get(i+1).y,
327 points.get(j).x, points.get(j).y, points.get(j+1).x, points.get(j+1).y)) {
328 throw new IllegalFinPointException("segments intersect");
331 if (points.get(i).z != 0) {
332 throw new IllegalFinPointException("z-coordinate not zero");