diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/AnglePositionSetter.java b/core/src/net/sf/openrocket/file/openrocket/importt/AnglePositionSetter.java index 06b6d97d2..4843f6872 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/AnglePositionSetter.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/AnglePositionSetter.java @@ -20,9 +20,9 @@ class AnglePositionSetter implements Setter { double pos; try { - pos = Double.parseDouble(value) * Math.PI / 180.0 ; + pos = Math.toRadians(Double.parseDouble(value)); } catch (NumberFormatException e) { - warnings.add(String.format("Warning: invalid value radius position. value=%s class: %s", value, c.getClass().getCanonicalName() )); + warnings.add(String.format("Warning: invalid angle position. value=%s (degrees) class: %s", value, c.getClass().getCanonicalName() )); return; } diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java index c7d5ded18..ef6c25212 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/FinSetSaver.java @@ -20,7 +20,7 @@ public class FinSetSaver extends ExternalComponentSaver { elements.add("" + fins.getThickness() + ""); elements.add("" + fins.getCrossSection().name().toLowerCase(Locale.ENGLISH) + ""); - elements.add("" + (fins.getCantAngle() * 180.0 / Math.PI) + ""); + elements.add("" + Math.toDegrees(fins.getCantAngle()) + ""); // Save fin tabs only if they exist (compatibility with file version < 1.1) if (!MathUtil.equals(fins.getTabHeight(), 0) && diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 54b8b683b..33b6e0e2e 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -35,7 +35,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab /** * Maximum allowed cant of fins. */ - public static final double MAX_CANT = (15.0 * Math.PI / 180); + public static final double MAX_CANT_RADIANS = (15.0 * Math.PI / 180); public enum CrossSection { //// Square @@ -71,20 +71,20 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab /** * Rotation about the x-axis by 2*PI/fins. */ - private Transformation finRotation = Transformation.IDENTITY; + private Transformation finRotationIncrement = Transformation.IDENTITY; /** * Rotation angle of the first fin. Zero corresponds to the positive y-axis. */ private AngleMethod angleMethod = AngleMethod.RELATIVE; - private double firstFinOffset = 0; + private double firstFinOffsetRadians = 0; private Transformation baseRotation = Transformation.IDENTITY; // initially, rotate by 0 degrees. /** * Cant angle of fins. */ - private double cantAngle = 0; + private double cantRadians = 0; /* Cached value: */ private Transformation cantRotation = null; @@ -164,13 +164,13 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab n = 8; finCount = n; - finRotation = Transformation.rotate_x(2 * Math.PI / finCount); + finRotationIncrement = Transformation.rotate_x(2 * Math.PI / finCount); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } public Transformation getFinRotationTransformation() { - return finRotation; + return finRotationIncrement; } @Override @@ -194,28 +194,32 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab setAngleOffset(r); } + /** + * @return angle current cant angle, in radians + */ public double getCantAngle() { - return cantAngle; + return cantRadians; } - public void setCantAngle(double cant) { - cant = MathUtil.clamp(cant, -MAX_CANT, MAX_CANT); - if (MathUtil.equals(cant, cantAngle)) + /** + * + * @param cant -- new cant angle, in radians + */ + public void setCantAngle(final double newCantRadians) { + final double clampedCant = MathUtil.clamp(newCantRadians, -MAX_CANT_RADIANS, MAX_CANT_RADIANS); + if (MathUtil.equals(clampedCant, this.cantRadians)) return; - this.cantAngle = cant; + this.cantRadians = clampedCant; + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - + public Transformation getCantRotation() { - if (cantRotation == null) { - if (MathUtil.equals(cantAngle, 0)) { + if( null == cantRotation ) { + if (MathUtil.equals(this.cantRadians, 0)) { cantRotation = Transformation.IDENTITY; } else { - Transformation t = new Transformation(-length / 2, 0, 0); - t = Transformation.rotate_y(cantAngle).applyTransformation(t); - t = new Transformation(length / 2, 0, 0).applyTransformation(t); - cantRotation = t; + cantRotation = Transformation.rotate_y(cantRadians); } } return cantRotation; @@ -928,16 +932,22 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab @Override public double getAngleOffset() { - return firstFinOffset; + return firstFinOffsetRadians; } - @Override - public void setAngleOffset(double angle) { - angle = MathUtil.reduce180(angle); - if (MathUtil.equals(angle, firstFinOffset)) + public void setAngleOffset(final double angleRadians) { + final double reducedAngle = MathUtil.reducePI(angleRadians); + if (MathUtil.equals(reducedAngle, firstFinOffsetRadians)) return; - firstFinOffset = angle; + firstFinOffsetRadians = reducedAngle; + + if (MathUtil.equals(this.firstFinOffsetRadians, 0)) { + baseRotation = Transformation.IDENTITY; + } else { + baseRotation = Transformation.rotate_x(firstFinOffsetRadians); + } + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -947,13 +957,12 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab } @Override - public double[] getInstanceAngles(){ - final double baseAngle = getAngleOffset(); - final double incrAngle = getInstanceAngleIncrement(); + public double[] getInstanceAngles() { + final double angleIncrementRadians = getInstanceAngleIncrement(); double[] result = new double[ getFinCount()]; - for( int i=0; i copyFrom(RocketComponent c) { FinSet src = (FinSet) c; this.finCount = src.finCount; - this.finRotation = src.finRotation; - this.firstFinOffset = src.firstFinOffset; - this.cantAngle = src.cantAngle; + this.finRotationIncrement = src.finRotationIncrement; + this.firstFinOffsetRadians = src.firstFinOffsetRadians; + this.cantRadians = src.cantRadians; this.cantRotation = src.cantRotation; this.thickness = src.thickness; this.crossSection = src.crossSection; @@ -1066,7 +1075,6 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab } /** -<<<<<<< HEAD * use this for calculating physical properties, and routine drawing * * @return points representing the fin-root points, relative to ( x: fin-front, y: centerline ) i.e. relto: fin Component reference point @@ -1226,22 +1234,22 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab checkState(); final double bodyRadius = this.getBodyRadius(); + + // already includes the base rotation final double[] angles = getInstanceAngles(); - final Transformation localCantRotation = getCantRotation(); - + final Transformation localCantRotation = new Transformation(length / 2, 0, 0) + .applyTransformation(getCantRotation()) + .applyTransformation(new Transformation(-length / 2, 0, 0)); + Coordinate[] toReturn = new Coordinate[finCount]; for (int instanceNumber = 0; instanceNumber < finCount; instanceNumber++) { - final double curY = bodyRadius * Math.cos(angles[instanceNumber]); - final double curZ = bodyRadius * Math.sin(angles[instanceNumber]); - - final Coordinate naiveLocation = new Coordinate(0, curY, curZ); - - final Coordinate adjustedLocation = baseRotation.transform(localCantRotation.transform( naiveLocation)); - - toReturn[instanceNumber] = adjustedLocation; + final Coordinate raw = new Coordinate( 0, bodyRadius, 0); + final Coordinate canted = localCantRotation.transform(raw); + final Coordinate rotated = Transformation.rotate_x(angles[instanceNumber]).transform(canted); + toReturn[instanceNumber] = rotated; } - + return toReturn; } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/position/AnglePositionable.java b/core/src/net/sf/openrocket/rocketcomponent/position/AnglePositionable.java index 577b3bdf2..33831856b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/position/AnglePositionable.java +++ b/core/src/net/sf/openrocket/rocketcomponent/position/AnglePositionable.java @@ -2,8 +2,14 @@ package net.sf.openrocket.rocketcomponent.position; public interface AnglePositionable { + /** + * @return angle to the first element, in radians + */ public double getAngleOffset(); + /** + * @param new offset angle, in radians + */ public void setAngleOffset(final double angle); public AngleMethod getAngleMethod( ); diff --git a/core/src/net/sf/openrocket/util/MathUtil.java b/core/src/net/sf/openrocket/util/MathUtil.java index e1e986b24..ef6cc4956 100644 --- a/core/src/net/sf/openrocket/util/MathUtil.java +++ b/core/src/net/sf/openrocket/util/MathUtil.java @@ -211,10 +211,13 @@ public class MathUtil { /** * Reduce the angle x to the range 0 - 2*PI. - * @param x Original angle. + * + * @deprecated function refers to units:degrees, but operates in units:radians. Please use 'MathUtil.reduce2PI' + * @param x Original angle. * @return The equivalent angle in the range 0 ... 2*PI. */ - public static double reduce360(double x) { + @Deprecated + public static double reduce360(double x) { double d = Math.floor(x / (2 * Math.PI)); return x - d * 2 * Math.PI; } @@ -224,9 +227,11 @@ public class MathUtil { * * Either -PI and PI might be returned, depending on the rounding function. * - * @param x Original angle. + * @deprecated function refers to units:degrees, but operates in units:radians. Please use 'MathUtil.reducePI' + * @param x Original angle. * @return The equivalent angle in the range -PI ... PI. */ + @Deprecated public static double reduce180(double x) { double d = Math.rint(x / (2 * Math.PI)); return x - d * 2 * Math.PI; diff --git a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java index 3dd94dd5b..15a39a401 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java @@ -50,8 +50,24 @@ public class FinSetTest extends BaseTestCase { fins.setFilletRadius(0.0); - return fins; - } + fins.setAngleMethod(AngleMethod.FIXED); + fins.setAngleOffset(Math.toRadians(90.0)); + + fins.setCantAngle(Math.toRadians(3.0)); + + return fins; + } + + @Test + public void testAngleOffset() { + final FinSet fins = FinSetTest.createSimpleFin(); + + assertEquals("Angle Offset Doesn't match!", Math.PI/2, fins.getAngleOffset(), EPSILON); + assertEquals("Angle Offset Doesn't match!", 90.0, Math.toDegrees(fins.getAngleOffset()), EPSILON); + + assertEquals("Cant angle doesn't match!", Math.PI/60, fins.getCantAngle(), EPSILON); + assertEquals("Cant angle doesn't match!", 3.0, Math.toDegrees(fins.getCantAngle()), EPSILON); + } @Test public void testTabLocation() { diff --git a/swing/src/net/sf/openrocket/gui/configdialog/EllipticalFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/EllipticalFinSetConfig.java index e37ffa058..9e7fa4c68 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/EllipticalFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/EllipticalFinSetConfig.java @@ -69,14 +69,14 @@ public class EllipticalFinSetConfig extends FinSetConfig { panel.add(label); m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE, - -FinSet.MAX_CANT, FinSet.MAX_CANT); + -FinSet.MAX_CANT_RADIANS, FinSet.MAX_CANT_RADIANS); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); - panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT, FinSet.MAX_CANT)), + panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT_RADIANS, FinSet.MAX_CANT_RADIANS)), "w 100lp, wrap"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 37ccc6118..69f84d0a0 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -134,14 +134,14 @@ public class FreeformFinSetConfig extends FinSetConfig { label.setToolTipText(trans.get("FreeformFinSetCfg.lbl.ttip.Fincant")); panel.add(label); - m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE, -FinSet.MAX_CANT, FinSet.MAX_CANT); + m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE, -FinSet.MAX_CANT_RADIANS, FinSet.MAX_CANT_RADIANS); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); - panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT, FinSet.MAX_CANT)), "w 100lp, wrap 40lp"); + panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT_RADIANS, FinSet.MAX_CANT_RADIANS)), "w 100lp, wrap 40lp"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/TrapezoidFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/TrapezoidFinSetConfig.java index 1035009c1..804d698e9 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/TrapezoidFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/TrapezoidFinSetConfig.java @@ -75,14 +75,14 @@ public class TrapezoidFinSetConfig extends FinSetConfig { label.setToolTipText(trans.get("TrapezoidFinSetCfg.lbl.ttip.Fincant")); panel.add(label); - final DoubleModel cantModel = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE, -FinSet.MAX_CANT, FinSet.MAX_CANT); + final DoubleModel cantModel = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE, -FinSet.MAX_CANT_RADIANS, FinSet.MAX_CANT_RADIANS); final JSpinner cantSpinner = new JSpinner(cantModel.getSpinnerModel()); cantSpinner.setEditor(new SpinnerEditor(cantSpinner)); panel.add(cantSpinner, "growx"); panel.add(new UnitSelector(cantModel), "growx"); - panel.add(new BasicSlider(cantModel.getSliderModel(-FinSet.MAX_CANT, FinSet.MAX_CANT)), + panel.add(new BasicSlider(cantModel.getSliderModel(-FinSet.MAX_CANT_RADIANS, FinSet.MAX_CANT_RADIANS)), "w 100lp, wrap"); } diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java index 4fb738e9a..29d98a266 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/FinSetShapes.java @@ -30,7 +30,7 @@ public class FinSetShapes extends RocketComponentShape { */ final Transformation cantRotation = finset.getCantRotation(); - final Transformation compositeTransform = cantRotation.applyTransformation( transformation); + final Transformation compositeTransform = transformation.applyTransformation(cantRotation); Coordinate finPoints[] = finset.getFinPoints(); Coordinate tabPoints[] = finset.getTabPoints(); @@ -117,47 +117,37 @@ public class FinSetShapes extends RocketComponentShape { return new Shape[]{p}; } - - // TODO: LOW: Jagged shapes from back draw incorrectly. private static Shape[] cantedShapesBack(FinSet finset, Transformation transformation) { - int i; - int fins = finset.getFinCount(); + double thickness = finset.getThickness(); - Transformation cantRotation = finset.getCantRotation(); - Coordinate[] sidePoints; Coordinate[] backPoints; int maxIndex; Coordinate[] points = finset.getFinPoints(); + + // this loop finds the index @ max-y, as visible from the back for (maxIndex = points.length-1; maxIndex > 0; maxIndex--) { if (points[maxIndex-1].y < points[maxIndex].y) break; } - - points = cantRotation.transform( points ); -// transformPoints(points,new Transformation(0,radius,0)); - points = transformation.transform( points ); - + + Transformation cantTransform = finset.getCantRotation(); + final Transformation compositeTransform = transformation.applyTransformation(cantTransform); sidePoints = new Coordinate[points.length]; backPoints = new Coordinate[2*(points.length-maxIndex)]; - double sign; - if (finset.getCantAngle() > 0) { - sign = 1.0; - } else { - sign = -1.0; - } - - // Calculate points for the side panel - for (i=0; i < points.length; i++) { + double sign = Math.copySign(1.0, finset.getCantAngle()); + + // Calculate points for the visible side panel + for (int i=0; i < points.length; i++) { sidePoints[i] = points[i].add(0,0,sign*thickness/2); } // Calculate points for the back portion - i=0; + int i=0; for (int j=points.length-1; j >= maxIndex; j--, i++) { backPoints[i] = points[j].add(0,0,sign*thickness/2); } @@ -168,35 +158,24 @@ public class FinSetShapes extends RocketComponentShape { // Generate shapes Shape[] s; if (thickness > 0.0005) { - - s = new Shape[fins*2]; - for (int fin=0; fin