diff --git a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java index e1c22a7f1..94a4f80b9 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java @@ -103,6 +103,43 @@ public class FreeformFinSet extends FinSet { } return freeform; } + + /** + * Converts a point of this fin set to edit into a point for a config listener to edit. + * + * The editing is as follows: + * 1) Editing the first point of this fin set will always edit the first point of the listener set + * 2) Editing the last point of this fin set will always edit the last point of the listener set + * 3) Editing any other point of this fin set will edit the corresponding point of the listener set, except + * for when the current point is not the last of this set, but it is the last of the listener set. In that + * case, no listener point will be edited. + * + * @param listener the listener which point needs to be edited + * @param index the point index of this fin set that is being edited + * @return the point index of the listener fin set that needs to be edited. Returns -1 if the listener's point should not be edited. + */ + private int getConfigListenerPointIdx(FreeformFinSet listener, int index) { + /* + The editing is as follows: + 1) Editing the first point of this fin set will always edit the first point of the listener set + 2) Editing the last point of this fin set will always edit the last point of the listener set + 3) Editing any other point of this fin set will edit the corresponding point of the listener set, except + for when the current point is not the last of this set, but it is the last of the listener set. In that + case, no listener point will be edited. + */ + if (index == this.points.size() - 1) { + // If editing the last point, also edit the last point of the listener + return listener.getPointCount() - 1; + } else { + if (index == listener.getPointCount() - 1) { + // If editing the last point of the listener, but not the last point of this fin set, don't edit the listener + return -1; + } else { + // Index-wise editing + return index; + } + } + } /** * Add a fin point between indices index-1 and index. @@ -111,7 +148,22 @@ public class FreeformFinSet extends FinSet { * @param index the fin point before which to add the new point. * @param location the target location to create the new point at */ - public void addPoint(int index, Point2D.Double location) { + public void addPoint(int index, Point2D.Double location) throws IllegalFinPointException { + if (index < 1 || index > points.size() - 1) { + throw new IllegalFinPointException("Cannot add new point before the first or after the last point"); + } + + for (RocketComponent listener : configListeners) { + if (listener instanceof FreeformFinSet) { + try { + int listenerIdx = getConfigListenerPointIdx((FreeformFinSet) listener, index); + ((FreeformFinSet) listener).addPoint(listenerIdx, location); + } catch (IllegalFinPointException ignored) { + // ignore + } + } + } + // new method: add new point at closest point points.add(index, new Coordinate(location.x, location.y)); @@ -131,6 +183,20 @@ public class FreeformFinSet extends FinSet { if (index == 0 || index == points.size() - 1) { throw new IllegalFinPointException("cannot remove first or last point"); } + if (index < 0 || index >= points.size() - 1) { + throw new IllegalFinPointException("index out of range"); + } + + for (RocketComponent listener : configListeners) { + if (listener instanceof FreeformFinSet) { + try { + int listenerIdx = getConfigListenerPointIdx((FreeformFinSet) listener, index); + ((FreeformFinSet) listener).removePoint(listenerIdx); + } catch (IllegalFinPointException ignored) { + // ignore + } + } + } // copy the old list in case the operation fails ArrayList copy = new ArrayList<>(this.points); @@ -226,11 +292,26 @@ public class FreeformFinSet extends FinSet { * @param xRequest the x-coordinate. * @param yRequest the y-coordinate. */ - public void setPoint(final int index, final double xRequest, final double yRequest) { - if(null == this.getParent()) { + public void setPoint(final int index, final double xRequest, final double yRequest) throws IllegalFinPointException { + if (this.getParent() == null) { return; } + if (index < 0 || index > this.points.size() - 1) { + throw new IllegalFinPointException("index out of range"); + } + + for (RocketComponent listener : configListeners) { + if (listener instanceof FreeformFinSet) { + try { + int listenerIdx = getConfigListenerPointIdx((FreeformFinSet) listener, index); + ((FreeformFinSet) listener).setPoint(listenerIdx, xRequest, yRequest); + } catch (IllegalFinPointException ignored) { + // Ignore + } + } + } + // if the new x,y would cause a fin larger than our max-size, limit the new request: double xAccept = xRequest; double yAccept = yRequest; diff --git a/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java index 8e17dfa71..9e14b2050 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.awt.geom.Point2D; @@ -432,7 +433,11 @@ public class FreeformFinSetTest extends BaseTestCase { // / | // +=====+ Point2D.Double toAdd = new Point2D.Double(1.01, 0.8); - fin.addPoint(3, toAdd); + try { + fin.addPoint(3, toAdd); + } catch (IllegalFinPointException e) { + fail("IllegalFinPointException thrown"); + } assertEquals(5, fin.getPointCount()); final Coordinate added = fin.getFinPoints()[3]; @@ -441,7 +446,7 @@ public class FreeformFinSetTest extends BaseTestCase { } @Test - public void testSetFirstPoint() { + public void testSetFirstPoint() throws IllegalFinPointException { // more transitions trigger more complicated positioning math: final Rocket rkt = createTemplateRocket(); final Transition tailCone = (Transition) rkt.getChild(0).getChild(2); @@ -605,7 +610,7 @@ public class FreeformFinSetTest extends BaseTestCase { } @Test - public void testSetLastPoint() { + public void testSetLastPoint() throws IllegalFinPointException { final Rocket rkt = createTemplateRocket(); final Transition tailCone = (Transition) rkt.getChild(0).getChild(2); final FreeformFinSet fins = createFinOnConicalTransition(tailCone); @@ -771,7 +776,7 @@ public class FreeformFinSetTest extends BaseTestCase { } @Test - public void testSetInteriorPoint() { + public void testSetInteriorPoint() throws IllegalFinPointException { final Rocket rkt = createTemplateRocket(); final Transition tailCone = (Transition) rkt.getChild(0).getChild(2); final FreeformFinSet fins = this.createFinOnConicalTransition(tailCone); @@ -875,7 +880,7 @@ public class FreeformFinSetTest extends BaseTestCase { } @Test - public void testSetFirstPoint_clampToLast() { + public void testSetFirstPoint_clampToLast() throws IllegalFinPointException { final Rocket rkt = createTemplateRocket(); final Transition tailCone = (Transition) rkt.getChild(0).getChild(2); final FreeformFinSet fins = this.createFinOnConicalTransition(tailCone); @@ -906,7 +911,7 @@ public class FreeformFinSetTest extends BaseTestCase { } @Test - public void testSetPoint_otherPoint(){ + public void testSetPoint_otherPoint() throws IllegalFinPointException { // combine the simple case with the complicated to ensure that the simple case is flagged, tested, and debugged before running the more complicated case... { // setting points on a Tube Body is the simpler case. Test this first: final Rocket rkt = createTemplateRocket(); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 66ba5710c..47ade763f 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -466,7 +466,7 @@ public class FreeformFinSetConfig extends FinSetConfig { * Insert a new fin point between the currently selected point and the next point. * The coordinates of the new point will be the average of the two points. */ - private void insertPoint() { + private void insertPoint() throws IllegalFinPointException { int currentPointIdx = table.getSelectedRow(); if (currentPointIdx == -1 || currentPointIdx >= table.getRowCount() - 1) { return; @@ -532,7 +532,11 @@ public class FreeformFinSetConfig extends FinSetConfig { final int segmentIndex = getSegment(event); if (segmentIndex >= 0) { Point2D.Double point = getCoordinates(event); - finset.addPoint(segmentIndex, point); + try { + finset.addPoint(segmentIndex, point); + } catch (IllegalFinPointException e) { + throw new RuntimeException(e); + } dragIndex = segmentIndex; dragPoint = event.getPoint(); @@ -554,7 +558,11 @@ public class FreeformFinSetConfig extends FinSetConfig { Point2D.Double point = getCoordinates(event); final FreeformFinSet finset = (FreeformFinSet) component; - finset.setPoint(dragIndex, point.x, point.y); + try { + finset.setPoint(dragIndex, point.x, point.y); + } catch (IllegalFinPointException e) { + throw new RuntimeException(e); + } dragPoint.x = event.getX(); dragPoint.y = event.getY(); @@ -782,6 +790,8 @@ public class FreeformFinSetConfig extends FinSetConfig { updateFields(); } catch (NumberFormatException ignore) { log.warn("ignoring NumberFormatException while editing a Freeform Fin"); + } catch (IllegalFinPointException e) { + throw new RuntimeException(e); } } } @@ -800,7 +810,11 @@ public class FreeformFinSetConfig extends FinSetConfig { @Override public void actionPerformed(ActionEvent e) { - insertPoint(); + try { + insertPoint(); + } catch (IllegalFinPointException ex) { + throw new RuntimeException(ex); + } } @Override