Merge pull request #767 from teyrana/fix/756/bound-box-error

Fixes #756 -- Launch Lug & Stage Dimensions
This commit is contained in:
Daniel Williams 2020-09-01 19:15:16 -04:00 committed by GitHub
commit 86b0b0fcbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 215 additions and 37 deletions

View File

@ -543,9 +543,13 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
* @return the rocket's bounding box (under the selected configuration)
*/
public BoundingBox getBoundingBox() {
if (rocket.getModID() != boundsModID) {
calculateBounds();
}
// if (rocket.getModID() != boundsModID) {
calculateBounds();
// }
if(cachedBounds.isEmpty())
cachedBounds = new BoundingBox(Coordinate.ZERO,Coordinate.X_UNIT);
return cachedBounds;
}
@ -558,11 +562,12 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
InstanceMap map = getActiveInstances();
for (Map.Entry<RocketComponent, java.util.ArrayList<InstanceContext>> entry : map.entrySet()) {
RocketComponent component = entry.getKey();
BoundingBox componentBounds = new BoundingBox();
List<InstanceContext> contexts = entry.getValue();
final RocketComponent component = entry.getKey();
final BoundingBox componentBounds = new BoundingBox();
final List<InstanceContext> contexts = entry.getValue();
if( ! component.isAerodynamic()){
// System.err.println(" << non-aerodynamic");
// all non-aerodynamic components should be surrounded by aerodynamic ones
continue;
}
@ -571,6 +576,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
if (component instanceof BoxBounded) {
final BoundingBox instanceBounds = ((BoxBounded) component).getInstanceBoundingBox();
if(instanceBounds.isEmpty()) {
// probably redundant
// this component is probably non-physical (like an assembly) or has invalid bounds. Skip.
continue;
}
@ -627,7 +633,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
if (rocketBounds.isEmpty()) {
cachedLength = 0;
}
cachedBounds.update( rocketBounds );
cachedBounds = rocketBounds;
}
/**

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.sf.openrocket.util.BoundingBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,7 +26,7 @@ import net.sf.openrocket.util.MathUtil;
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class InnerTube extends ThicknessRingComponent implements AxialPositionable, Clusterable, RadialParent, MotorMount {
public class InnerTube extends ThicknessRingComponent implements AxialPositionable, BoxBounded, Clusterable, RadialParent, MotorMount {
private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(InnerTube.class);
@ -135,6 +136,18 @@ public class InnerTube extends ThicknessRingComponent implements AxialPositionab
}
}
public BoundingBox getInstanceBoundingBox(){
BoundingBox instanceBounds = new BoundingBox();
instanceBounds.update(new Coordinate(this.getLength(), 0,0));
final double r = getOuterRadius();
instanceBounds.update(new Coordinate(0,r,r));
instanceBounds.update(new Coordinate(0,-r,-r));
return instanceBounds;
}
@Override
public int getInstanceCount() {
return cluster.getClusterCount();

View File

@ -8,12 +8,13 @@ import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.ComponentPreset.Type;
import net.sf.openrocket.rocketcomponent.position.*;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
public class LaunchLug extends ExternalComponent implements AnglePositionable, Coaxial, LineInstanceable {
public class LaunchLug extends ExternalComponent implements AnglePositionable, BoxBounded, Coaxial, LineInstanceable {
private static final Translator trans = Application.getTranslator();
@ -252,7 +253,20 @@ public class LaunchLug extends ExternalComponent implements AnglePositionable, C
public int getInstanceCount(){
return this.instanceCount;
}
@Override
public BoundingBox getInstanceBoundingBox() {
BoundingBox instanceBounds = new BoundingBox();
instanceBounds.update(new Coordinate(this.getLength(), 0,0));
final double r = getOuterRadius();
instanceBounds.update(new Coordinate(0,r,r));
instanceBounds.update(new Coordinate(0,-r,-r));
return instanceBounds;
}
@Override
public String getPatternName(){
return (this.getInstanceCount() + "-Line");
@ -281,5 +295,4 @@ public class LaunchLug extends ExternalComponent implements AnglePositionable, C
public void setAngleMethod(AngleMethod newMethod) {
// no-op
}
}

View File

@ -13,6 +13,7 @@ import net.sf.openrocket.rocketcomponent.position.AnglePositionable;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.AxialPositionable;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
@ -22,7 +23,7 @@ import net.sf.openrocket.util.MathUtil;
* @author widget (Daniel Williams)
*
*/
public class RailButton extends ExternalComponent implements AnglePositionable, AxialPositionable, LineInstanceable {
public class RailButton extends ExternalComponent implements AnglePositionable, AxialPositionable, BoxBounded, LineInstanceable {
private static final Translator trans = Application.getTranslator();
@ -210,6 +211,18 @@ public class RailButton extends ExternalComponent implements AnglePositionable,
fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
}
public BoundingBox getInstanceBoundingBox(){
BoundingBox instanceBounds = new BoundingBox();
instanceBounds.update(new Coordinate(0, this.getTotalHeight(), 0));
final double r = this.getOuterDiameter();
instanceBounds.update(new Coordinate(r,r,0));
instanceBounds.update(new Coordinate(-r,-r,0));
return instanceBounds;
}
@Override
public Coordinate[] getInstanceOffsets(){
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
@ -16,7 +17,7 @@ import net.sf.openrocket.util.MathUtil;
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public abstract class RingComponent extends StructuralComponent implements Coaxial {
public abstract class RingComponent extends StructuralComponent implements BoxBounded, Coaxial {
protected boolean outerRadiusAutomatic = false;
protected boolean innerRadiusAutomatic = false;
@ -111,9 +112,18 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
public BoundingBox getInstanceBoundingBox(){
BoundingBox instanceBounds = new BoundingBox();
instanceBounds.update(new Coordinate(this.getLength(), 0,0));
final double r = getOuterRadius();
instanceBounds.update(new Coordinate(0,r,r));
instanceBounds.update(new Coordinate(0,-r,-r));
return instanceBounds;
}
/**
* Return the radial position of the component. The position is the distance
* of the center of the component from the center of the parent component.

View File

@ -556,12 +556,13 @@ public class TestRockets {
//
// This function is used for unit, integration tests, DO NOT CHANGE WITHOUT UPDATING TESTS
public static final Rocket makeBeta(){
Rocket rocket = makeEstesAlphaIII();
final Rocket rocket = makeEstesAlphaIII();
rocket.setName("Kit-bash Beta");
AxialStage sustainerStage = (AxialStage)rocket.getChild(0);
final AxialStage sustainerStage = (AxialStage)rocket.getChild(0);
sustainerStage.setName( "Sustainer Stage");
BodyTube sustainerBody = (BodyTube)sustainerStage.getChild(1);
final BodyTube sustainerBody = (BodyTube)sustainerStage.getChild(1);
sustainerBody.setName("Sustainer Body Tube");
final double sustainerRadius = sustainerBody.getAftRadius();
final double sustainerThickness = sustainerBody.getThickness();
@ -586,7 +587,7 @@ public class TestRockets {
double finRootChord = .05;
double finTipChord = .03;
double finSweep = 0.02;
double finHeight = 0.03;
double finHeight = 0.05;
FinSet finset = new TrapezoidFinSet(finCount, finRootChord, finTipChord, finSweep, finHeight);
finset.setName("Booster Fins");
finset.setThickness( 0.0032);
@ -610,6 +611,15 @@ public class TestRockets {
boosterMMT.setMotorConfig( motorConfig, TEST_FCID_1);
}
boosterBody.addChild(boosterMMT);
LaunchLug lug = new LaunchLug();
lug.setName("Launch Lugs");
lug.setAxialMethod(AxialMethod.TOP);
lug.setAxialOffset(0.0);
lug.setLength(0.050);
lug.setOuterRadius(0.0022);
lug.setInnerRadius(0.0020);
boosterBody.addChild(lug);
}
// Tail Cone

View File

@ -0,0 +1,111 @@
package net.sf.openrocket.rocketcomponent;
import net.sf.openrocket.rocketcomponent.position.AngleMethod;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.TestRockets;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class BoundingBoxTest extends BaseTestCase {
final double EPSILON = MathUtil.EPSILON;
@Test
public void testEstesAlphaIIIBoundingBox(){
final Rocket rocket = TestRockets.makeEstesAlphaIII();
final FlightConfiguration config = rocket.getSelectedConfiguration();
final BoundingBox bounds = config.getBoundingBox();
assertEquals("bounds max x", 0.000000000, bounds.min.x, EPSILON);
assertEquals("bounds max x", 0.270000000, bounds.max.x, EPSILON);
assertEquals("bounds min y", -0.032385640, bounds.min.y, EPSILON);
assertEquals("bounds max y", 0.062000000, bounds.max.y, EPSILON);
assertEquals("bounds min z", -0.054493575, bounds.min.z, EPSILON);
assertEquals("bounds max z", 0.052893575, bounds.max.z, EPSILON);
}
@Test
public void testBetaBoundingBox() {
Rocket rocket = TestRockets.makeBeta();
final FlightConfiguration config = rocket.getSelectedConfiguration();
{ // case A: All Stages
config.setAllStages();
// DEBUG
System.err.println("==== Case A: All Stages ====");
final BoundingBox bounds = config.getBoundingBox();
assertEquals("bounds min x", 0.000000000, bounds.min.x, EPSILON);
assertEquals("bounds max x", 0.335000000, bounds.max.x, EPSILON);
assertEquals("bounds min y", -0.032385640, bounds.min.y, EPSILON);
assertEquals("bounds max y", 0.062000000, bounds.max.y, EPSILON);
assertEquals("bounds min z", -0.054493575, bounds.min.z, EPSILON);
assertEquals("bounds max z", 0.052893575, bounds.max.z, EPSILON);
}
{ // case B: Sustainer Only
config.setOnlyStage(0);
// DEBUG
System.err.println("==== Case B: Sustainer Only ====");
final BoundingBox bounds = config.getBoundingBox();
assertEquals("bounds min x", 0.000000000, bounds.min.x, EPSILON);
assertEquals("bounds max x", 0.270000000, bounds.max.x, EPSILON);
assertEquals("bounds min y", -0.032385640, bounds.min.y, EPSILON);
assertEquals("bounds max y", 0.062000000, bounds.max.y, EPSILON);
assertEquals("bounds min z", -0.054493575, bounds.min.z, EPSILON);
assertEquals("bounds max z", 0.052893575, bounds.max.z, EPSILON);
}
{ // case C: Booster Only
config.setOnlyStage(1);
// DEBUG
System.err.println("==== Case C: Booster Only ====");
System.err.println(rocket.toDebugTree());
final BoundingBox bounds = config.getBoundingBox();
assertEquals("bounds min x", 0.270000000, bounds.min.x, EPSILON);
assertEquals("bounds max x", 0.335000000, bounds.max.x, EPSILON);
assertEquals("bounds min y", -0.032385640, bounds.min.y, EPSILON);
assertEquals("bounds max y", 0.062000000, bounds.max.y, EPSILON);
assertEquals("bounds min z", -0.054493575, bounds.min.z, EPSILON);
assertEquals("bounds max z", 0.052893575, bounds.max.z, EPSILON);
}
}
@Test
public void testFalcon9HBoundingBox() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
// DEBUG
System.err.println(rocket.toDebugTree());
final BoundingBox bounds = rocket.getBoundingBox();
assertEquals( 0.0, bounds.min.x, EPSILON);
assertEquals( 1.364, bounds.max.x, EPSILON);
assertEquals( -0.215500, bounds.min.y, EPSILON);
assertEquals( 0.215500, bounds.max.y, EPSILON);
assertEquals( -0.12069451, bounds.min.z, EPSILON);
assertEquals( 0.12069451, bounds.max.z, EPSILON);
}
}

View File

@ -423,10 +423,12 @@ public class RocketFigure extends AbstractScaleFigure {
@Override
protected void updateSubjectDimensions() {
// calculate bounds, and store in class variables
BoundingBox newBounds = rocket.getSelectedConfiguration().getBoundingBox();
if(newBounds.isEmpty())
newBounds = new BoundingBox(Coordinate.ZERO,Coordinate.X_UNIT);
final FlightConfiguration config = rocket.getSelectedConfiguration().clone();
// Explicitly zoom & draw at a scale to fit the entire rocket, but only show the selected stages.
config.setAllStages();
final BoundingBox newBounds = config.getBoundingBox();
final double maxR = Math.max( Math.hypot(newBounds.min.y, newBounds.min.z),
Math.hypot(newBounds.max.y, newBounds.max.z));
@ -451,19 +453,19 @@ public class RocketFigure extends AbstractScaleFigure {
@Override
protected void updateCanvasOrigin() {
final int subjectWidth = (int)(subjectBounds_m.getWidth()*scale);
final int subjectHeight = (int)(subjectBounds_m.getHeight()*scale);
final int mid_x = (Math.max(getWidth(), subjectWidth) / 2);
if (currentViewType == RocketPanel.VIEW_TYPE.BackView){
final int newOriginX = mid_x;
final int newOriginY = borderThickness_px.height + getHeight() / 2;
originLocation_px = new Point(newOriginX, newOriginY);
}else if (currentViewType == RocketPanel.VIEW_TYPE.SideView){
final int newOriginX = mid_x - (int) ((subjectBounds_m.getWidth() * scale) / 2);
final int newOriginY = Math.max(getHeight(), subjectHeight + 2*borderThickness_px.height )/ 2;
originLocation_px = new Point(newOriginX, newOriginY);
}
final int subjectHeight = (int)(subjectBounds_m.getHeight()*scale);
final int mid_x = (Math.max(getWidth(), subjectWidth) / 2);
if (currentViewType == RocketPanel.VIEW_TYPE.BackView){
final int newOriginX = mid_x;
final int newOriginY = borderThickness_px.height + getHeight() / 2;
originLocation_px = new Point(newOriginX, newOriginY);
}else if (currentViewType == RocketPanel.VIEW_TYPE.SideView){
final int newOriginX = mid_x - (subjectWidth / 2);
final int newOriginY = Math.max(getHeight(), subjectHeight + 2*borderThickness_px.height )/ 2;
originLocation_px = new Point(newOriginX, newOriginY);
}
}
}