Merge pull request #602 from wolsen/fix-stage-length
[Fixes 452] Reflect length of selected stages
This commit is contained in:
commit
bd8bef2c85
@ -714,10 +714,31 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
|
|||||||
final double finLength = singleFinBounds.max.x;
|
final double finLength = singleFinBounds.max.x;
|
||||||
final double finHeight = singleFinBounds.max.y;
|
final double finHeight = singleFinBounds.max.y;
|
||||||
|
|
||||||
BoundingBox compBox = new BoundingBox().update(getComponentLocations());
|
Coordinate[] locations = getInstanceLocations();
|
||||||
|
double[] angles = getInstanceAngles();
|
||||||
|
BoundingBox finSetBox = new BoundingBox();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
BoundingBox finSetBox = new BoundingBox( compBox.min.sub( 0, finHeight, finHeight ),
|
|
||||||
compBox.max.add( finLength, finHeight, finHeight ));
|
|
||||||
return finSetBox;
|
return finSetBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.sf.openrocket.rocketcomponent;
|
package net.sf.openrocket.rocketcomponent;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -551,16 +552,71 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
|||||||
return cachedBounds;
|
return cachedBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the bounds for all the active component instances
|
||||||
|
* in the current configuration.
|
||||||
|
*/
|
||||||
private void calculateBounds(){
|
private void calculateBounds(){
|
||||||
BoundingBox bounds = new BoundingBox();
|
BoundingBox bounds = new BoundingBox();
|
||||||
|
|
||||||
for (RocketComponent component : this.getActiveComponents()) {
|
InstanceMap map = getActiveInstances();
|
||||||
BoundingBox componentBounds = new BoundingBox().update(component.getComponentBounds());
|
for (Map.Entry<RocketComponent, java.util.ArrayList<InstanceContext>> entry : map.entrySet()) {
|
||||||
bounds.update( componentBounds );
|
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();
|
boundsModID = rocket.getModID();
|
||||||
cachedLength = bounds.span().x;
|
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 );
|
cachedBounds.update( bounds );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ public class FlightConfigurationTest extends BaseTestCase {
|
|||||||
// preconditions
|
// preconditions
|
||||||
assertThat("active stage count doesn't match", config.getActiveStageCount(), equalTo(2));
|
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();
|
final double calculatedLength = config.getLength();
|
||||||
assertEquals("source config length doesn't match: ", expectedLength, calculatedLength, EPSILON);
|
assertEquals("source config length doesn't match: ", expectedLength, calculatedLength, EPSILON);
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ public class FlightConfigurationTest extends BaseTestCase {
|
|||||||
int expectedMotorCount = 2;
|
int expectedMotorCount = 2;
|
||||||
int actualMotorCount = config1.getActiveMotors().size();
|
int actualMotorCount = config1.getActiveMotors().size();
|
||||||
assertThat("active motor count doesn't match", actualMotorCount, equalTo(expectedMotorCount));
|
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);
|
assertEquals("source config length doesn't match: ", expectedLength, config1.getLength(), EPSILON);
|
||||||
double expectedReferenceLength = 0.024;
|
double expectedReferenceLength = 0.024;
|
||||||
assertEquals("source config reference length doesn't match: ", expectedReferenceLength, config1.getReferenceLength(), EPSILON);
|
assertEquals("source config reference length doesn't match: ", expectedReferenceLength, config1.getReferenceLength(), EPSILON);
|
||||||
|
@ -10,21 +10,17 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import net.sf.openrocket.l10n.Translator;
|
|
||||||
import net.sf.openrocket.rocketcomponent.AxialStage;
|
import net.sf.openrocket.rocketcomponent.AxialStage;
|
||||||
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
|
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
|
||||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||||
import net.sf.openrocket.startup.Application;
|
|
||||||
import net.sf.openrocket.util.StateChangeListener;
|
import net.sf.openrocket.util.StateChangeListener;
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class StageSelector extends JPanel implements StateChangeListener {
|
public class StageSelector extends JPanel implements StateChangeListener {
|
||||||
|
|
||||||
private static final Translator trans = Application.getTranslator();
|
|
||||||
|
|
||||||
private final Rocket rocket;
|
private final Rocket rocket;
|
||||||
|
|
||||||
private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
|
private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
|
||||||
@ -41,7 +37,7 @@ public class StageSelector extends JPanel implements StateChangeListener {
|
|||||||
this.removeAll();
|
this.removeAll();
|
||||||
for(AxialStage stage : configuration.getRocket().getStageList()){
|
for(AxialStage stage : configuration.getRocket().getStageList()){
|
||||||
JToggleButton button = new JToggleButton(new StageAction(stage));
|
JToggleButton button = new JToggleButton(new StageAction(stage));
|
||||||
button.setSelected(true);
|
button.setSelected(configuration.isStageActive(stage.getStageNumber()));
|
||||||
this.add(button);
|
this.add(button);
|
||||||
buttons.add(button);
|
buttons.add(button);
|
||||||
}
|
}
|
||||||
|
@ -419,13 +419,14 @@ public class RocketFigure extends AbstractScaleFigure {
|
|||||||
protected void updateSubjectDimensions() {
|
protected void updateSubjectDimensions() {
|
||||||
// calculate bounds, and store in class variables
|
// calculate bounds, and store in class variables
|
||||||
final BoundingBox bounds = rocket.getSelectedConfiguration().getBoundingBox();
|
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) {
|
switch (currentViewType) {
|
||||||
case SideView:
|
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;
|
break;
|
||||||
case BackView:
|
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);
|
subjectBounds_m = new Rectangle2D.Double(-maxR, -maxR, 2 * maxR, 2 * maxR);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -607,19 +607,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
|||||||
figure3d.setCG(cg);
|
figure3d.setCG(cg);
|
||||||
figure3d.setCP(cp);
|
figure3d.setCP(cp);
|
||||||
|
|
||||||
// Length bound is assumed to be tight
|
double length = curConfig.getLength();
|
||||||
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 diameter = Double.NaN;
|
double diameter = Double.NaN;
|
||||||
for (RocketComponent c : curConfig.getCoreComponents()) {
|
for (RocketComponent c : curConfig.getCoreComponents()) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user