[refactor] Refactored FlightConfiguration.calculateBounds to be clearer

This commit is contained in:
Daniel_M_Williams 2020-06-20 12:59:24 -04:00
parent 056c6b2b39
commit 0a9df5cc64
5 changed files with 101 additions and 51 deletions

View File

@ -0,0 +1,15 @@
package net.sf.openrocket.rocketcomponent;
import net.sf.openrocket.util.BoundingBox;
public interface BoxBounded {
/**
* Get a bounding box for a single instance of this component, from its own reference point.
* This is expected to be compbined with a InstanceContext for bounds in the global / rocket frame.
*
* @return BoundingBox from the instance's reference point.
*/
BoundingBox getInstanceBoundingBox();
}

View File

@ -25,13 +25,9 @@ import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Transformation; import net.sf.openrocket.util.Transformation;
public abstract class FinSet extends ExternalComponent implements RingInstanceable, AxialPositionable { public abstract class FinSet extends ExternalComponent implements AxialPositionable, BoxBounded, RingInstanceable {
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(FinSet.class);
/** /**
* Maximum allowed cant of fins. * Maximum allowed cant of fins.
*/ */
@ -735,13 +731,14 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
} }
public BoundingBox getBoundingBox() { public BoundingBox getInstanceBoundingBox(){
BoundingBox singleFinBounds= new BoundingBox().update(getFinPoints()); BoundingBox singleFinBounds= new BoundingBox().update(getFinPoints());
final double finLength = singleFinBounds.max.x; final double finLength = singleFinBounds.max.x;
final double finHeight = singleFinBounds.max.y; final double finHeight = singleFinBounds.max.y;
Coordinate[] locations = getInstanceLocations(); Coordinate[] locations = getInstanceLocations();
double[] angles = getInstanceAngles(); double[] angles = getInstanceAngles();
BoundingBox finSetBox = new BoundingBox(); BoundingBox finSetBox = new BoundingBox();
/* /*

View File

@ -550,25 +550,18 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
* in the current configuration. * in the current configuration.
*/ */
private void calculateBounds(){ private void calculateBounds(){
BoundingBox bounds = new BoundingBox(); BoundingBox rocketBounds = new BoundingBox();
InstanceMap map = getActiveInstances(); InstanceMap map = getActiveInstances();
for (Map.Entry<RocketComponent, java.util.ArrayList<InstanceContext>> entry : map.entrySet()) { for (Map.Entry<RocketComponent, java.util.ArrayList<InstanceContext>> entry : map.entrySet()) {
RocketComponent component = entry.getKey(); RocketComponent component = entry.getKey();
BoundingBox componentBounds = new BoundingBox();
List<InstanceContext> contexts = entry.getValue(); List<InstanceContext> contexts = entry.getValue();
Collection<Coordinate> coordinates = new ArrayList<Coordinate>();
/* FinSets already provide a bounding box, so let's use that. /* FinSets already provide a bounding box, so let's use that.
*/ */
if (component instanceof FinSet) { if (component instanceof BoxBounded) {
bounds.update(((FinSet) component).getBoundingBox()); for (InstanceContext context : contexts) {
continue;
} else {
coordinates.addAll(component.getComponentBounds());
}
BoundingBox componentBox = new BoundingBox();
List<Coordinate> transformedCoords = new ArrayList<Coordinate>();
for (InstanceContext ctxt : contexts) {
/* /*
* If the instance is not active in the current context, then * If the instance is not active in the current context, then
* skip the bound calculations. This is mildly confusing since * skip the bound calculations. This is mildly confusing since
@ -576,33 +569,58 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
* instances that are active, but it returns all instances and * instances that are active, but it returns all instances and
* the context indicates if it is active or not. * the context indicates if it is active or not.
*/ */
if (!ctxt.active) { if (!context.active) {
continue; continue;
} }
for (Coordinate c : coordinates) {
Coordinate tc = null; BoundingBox instanceBounds = ((BoxBounded) component).getInstanceBoundingBox();
/* These components do not need the transform performed in if(instanceBounds.isEmpty()) {
* order to provide the proper coordinates for length calculation. // this component is probably non-physical (like an assembly) or has invalid bounds. Skip.
* The transformation will cause the values to be calculated // i.e. 'break' out of the per-instance-context loop, and let the per-component loop continue
* incorrectly. This should be fixed in the appropriate places break;
* not handled as one-offs in here. }
*/ componentBounds = instanceBounds.transform(context.transform);
if ((component instanceof AxialStage) || (component instanceof BodyTube) || }
(component instanceof PodSet)) { }else if ((component instanceof AxialStage) || (component instanceof BodyTube) || (component instanceof PodSet)) {
tc = c; // Legacy Case #1:
} else { // These components do not need the transform performed in
tc = ctxt.transform.transform(c); // order to provide the proper coordinates for length calculation.
// The transformation will cause the values to be calculated
// incorrectly. This should be fixed in the appropriate places
// not handled as one-offs in here.
componentBounds.update(component.getComponentBounds());
} else {
// Legacy Case #2:
// These components do not implement
Collection<Coordinate> instanceCoordinates = component.getComponentBounds();
for (InstanceContext context : contexts) {
/*
* If the instance is not active in the current context, then
* skip the bound calculations. This is mildly confusing since
* getActiveInstances() implies that it will only return the
* instances that are active, but it returns all instances and
* the context indicates if it is active or not.
*/
if (!context.active) {
continue;
}
Collection<Coordinate> transformedCoords = new ArrayList<>(instanceCoordinates);
// mutating. Transforms coordinates in place.
context.transform.transform(instanceCoordinates);
for (Coordinate tc : transformedCoords) {
componentBounds.update(tc);
} }
componentBox.update(tc);
transformedCoords.add(tc);
} }
} }
bounds.update(componentBox); rocketBounds.update(componentBounds);
} }
boundsModID = rocket.getModID(); boundsModID = rocket.getModID();
cachedLength = bounds.span().x; cachedLength = rocketBounds.span().x;
/* Special case for the scenario that all of the stages are removed and are /* Special case for the scenario that all of the stages are removed and are
* inactive. Its possible that this shouldn't be allowed, but it is currently * inactive. Its possible that this shouldn't be allowed, but it is currently
* so we'll just adjust the length here. * so we'll just adjust the length here.
@ -610,7 +628,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
if (getActiveStages().isEmpty()) { if (getActiveStages().isEmpty()) {
cachedLength = 0; cachedLength = 0;
} }
cachedBounds.update( bounds ); cachedBounds.update( rocketBounds );
} }
/** /**

View File

@ -28,6 +28,23 @@ public class BoundingBox {
return new BoundingBox( this.min, this.max ); return new BoundingBox( this.min, this.max );
} }
public boolean isEmpty(){
if( (min.x > max.x) ||
(min.y > max.y) ||
(min.z > max.z)){
return true;
}else{
return false;
}
}
/**
* return a copy of this bounding box, transformed by the given transformation matrix
* @return
*/
public BoundingBox transform(Transformation transform){
return new BoundingBox(transform.transform(this.min), transform.transform(this.max));
}
private void update_x_min( final double xVal) { private void update_x_min( final double xVal) {
if( min.x > xVal) if( min.x > xVal)
@ -104,6 +121,9 @@ public class BoundingBox {
} }
public BoundingBox update( BoundingBox other ) { public BoundingBox update( BoundingBox other ) {
if(other.isEmpty()){
return this;
}
update_x_min(other.min.x); update_x_min(other.min.x);
update_y_min(other.min.y); update_y_min(other.min.y);
update_z_min(other.min.y); update_z_min(other.min.y);
@ -121,7 +141,7 @@ public class BoundingBox {
} }
public Collection<Coordinate> toCollection(){ public Collection<Coordinate> toCollection(){
Collection<Coordinate> toReturn = new ArrayList<Coordinate>(); Collection<Coordinate> toReturn = new ArrayList<>();
toReturn.add( this.max); toReturn.add( this.max);
toReturn.add( this.min); toReturn.add( this.min);
return toReturn; return toReturn;

View File

@ -19,7 +19,7 @@ public class FinRenderer {
public void renderFinSet(final GL2 gl, FinSet finSet ) { public void renderFinSet(final GL2 gl, FinSet finSet ) {
BoundingBox bounds = finSet.getBoundingBox(); BoundingBox bounds = finSet.getInstanceBoundingBox();
gl.glMatrixMode(GL.GL_TEXTURE); gl.glMatrixMode(GL.GL_TEXTURE);
gl.glPushMatrix(); gl.glPushMatrix();
gl.glScaled(1 / (bounds.max.x - bounds.min.x), 1 / (bounds.max.y - bounds.min.y), 0); gl.glScaled(1 / (bounds.max.x - bounds.min.x), 1 / (bounds.max.y - bounds.min.y), 0);