Merge pull request #602 from wolsen/fix-stage-length

[Fixes 452] Reflect length of selected stages
This commit is contained in:
Daniel Williams 2020-04-19 22:34:09 -04:00 committed by GitHub
commit bd8bef2c85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 32 deletions

View File

@ -714,11 +714,32 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
final double finLength = singleFinBounds.max.x;
final double finHeight = singleFinBounds.max.y;
BoundingBox compBox = new BoundingBox().update(getComponentLocations());
Coordinate[] locations = getInstanceLocations();
double[] angles = getInstanceAngles();
BoundingBox finSetBox = new BoundingBox();
BoundingBox finSetBox = new BoundingBox( compBox.min.sub( 0, finHeight, finHeight ),
compBox.max.add( finLength, finHeight, finHeight ));
return finSetBox;
/*
* The fins themselves will be offset by the location itself to match
* the outside radius of a body tube. The bounds encapsulate the outer
* portion of all the tips of the fins.
*
* The height of each fin along the Y axis can be determined by:
* yHeight = cos(angle) * finHeight
*
* The height (depth?) of each fin along the Z axis can be determined by:
* zHeight = sin(angle) * finHeight
*
* The boundingBox should be that box which is the smallest box where
* a convex hull will contain all Coordinates.
*/
for (int i = 0; i < locations.length; i++) {
double y = Math.cos(angles[i]) * finHeight;
double z = Math.sin(angles[i]) * finHeight;
finSetBox.update(locations[i].add(0, y, z));
finSetBox.update(locations[i].add(finLength, y, z));
}
return finSetBox;
}
/**

View File

@ -1,6 +1,7 @@
package net.sf.openrocket.rocketcomponent;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@ -551,16 +552,71 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return cachedBounds;
}
/**
* Calculates the bounds for all the active component instances
* in the current configuration.
*/
private void calculateBounds(){
BoundingBox bounds = new BoundingBox();
for (RocketComponent component : this.getActiveComponents()) {
BoundingBox componentBounds = new BoundingBox().update(component.getComponentBounds());
bounds.update( componentBounds );
}
InstanceMap map = getActiveInstances();
for (Map.Entry<RocketComponent, java.util.ArrayList<InstanceContext>> entry : map.entrySet()) {
RocketComponent component = entry.getKey();
List<InstanceContext> contexts = entry.getValue();
Collection<Coordinate> coordinates = new ArrayList<Coordinate>();
/* FinSets already provide a bounding box, so let's use that.
*/
if (component instanceof FinSet) {
bounds.update(((FinSet) component).getBoundingBox());
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
* 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 (!ctxt.active) {
continue;
}
for (Coordinate c : coordinates) {
Coordinate tc = null;
/* These components do not need the transform performed in
* 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.
*/
if ((component instanceof AxialStage) || (component instanceof BodyTube) ||
(component instanceof PodSet)) {
tc = c;
} else {
tc = ctxt.transform.transform(c);
}
componentBox.update(tc);
transformedCoords.add(tc);
}
}
bounds.update(componentBox);
}
boundsModID = rocket.getModID();
cachedLength = bounds.span().x;
/* 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
* so we'll just adjust the length here.
*/
if (getActiveStages().isEmpty()) {
cachedLength = 0;
}
cachedBounds.update( bounds );
}

View File

@ -44,7 +44,7 @@ public class FlightConfigurationTest extends BaseTestCase {
// preconditions
assertThat("active stage count doesn't match", config.getActiveStageCount(), equalTo(2));
final double expectedLength = 0.33;
final double expectedLength = 0.335;
final double calculatedLength = config.getLength();
assertEquals("source config length doesn't match: ", expectedLength, calculatedLength, EPSILON);
@ -70,7 +70,7 @@ public class FlightConfigurationTest extends BaseTestCase {
int expectedMotorCount = 2;
int actualMotorCount = config1.getActiveMotors().size();
assertThat("active motor count doesn't match", actualMotorCount, equalTo(expectedMotorCount));
double expectedLength = 0.33;
double expectedLength = 0.335;
assertEquals("source config length doesn't match: ", expectedLength, config1.getLength(), EPSILON);
double expectedReferenceLength = 0.024;
assertEquals("source config reference length doesn't match: ", expectedReferenceLength, config1.getReferenceLength(), EPSILON);

View File

@ -10,21 +10,17 @@ import javax.swing.JPanel;
import javax.swing.JToggleButton;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.StateChangeListener;
@SuppressWarnings("serial")
public class StageSelector extends JPanel implements StateChangeListener {
private static final Translator trans = Application.getTranslator();
private final Rocket rocket;
private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
@ -41,7 +37,7 @@ public class StageSelector extends JPanel implements StateChangeListener {
this.removeAll();
for(AxialStage stage : configuration.getRocket().getStageList()){
JToggleButton button = new JToggleButton(new StageAction(stage));
button.setSelected(true);
button.setSelected(configuration.isStageActive(stage.getStageNumber()));
this.add(button);
buttons.add(button);
}

View File

@ -419,13 +419,14 @@ public class RocketFigure extends AbstractScaleFigure {
protected void updateSubjectDimensions() {
// calculate bounds, and store in class variables
final BoundingBox bounds = rocket.getSelectedConfiguration().getBoundingBox();
final double maxR = Math.max(Math.hypot(bounds.min.y, bounds.min.z),
Math.hypot(bounds.max.y, bounds.max.z));
switch (currentViewType) {
case SideView:
subjectBounds_m = new Rectangle2D.Double(bounds.min.x, bounds.min.y, bounds.span().x, bounds.span().y);
subjectBounds_m = new Rectangle2D.Double(bounds.min.x, -maxR, bounds.span().x, 2 * maxR);
break;
case BackView:
final double maxR = Math.max(Math.hypot(bounds.min.y, bounds.min.z), Math.hypot(bounds.max.y, bounds.max.z));
subjectBounds_m = new Rectangle2D.Double(-maxR, -maxR, 2 * maxR, 2 * maxR);
break;
default:

View File

@ -607,20 +607,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
figure3d.setCG(cg);
figure3d.setCP(cp);
// Length bound is assumed to be tight
double length = 0;
Collection<Coordinate> bounds = curConfig.getBounds();
if (!bounds.isEmpty()) {
double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY;
for (Coordinate c : bounds) {
if (c.x < minX)
minX = c.x;
if (c.x > maxX)
maxX = c.x;
}
length = maxX - minX;
}
double length = curConfig.getLength();
double diameter = Double.NaN;
for (RocketComponent c : curConfig.getCoreComponents()) {
if (c instanceof SymmetricComponent) {