1 package net.sf.openrocket.rocketcomponent;
3 import java.util.Arrays;
4 import java.util.Collections;
7 import net.sf.openrocket.l10n.Translator;
8 import net.sf.openrocket.logging.LogHelper;
9 import net.sf.openrocket.startup.Application;
10 import net.sf.openrocket.util.ArrayList;
11 import net.sf.openrocket.util.BugException;
12 import net.sf.openrocket.util.Coordinate;
15 public class FreeformFinSet extends FinSet {
16 private static final LogHelper log = Application.getLogger();
17 private static final Translator trans = Application.getTranslator();
19 private ArrayList<Coordinate> points = new ArrayList<Coordinate>();
21 public FreeformFinSet() {
22 points.add(Coordinate.NUL);
23 points.add(new Coordinate(0.025, 0.05));
24 points.add(new Coordinate(0.075, 0.05));
25 points.add(new Coordinate(0.05, 0));
31 public FreeformFinSet(Coordinate[] finpoints) throws IllegalFinPointException {
36 public FreeformFinSet(FinSet finset) {
37 Coordinate[] finpoints = finset.getFinPoints();
38 this.copyFrom(finset);
41 for (Coordinate c: finpoints) {
44 this.length = points.get(points.size()-1).x - points.get(0).x;
50 * Convert an existing fin set into a freeform fin set. The specified
51 * fin set is taken out of the rocket tree (if any) and the new component
52 * inserted in its stead.
54 * The specified fin set should not be used after the call!
56 * @param finset the fin set to convert.
57 * @return the new freeform fin set.
59 public static FreeformFinSet convertFinSet(FinSet finset) {
60 log.info("Converting " + finset.getComponentName() + " into freeform fin set");
61 final RocketComponent root = finset.getRoot();
62 FreeformFinSet freeform;
63 List<RocketComponent> toInvalidate = Collections.emptyList();
66 if (root instanceof Rocket) {
67 ((Rocket) root).freeze();
70 // Get fin set position and remove fin set
71 final RocketComponent parent = finset.getParent();
74 position = parent.getChildPosition(finset);
75 parent.removeChild(position);
81 // Create the freeform fin set
82 Coordinate[] finpoints = finset.getFinPoints();
84 freeform = new FreeformFinSet(finpoints);
85 } catch (IllegalFinPointException e) {
86 throw new BugException("Illegal fin points when converting existing fin to " +
87 "freeform fin, fin=" + finset + " points=" + Arrays.toString(finpoints),
91 // Copy component attributes
92 toInvalidate = freeform.copyFrom(finset);
95 final String componentTypeName = finset.getComponentName();
96 final String name = freeform.getName();
98 if (name.startsWith(componentTypeName)) {
99 freeform.setName(freeform.getComponentName() +
100 name.substring(componentTypeName.length()));
103 // Add freeform fin set to parent
104 if (parent != null) {
105 parent.addChild(freeform, position);
109 if (root instanceof Rocket) {
110 ((Rocket) root).thaw();
112 // Invalidate components after events have been fired
113 for (RocketComponent c : toInvalidate) {
123 * Add a fin point between indices <code>index-1</code> and <code>index</code>.
124 * The point is placed at the midpoint of the current segment.
126 * @param index the fin point before which to add the new point.
128 public void addPoint(int index) {
129 double x0, y0, x1, y1;
131 x0 = points.get(index - 1).x;
132 y0 = points.get(index - 1).y;
133 x1 = points.get(index).x;
134 y1 = points.get(index).y;
136 points.add(index, new Coordinate((x0 + x1) / 2, (y0 + y1) / 2));
137 // adding a point within the segment affects neither mass nor aerodynamics
138 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
143 * Remove the fin point with the given index. The first and last fin points
144 * cannot be removed, and will cause an <code>IllegalFinPointException</code>
147 * @param index the fin point index to remove
148 * @throws IllegalFinPointException if removing would result in invalid fin planform
150 public void removePoint(int index) throws IllegalFinPointException {
151 if (index == 0 || index == points.size() - 1) {
152 throw new IllegalFinPointException("cannot remove first or last point");
155 ArrayList<Coordinate> copy = this.points.clone();
160 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
164 public int getPointCount() {
165 return points.size();
168 public void setPoints(Coordinate[] points) throws IllegalFinPointException {
169 ArrayList<Coordinate> list = new ArrayList<Coordinate>(points.length);
170 for (Coordinate p : points) {
176 this.length = points[points.length - 1].x;
177 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
182 * Set the point at position <code>i</code> to coordinates (x,y).
184 * Note that this method enforces basic fin shape restrictions (non-negative y,
185 * first and last point locations) silently, but throws an
186 * <code>IllegalFinPointException</code> if the point causes fin segments to
189 * Moving of the first point in the X-axis is allowed, but this actually moves
190 * all of the other points the corresponding distance back.
192 * @param index the point index to modify.
193 * @param x the x-coordinate.
194 * @param y the y-coordinate.
195 * @throws IllegalFinPointException if the specified fin point would cause intersecting
198 public void setPoint(int index, double x, double y) throws IllegalFinPointException {
202 double x0, y0, x1, y1;
207 x = Math.min(x, points.get(points.size() - 1).x);
211 x1 = points.get(1).x;
212 y1 = points.get(1).y;
214 } else if (index == points.size() - 1) {
219 x0 = points.get(index - 1).x;
220 y0 = points.get(index - 1).y;
226 x0 = points.get(index - 1).x;
227 y0 = points.get(index - 1).y;
228 x1 = points.get(index + 1).x;
229 y1 = points.get(index + 1).y;
235 // Check for intersecting
236 double px0, py0, px1, py1;
239 for (int i = 1; i < points.size(); i++) {
240 px1 = points.get(i).x;
241 py1 = points.get(i).y;
243 if (i != index - 1 && i != index && i != index + 1) {
244 if (intersects(x0, y0, x, y, px0, py0, px1, py1)) {
245 throw new IllegalFinPointException("segments intersect");
248 if (i != index && i != index + 1 && i != index + 2) {
249 if (intersects(x, y, x1, y1, px0, py0, px1, py1)) {
250 throw new IllegalFinPointException("segments intersect");
260 System.out.println("Set point zero to x:" + x);
261 for (int i = 1; i < points.size(); i++) {
262 Coordinate c = points.get(i);
263 points.set(i, c.setX(c.x - x));
268 points.set(index, new Coordinate(x, y));
271 if (index == 0 || index == points.size() - 1) {
272 this.length = points.get(points.size() - 1).x;
274 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
279 private boolean intersects(double ax0, double ay0, double ax1, double ay1,
280 double bx0, double by0, double bx1, double by1) {
282 double d = ((by1 - by0) * (ax1 - ax0) - (bx1 - bx0) * (ay1 - ay0));
284 double ua = ((bx1 - bx0) * (ay0 - by0) - (by1 - by0) * (ax0 - bx0)) / d;
285 double ub = ((ax1 - ax0) * (ay0 - by0) - (ay1 - ay0) * (ax0 - bx0)) / d;
287 return (ua >= 0) && (ua <= 1) && (ub >= 0) && (ub <= 1);
292 public Coordinate[] getFinPoints() {
293 return points.toArray(new Coordinate[0]);
297 public double getSpan() {
299 for (Coordinate c : points) {
307 public String getComponentName() {
308 //// Freeform fin set
309 return trans.get("FreeformFinSet.FreeformFinSet");
314 protected RocketComponent copyWithOriginalID() {
315 RocketComponent c = super.copyWithOriginalID();
316 ((FreeformFinSet) c).points = this.points.clone();
321 * Accept a visitor to this FreeformFinSet in the component hierarchy.
323 * @param theVisitor the visitor that will be called back with a reference to this FreeformFinSet
326 public void accept(ComponentVisitor theVisitor) {
327 theVisitor.visit(this);
330 private void validate(ArrayList<Coordinate> points) throws IllegalFinPointException {
331 final int n = points.size();
332 if (points.get(0).x != 0 || points.get(0).y != 0 ||
333 points.get(n - 1).x < 0 || points.get(n - 1).y != 0) {
334 throw new IllegalFinPointException("Start or end point illegal.");
336 for (int i = 0; i < n - 1; i++) {
337 for (int j = i + 2; j < n - 1; j++) {
338 if (intersects(points.get(i).x, points.get(i).y, points.get(i + 1).x, points.get(i + 1).y,
339 points.get(j).x, points.get(j).y, points.get(j + 1).x, points.get(j + 1).y)) {
340 throw new IllegalFinPointException("segments intersect");
343 if (points.get(i).z != 0) {
344 throw new IllegalFinPointException("z-coordinate not zero");