diff --git a/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java b/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java index b81ee55b3..acac70c8c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java @@ -102,7 +102,7 @@ public abstract class ExternalComponent extends RocketComponent { */ @Override public double getComponentMass() { - return material.getDensity() * getComponentVolume(); + return material.getDensity() * getComponentVolume() * getInstanceCount(); } /** diff --git a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java index de2285dc4..4b0c18912 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java +++ b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java @@ -20,7 +20,7 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li private double radius; private double thickness; - private double angleOffsetRadians = Math.PI; + private double angleOffsetRad = Math.PI; private double radialOffset = 0; private int instanceCount = 1; @@ -97,7 +97,7 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li @Override public double getAngleOffset() { - return this.angleOffsetRadians; + return this.angleOffsetRad; } @Override @@ -109,9 +109,9 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li } double clamped_rad = MathUtil.clamp( newAngleRadians, -Math.PI, Math.PI); - if (MathUtil.equals(this.angleOffsetRadians, clamped_rad)) + if (MathUtil.equals(this.angleOffsetRad, clamped_rad)) return; - this.angleOffsetRadians = clamped_rad; + this.angleOffsetRad = clamped_rad; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -159,8 +159,8 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li public Coordinate[] getInstanceOffsets(){ Coordinate[] toReturn = new Coordinate[this.getInstanceCount()]; - final double yOffset = Math.cos(angleOffsetRadians) * (radialOffset); - final double zOffset = Math.sin(angleOffsetRadians) * (radialOffset); + final double yOffset = Math.cos(angleOffsetRad) * (radialOffset); + final double zOffset = Math.sin(angleOffsetRad) * (radialOffset); for ( int index=0; index < this.getInstanceCount(); index++){ toReturn[index] = new Coordinate(index*this.instanceSeparation, yOffset, zOffset); @@ -228,7 +228,10 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li @Override public Coordinate getComponentCG() { - return new Coordinate(length / 2, 0, 0, getComponentMass()); + final double CMx = length / 2 + (instanceSeparation * (instanceCount-1)) / 2; + final double CMy = Math.cos(this.angleOffsetRad)*getOuterRadius(); + final double CMz = Math.sin(this.angleOffsetRad)*getOuterRadius(); + return new Coordinate(CMx, CMy, CMz, getComponentMass()); } @Override diff --git a/core/src/net/sf/openrocket/rocketcomponent/RailButton.java b/core/src/net/sf/openrocket/rocketcomponent/RailButton.java index 499795fc0..734966339 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RailButton.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RailButton.java @@ -51,7 +51,7 @@ public class RailButton extends ExternalComponent implements AnglePositionable, private double radialDistance_m=0; protected static final AngleMethod angleMethod = AngleMethod.RELATIVE; - private double angle_rad = Math.PI; + private double angleOffsetRad = Math.PI; private int instanceCount = 1; private double instanceSeparation = 0; // front-front along the positive rocket axis. i.e. [1,0,0]; @@ -210,7 +210,7 @@ public class RailButton extends ExternalComponent implements AnglePositionable, @Override public double getAngleOffset(){ - return angle_rad; + return angleOffsetRad; } @Override @@ -234,9 +234,9 @@ public class RailButton extends ExternalComponent implements AnglePositionable, double clamped_rad = MathUtil.clamp(angle_rad, -Math.PI, Math.PI); - if (MathUtil.equals(this.angle_rad, clamped_rad)) + if (MathUtil.equals(this.angleOffsetRad, clamped_rad)) return; - this.angle_rad = clamped_rad; + this.angleOffsetRad = clamped_rad; fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE); } @@ -265,8 +265,8 @@ public class RailButton extends ExternalComponent implements AnglePositionable, public Coordinate[] getInstanceOffsets(){ Coordinate[] toReturn = new Coordinate[this.getInstanceCount()]; - final double yOffset = Math.cos(this.angle_rad) * ( this.radialDistance_m ); - final double zOffset = Math.sin(this.angle_rad) * ( this.radialDistance_m ); + final double yOffset = Math.cos(this.angleOffsetRad) * ( this.radialDistance_m ); + final double zOffset = Math.sin(this.angleOffsetRad) * ( this.radialDistance_m ); for ( int index=0; index < this.getInstanceCount(); index++){ toReturn[index] = new Coordinate(index*this.instanceSeparation, yOffset, zOffset); @@ -384,13 +384,14 @@ public class RailButton extends ExternalComponent implements AnglePositionable, if (heightCM > this.totalHeight_m + this.screwHeight_m) { throw new BugException(" bug found while computing the CG of a RailButton: "+this.getName()+"\n height of CG: "+heightCM); } + + final double CMx = (instanceSeparation * (instanceCount-1)) / 2; + final double CMy = Math.cos(this.angleOffsetRad)*heightCM; + final double CMz = Math.sin(this.angleOffsetRad)*heightCM; - final double CMy = Math.cos(this.angle_rad)*heightCM; - final double CMz = Math.sin(this.angle_rad)*heightCM; - - return new Coordinate( 0, CMy, CMz, getComponentMass()); + return new Coordinate( CMx, CMy, CMz, getComponentMass()); } - + @Override public String getComponentName() { return trans.get("RailButton.RailButton"); diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index 977feb3c6..dd27bcc7c 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -65,7 +65,8 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Alpha III Empty Mass is incorrect: ", expRocketDryMass, actualRocketDryMass, EPSILON); double expCMx = 0.1917685523; - Coordinate expCM = new Coordinate(expCMx, 0, 0, expRocketDryMass); + double expCMy = -0.00006340812673; // Slight offset due to launch lug + Coordinate expCM = new Coordinate(expCMx, expCMy, 0, expRocketDryMass); assertEquals("Simple Rocket CM.x is incorrect: ", expCM.x, actualRocketDryCM.x, EPSILON); assertEquals("Simple Rocket CM.y is incorrect: ", expCM.y, actualRocketDryCM.y, EPSILON); assertEquals("Simple Rocket CM.z is incorrect: ", expCM.z, actualRocketDryCM.z, EPSILON); @@ -117,7 +118,8 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Alpha III Total Mass (with motor: " + desig + ") is incorrect: ", expRocketLaunchMass, actualRocketLaunchMass, EPSILON); double expCMx = 0.20996455968266833; - Coordinate expCM = new Coordinate(expCMx, 0, 0, expRocketLaunchMass); + double expCMy = -0.00003845163503; // Slight offset due to launch lug + Coordinate expCM = new Coordinate(expCMx, expCMy, 0, expRocketLaunchMass); assertEquals("Simple Rocket CM.x is incorrect: ", expCM.x, actualRocketLaunchCM.x, EPSILON); assertEquals("Simple Rocket CM.y is incorrect: ", expCM.y, actualRocketLaunchCM.y, EPSILON); assertEquals("Simple Rocket CM.z is incorrect: ", expCM.z, actualRocketLaunchCM.z, EPSILON); diff --git a/core/test/net/sf/openrocket/rocketcomponent/LaunchLugTest.java b/core/test/net/sf/openrocket/rocketcomponent/LaunchLugTest.java index 1df1864f3..4d2e7f594 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/LaunchLugTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/LaunchLugTest.java @@ -63,5 +63,255 @@ public class LaunchLugTest extends BaseTestCase { assertEquals(" LaunchLug #2 is in the wrong place: ", expPos, actPos[1]); } } + + @Test + public void testCMSingleInstance() { + BodyTube bodyTube = new BodyTube(); + LaunchLug lug = new LaunchLug(); + lug.setLength(0.1); + lug.setOuterRadius(0.02); + bodyTube.addChild(lug); + + // Test normal CG + Coordinate CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.05, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.05, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.05, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON); + + + // Change dimensions + lug.setLength(0.05); + lug.setOuterRadius(0.015); + lug.setAngleOffset(0); + + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.025, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.00309761, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.025, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.00309761, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.025, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.00309761, CG.weight, EPSILON); + } + + @Test + public void testCMSingleInstanceOverride() { + BodyTube bodyTube = new BodyTube(); + LaunchLug lug = new LaunchLug(); + lug.setLength(0.1); + lug.setOuterRadius(0.02); + lug.setCGOverridden(true); + lug.setOverrideCGX(0.0123); + bodyTube.addChild(lug); + + // Test normal CG + Coordinate CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON); + + + // Change dimensions + lug.setLength(0.05); + lug.setOuterRadius(0.015); + lug.setAngleOffset(0); + lug.setOverrideCGX(0.0321); + lug.setMassOverridden(true); + lug.setOverrideMass(0.1); + + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.1, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.1, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.1, CG.weight, EPSILON); + } + + @Test + public void testCMMultipleInstances() { + BodyTube bodyTube = new BodyTube(); + LaunchLug lug = new LaunchLug(); + lug.setLength(0.1); + lug.setOuterRadius(0.02); + lug.setInstanceCount(3); + lug.setInstanceSeparation(0.2); + bodyTube.addChild(lug); + + // Test normal CG + Coordinate CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.25, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.25, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.25, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON); + + + // Change dimensions + lug.setLength(0.05); + lug.setOuterRadius(0.015); + lug.setAngleOffset(0); + lug.setInstanceCount(2); + lug.setInstanceSeparation(0.15); + + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.1, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.00619522, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.1, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.00619522, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.1, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.00619522, CG.weight, EPSILON); + } + + @Test + public void testCMMultipleInstancesOverride() { + BodyTube bodyTube = new BodyTube(); + LaunchLug lug = new LaunchLug(); + lug.setLength(0.1); + lug.setOuterRadius(0.02); + lug.setInstanceCount(3); + lug.setInstanceSeparation(0.2); + lug.setCGOverridden(true); + lug.setOverrideCGX(0.0123); + bodyTube.addChild(lug); + + // Test normal CG + Coordinate CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON); + + + // Change dimensions + lug.setLength(0.05); + lug.setOuterRadius(0.015); + lug.setAngleOffset(0); + lug.setInstanceCount(2); + lug.setInstanceSeparation(0.15); + lug.setOverrideCGX(0.0321); + lug.setMassOverridden(true); + lug.setOverrideMass(0.2); + + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.2, CG.weight, EPSILON); + + // Test rotated CG + lug.setAngleOffset(Math.PI / 2); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.2, CG.weight, EPSILON); + + lug.setAngleOffset(-Math.PI / 3); + CG = lug.getCG(); + assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON); + assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON); + assertEquals(" LaunchLug CM has the wrong value: ", 0.2, CG.weight, EPSILON); + } } diff --git a/core/test/net/sf/openrocket/rocketcomponent/RailButtonTest.java b/core/test/net/sf/openrocket/rocketcomponent/RailButtonTest.java new file mode 100644 index 000000000..985299e38 --- /dev/null +++ b/core/test/net/sf/openrocket/rocketcomponent/RailButtonTest.java @@ -0,0 +1,263 @@ +package net.sf.openrocket.rocketcomponent; + +import net.sf.openrocket.util.BaseTestCase.BaseTestCase; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.MathUtil; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class RailButtonTest extends BaseTestCase { + protected final double EPSILON = MathUtil.EPSILON; + + @Test + public void testCMSingleInstance() { + BodyTube bodyTube = new BodyTube(); + RailButton button = new RailButton(); + button.setOuterDiameter(0.05); + button.setTotalHeight(0.05); + bodyTube.addChild(button); + + // Test normal CG + Coordinate CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON); + + + // Change dimensions + button.setOuterDiameter(0.025); + button.setTotalHeight(0.02); + button.setAngleOffset(0); + + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.003930195, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.003930195, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.003930195, CG.weight, EPSILON); + } + + @Test + public void testCMSingleInstanceOverride() { + BodyTube bodyTube = new BodyTube(); + RailButton button = new RailButton(); + button.setOuterDiameter(0.05); + button.setTotalHeight(0.05); + button.setCGOverridden(true); + button.setOverrideCGX(0.0123); + bodyTube.addChild(button); + + // Test normal CG + Coordinate CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON); + + + // Change dimensions + button.setOuterDiameter(0.025); + button.setTotalHeight(0.02); + button.setAngleOffset(0); + button.setOverrideCGX(0.0321); + button.setMassOverridden(true); + button.setOverrideMass(0.1); + + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.1, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.1, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.1, CG.weight, EPSILON); + } + + @Test + public void testCMMultipleInstances() { + BodyTube bodyTube = new BodyTube(); + RailButton button = new RailButton(); + button.setOuterDiameter(0.05); + button.setTotalHeight(0.05); + button.setInstanceCount(3); + button.setInstanceSeparation(0.2); + bodyTube.addChild(button); + + // Test normal CG + Coordinate CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.2, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.2, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.2, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON); + + + // Change dimensions + button.setOuterDiameter(0.025); + button.setTotalHeight(0.02); + button.setAngleOffset(0); + button.setInstanceCount(2); + button.setInstanceSeparation(0.15); + + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.075, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.00786039, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.075, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.00786039, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.075, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.00786039, CG.weight, EPSILON); + } + + @Test + public void testCMMultipleInstancesOverride() { + BodyTube bodyTube = new BodyTube(); + RailButton button = new RailButton(); + button.setOuterDiameter(0.05); + button.setTotalHeight(0.05); + button.setInstanceCount(3); + button.setInstanceSeparation(0.2); + button.setCGOverridden(true); + button.setOverrideCGX(0.0123); + bodyTube.addChild(button); + + // Test normal CG + Coordinate CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON); + + + // Change dimensions + button.setOuterDiameter(0.025); + button.setTotalHeight(0.02); + button.setAngleOffset(0); + button.setInstanceCount(2); + button.setInstanceSeparation(0.15); + button.setOverrideCGX(0.0321); + button.setMassOverridden(true); + button.setOverrideMass(0.2); + + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.2, CG.weight, EPSILON); + + // Test rotated CG + button.setAngleOffset(Math.PI / 2); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.2, CG.weight, EPSILON); + + button.setAngleOffset(-Math.PI / 3); + CG = button.getCG(); + assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON); + assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON); + assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON); + assertEquals(" RailButton CM has the wrong value: ", 0.2, CG.weight, EPSILON); + } + +}