[feat] FlightConfiguration may now generate an InstanceMap
This commit is contained in:
parent
7b28923659
commit
efabe81790
@ -4,6 +4,7 @@ import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -18,6 +19,7 @@ import net.sf.openrocket.util.BoundingBox;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Monitorable;
|
||||
import net.sf.openrocket.util.Transformation;
|
||||
|
||||
|
||||
/**
|
||||
@ -36,9 +38,9 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
protected final Rocket rocket;
|
||||
protected final FlightConfigurationId fcid;
|
||||
|
||||
private static int instanceCount=0;
|
||||
private static int configurationInstanceCount=0;
|
||||
// made public for testing.... there is probably a better way
|
||||
public final int instanceNumber;
|
||||
public final int configurationInstanceId;
|
||||
|
||||
private class StageFlags implements Cloneable {
|
||||
public boolean active = true;
|
||||
@ -83,7 +85,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
}
|
||||
this.rocket = rocket;
|
||||
this.configurationName = null;
|
||||
this.instanceNumber = instanceCount++;
|
||||
this.configurationInstanceId = configurationInstanceCount++;
|
||||
|
||||
updateStages();
|
||||
updateMotors();
|
||||
@ -214,6 +216,49 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates a read-only, instance-aware collection of the components for this rocket & configuration
|
||||
*
|
||||
* TODO: swap in this function for the 'getActiveComponents() function, above; ONLY WHEN READY / MATURE!
|
||||
*/
|
||||
public InstanceMap getActiveInstances() {
|
||||
InstanceMap contexts = new InstanceMap();
|
||||
getContextListAt( this.rocket, contexts, Transformation.IDENTITY);
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private InstanceMap getContextListAt(final RocketComponent component, final InstanceMap results, final Transformation parentTransform ){
|
||||
final int instanceCount = component.getInstanceCount();
|
||||
final Coordinate[] allOffsets = component.getInstanceOffsets();
|
||||
final double[] allAngles = component.getInstanceAngles();
|
||||
final boolean active = this.isComponentActive(component);
|
||||
|
||||
final Transformation compLocTransform = Transformation.getTranslationTransform( component.getPosition() );
|
||||
final Transformation componentTransform = parentTransform.applyTransformation(compLocTransform);
|
||||
|
||||
// generate the Instance's Context:
|
||||
for(int currentInstanceNumber=0; currentInstanceNumber < instanceCount; currentInstanceNumber++) {
|
||||
|
||||
final Coordinate instanceOffset = allOffsets[currentInstanceNumber];
|
||||
final Transformation offsetTransform = Transformation.getTranslationTransform( instanceOffset );
|
||||
|
||||
final double instanceAngle = allAngles[currentInstanceNumber];
|
||||
final Transformation angleTransform = Transformation.getAxialRotation(instanceAngle);
|
||||
|
||||
final Transformation currentTransform = componentTransform.applyTransformation(offsetTransform)
|
||||
.applyTransformation(angleTransform);
|
||||
|
||||
// constructs entry in-place
|
||||
results.emplace(component, active, currentInstanceNumber, currentTransform);
|
||||
|
||||
for(RocketComponent child : component.getChildren()) {
|
||||
getContextListAt(child, results, currentTransform);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<AxialStage> getActiveStages() {
|
||||
List<AxialStage> activeStages = new ArrayList<>();
|
||||
|
||||
@ -554,14 +599,14 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
}
|
||||
|
||||
public String toDebug() {
|
||||
return this.fcid.toDebug()+" (#"+instanceNumber+") "+ getOneLineMotorDescription();
|
||||
return this.fcid.toDebug()+" (#"+configurationInstanceId+") "+ getOneLineMotorDescription();
|
||||
}
|
||||
|
||||
// DEBUG / DEVEL
|
||||
public String toStageListDetail() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(String.format("\nDumping %d stages for config: %s: (%s)(#: %d)\n",
|
||||
stages.size(), getName(), getId().toShortKey(), instanceNumber));
|
||||
stages.size(), getName(), getId().toShortKey(), configurationInstanceId));
|
||||
final String fmt = " [%-2s][%4s]: %6s \n";
|
||||
buf.append(String.format(fmt, "#", "?actv", "Name"));
|
||||
for (StageFlags flags : stages.values()) {
|
||||
@ -576,7 +621,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
public String toMotorDetail(){
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(String.format("\nDumping %2d Motors for configuration %s (%s)(#: %s)\n",
|
||||
motors.size(), getName(), getId().toShortKey(), this.instanceNumber));
|
||||
motors.size(), getName(), getId().toShortKey(), this.configurationInstanceId));
|
||||
|
||||
for( MotorConfiguration curConfig : this.motors.values() ){
|
||||
boolean active=this.isStageActive( curConfig.getMount().getStage().getStageNumber());
|
||||
|
@ -0,0 +1,59 @@
|
||||
package net.sf.openrocket.rocketcomponent;
|
||||
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.Transformation;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author teyrana (aka Daniel Williams) <equipoise@gmail.com>
|
||||
*
|
||||
*/
|
||||
public class InstanceContext {
|
||||
|
||||
// =========== Public Functions ========================
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
InstanceContext other = (InstanceContext) obj;
|
||||
return (component.equals(other.component) && transform.equals(other.transform));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (component.hashCode());
|
||||
}
|
||||
|
||||
public InstanceContext(final RocketComponent _component, final boolean _active, final int _instanceNumber, final Transformation _transform) {
|
||||
component = _component;
|
||||
active = _active;
|
||||
instanceNumber = _instanceNumber;
|
||||
transform = _transform;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Context for %s #%d", component);
|
||||
}
|
||||
|
||||
public Coordinate getLocation() {
|
||||
return transform.transform(Coordinate.ZERO);
|
||||
}
|
||||
|
||||
// =========== Instance Member Variables ========================
|
||||
|
||||
|
||||
// ==== public ====
|
||||
final public RocketComponent component;
|
||||
final public boolean active;
|
||||
final public int instanceNumber;
|
||||
final public Transformation transform;
|
||||
|
||||
// =========== Private Instance Functions ========================
|
||||
|
||||
|
||||
}
|
||||
|
73
core/src/net/sf/openrocket/rocketcomponent/InstanceMap.java
Normal file
73
core/src/net/sf/openrocket/rocketcomponent/InstanceMap.java
Normal file
@ -0,0 +1,73 @@
|
||||
package net.sf.openrocket.rocketcomponent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.openrocket.util.Transformation;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author teyrana (aka Daniel Williams) <equipoise@gmail.com>
|
||||
*
|
||||
*/
|
||||
public class InstanceMap extends HashMap<RocketComponent, ArrayList<InstanceContext>> {
|
||||
|
||||
// =========== Public Functions ========================
|
||||
|
||||
// public InstanceMap() {}
|
||||
|
||||
public int count(final RocketComponent key) {
|
||||
if(containsKey(key)){
|
||||
return get(key).size();
|
||||
}else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void emplace(final RocketComponent component, boolean active, int number, final Transformation xform) {
|
||||
final RocketComponent key = component;
|
||||
|
||||
if(!containsKey(component)) {
|
||||
put(key, new ArrayList<>());
|
||||
}
|
||||
|
||||
final InstanceContext context = new InstanceContext(component, active, number, xform);
|
||||
get(key).add(context);
|
||||
}
|
||||
|
||||
public List<InstanceContext> getInstanceContexts(final RocketComponent key) {
|
||||
return get(key);
|
||||
}
|
||||
|
||||
// this is primarily for debugging.
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
int outerIndex = 0;
|
||||
buffer.append(">> Printing InstanceMap:\n");
|
||||
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: entrySet() ) {
|
||||
final RocketComponent key = entry.getKey();
|
||||
final ArrayList<InstanceContext> contexts = entry.getValue();
|
||||
buffer.append(String.format("....[% 2d]:[%s]\n", outerIndex, key.getName()));
|
||||
outerIndex++;
|
||||
|
||||
int innerIndex = 0;
|
||||
for(InstanceContext ctxt: contexts ) {
|
||||
buffer.append(String.format("........[@% 2d][% 2d] %s\n", innerIndex, ctxt.instanceNumber, ctxt.getLocation().toPreciseString()));
|
||||
innerIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
// =========== Instance Member Variables ========================
|
||||
|
||||
// =========== Private Instance Functions ========================
|
||||
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import net.sf.openrocket.rocketcomponent.FinSet.CrossSection;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
|
||||
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
|
||||
import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
|
||||
import net.sf.openrocket.rocketcomponent.InnerTube;
|
||||
import net.sf.openrocket.rocketcomponent.InternalComponent;
|
||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
|
||||
@ -862,7 +861,7 @@ public class TestRockets {
|
||||
// ====== Payload Stage ======
|
||||
// ====== ====== ====== ======
|
||||
AxialStage payloadStage = new AxialStage();
|
||||
payloadStage.setName("Payload Fairing");
|
||||
payloadStage.setName("Payload Fairing Stage");
|
||||
rocket.addChild(payloadStage);
|
||||
|
||||
{
|
||||
|
@ -5,14 +5,16 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.TestRockets;
|
||||
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
|
||||
|
||||
public class FlightConfigurationTest extends BaseTestCase {
|
||||
|
||||
private final static double EPSILON = MathUtil.EPSILON*1E3;
|
||||
|
||||
/**
|
||||
@ -155,7 +157,6 @@ public class FlightConfigurationTest extends BaseTestCase {
|
||||
|
||||
// test explicitly setting all stages active
|
||||
config.setAllStages();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -326,5 +327,151 @@ public class FlightConfigurationTest extends BaseTestCase {
|
||||
assertThat("active motor count doesn't match: ", actualMotorCount, equalTo(expectedMotorCount));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterateComponents() {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
FlightConfiguration selected = rocket.getSelectedConfiguration();
|
||||
|
||||
selected.clearAllStages();
|
||||
selected.toggleStage(2);
|
||||
|
||||
// vvvv Test Target vvvv
|
||||
InstanceMap instances = selected.getActiveInstances();
|
||||
// ^^^^ Test Target ^^^^
|
||||
|
||||
// Payload Stage
|
||||
final AxialStage coreStage = (AxialStage)rocket.getChild(1);
|
||||
{ // Core Stage
|
||||
final List<InstanceContext> coreStageContextList = instances.getInstanceContexts(coreStage);
|
||||
final InstanceContext coreStageContext = coreStageContextList.get(0);
|
||||
assertThat(coreStageContext.component.getClass(), equalTo(AxialStage.class));
|
||||
assertThat(coreStageContext.component.getID(), equalTo(rocket.getChild(1).getID()));
|
||||
assertThat(coreStageContext.component.getInstanceCount(), equalTo(1));
|
||||
|
||||
final Coordinate coreLocation = coreStageContext.getLocation();
|
||||
assertEquals(coreLocation.x, 0.564, EPSILON);
|
||||
assertEquals(coreLocation.y, 0.0, EPSILON);
|
||||
assertEquals(coreLocation.z, 0.0, EPSILON);
|
||||
|
||||
//... skip uninteresting component
|
||||
}
|
||||
|
||||
// Booster Stage
|
||||
{ // instance #1
|
||||
final ParallelStage boosterStage = (ParallelStage)coreStage.getChild(0).getChild(0);
|
||||
final List<InstanceContext> boosterStageContextList = instances.getInstanceContexts(boosterStage);
|
||||
final InstanceContext boosterStage0Context = boosterStageContextList.get(0);
|
||||
assertThat(boosterStage0Context.component.getClass(), equalTo(ParallelStage.class));
|
||||
assertThat(boosterStage0Context.component.getID(), equalTo(boosterStage.getID()));
|
||||
assertThat(boosterStage0Context.instanceNumber, equalTo(0));
|
||||
{
|
||||
final Coordinate loc = boosterStage0Context.getLocation();
|
||||
assertEquals(loc.x, 0.484, EPSILON);
|
||||
assertEquals(loc.y, 0.077, EPSILON);
|
||||
assertEquals(loc.z, 0.0, EPSILON);
|
||||
}
|
||||
|
||||
final InstanceContext boosterStage1Context = boosterStageContextList.get(1);
|
||||
assertThat(boosterStage1Context.component.getClass(), equalTo(ParallelStage.class));
|
||||
assertThat(boosterStage1Context.component.getID(), equalTo(boosterStage.getID()));
|
||||
assertThat(boosterStage1Context.instanceNumber, equalTo(1));
|
||||
{
|
||||
final Coordinate loc = boosterStage1Context.getLocation();
|
||||
assertEquals(loc.x, 0.484, EPSILON);
|
||||
assertEquals(loc.y, -0.077, EPSILON);
|
||||
assertEquals(loc.z, 0.0, EPSILON);
|
||||
}
|
||||
|
||||
{ // Booster Body:
|
||||
final BodyTube boosterBody = (BodyTube)boosterStage.getChild(1);
|
||||
final List<InstanceContext> boosterBodyContextList = instances.getInstanceContexts(boosterBody);
|
||||
|
||||
// this is the instance number rocket-wide
|
||||
final InstanceContext boosterBodyContext = boosterBodyContextList.get(1);
|
||||
|
||||
// this is the instance number per-parent
|
||||
assertThat(boosterBodyContext.instanceNumber, equalTo(0));
|
||||
|
||||
assertThat(boosterBodyContext.component.getClass(), equalTo(BodyTube.class));
|
||||
|
||||
final Coordinate bodyTubeLocation = boosterBodyContext.getLocation();
|
||||
assertEquals(bodyTubeLocation.x, 0.564, EPSILON);
|
||||
assertEquals(bodyTubeLocation.y, -0.077, EPSILON);
|
||||
assertEquals(bodyTubeLocation.z, 0.0, EPSILON);
|
||||
|
||||
{ // Booster::Motor Tubes ( x2 x4)
|
||||
final InnerTube boosterMMT = (InnerTube)boosterBody.getChild(0);
|
||||
final List<InstanceContext> mmtContextList = instances.getInstanceContexts(boosterMMT);
|
||||
assertEquals(8, mmtContextList.size());
|
||||
|
||||
final InstanceContext motorTubeContext0 = mmtContextList.get(4);
|
||||
assertThat(motorTubeContext0.component.getClass(), equalTo(InnerTube.class));
|
||||
assertThat(motorTubeContext0.instanceNumber, equalTo(0));
|
||||
final Coordinate motorTube0Location = motorTubeContext0.getLocation();
|
||||
assertEquals(motorTube0Location.x, 1.214, EPSILON);
|
||||
assertEquals(motorTube0Location.y, -0.062, EPSILON);
|
||||
assertEquals(motorTube0Location.z, -0.015, EPSILON);
|
||||
|
||||
final InstanceContext motorTubeContext1 = mmtContextList.get(5);
|
||||
assertThat(motorTubeContext1.component.getClass(), equalTo(InnerTube.class));
|
||||
assertThat(motorTubeContext1.instanceNumber, equalTo(1));
|
||||
final Coordinate motorTube1Location = motorTubeContext1.getLocation();
|
||||
assertEquals(motorTube1Location.x, 1.214, EPSILON);
|
||||
assertEquals(motorTube1Location.y, -0.092, EPSILON);
|
||||
assertEquals(motorTube1Location.z, -0.015, EPSILON);
|
||||
|
||||
final InstanceContext motorTubeContext2 = mmtContextList.get(6);
|
||||
assertThat(motorTubeContext2.component.getClass(), equalTo(InnerTube.class));
|
||||
assertThat(motorTubeContext2.instanceNumber, equalTo(2));
|
||||
final Coordinate motorTube2Location = motorTubeContext2.getLocation();
|
||||
assertEquals(motorTube2Location.x, 1.214, EPSILON);
|
||||
assertEquals(motorTube2Location.y, -0.092, EPSILON);
|
||||
assertEquals(motorTube2Location.z, 0.015, EPSILON);
|
||||
|
||||
final InstanceContext motorTubeContext3 = mmtContextList.get(7);
|
||||
assertThat(motorTubeContext3.component.getClass(), equalTo(InnerTube.class));
|
||||
assertThat(motorTubeContext3.instanceNumber, equalTo(3));
|
||||
final Coordinate motorTube3Location = motorTubeContext3.getLocation();
|
||||
assertEquals(motorTube3Location.x, 1.214, EPSILON);
|
||||
assertEquals(motorTube3Location.y, -0.062, EPSILON);
|
||||
assertEquals(motorTube3Location.z, 0.015, EPSILON);
|
||||
|
||||
}{ // Booster::Fins::Instances ( x2 x3)
|
||||
final FinSet fins = (FinSet)boosterBody.getChild(1);
|
||||
final List<InstanceContext> finContextList = instances.getInstanceContexts(fins);
|
||||
assertEquals(6, finContextList.size());
|
||||
|
||||
final InstanceContext boosterFinContext0 = finContextList.get(3);
|
||||
assertThat(boosterFinContext0.component.getClass(), equalTo(TrapezoidFinSet.class));
|
||||
assertThat(boosterFinContext0.instanceNumber, equalTo(0));
|
||||
final Coordinate boosterFin0Location = boosterFinContext0.getLocation();
|
||||
assertEquals(boosterFin0Location.x, 1.044, EPSILON);
|
||||
assertEquals(boosterFin0Location.y, -0.104223611, EPSILON);
|
||||
assertEquals(boosterFin0Location.z, -0.027223611, EPSILON);
|
||||
|
||||
final InstanceContext boosterFinContext1 = finContextList.get(4);
|
||||
assertThat(boosterFinContext1.component.getClass(), equalTo(TrapezoidFinSet.class));
|
||||
assertThat(boosterFinContext1.instanceNumber, equalTo(1));
|
||||
final Coordinate boosterFin1Location = boosterFinContext1.getLocation();
|
||||
assertEquals(boosterFin1Location.x, 1.044, EPSILON);
|
||||
assertEquals(boosterFin1Location.y, -0.03981186, EPSILON);
|
||||
assertEquals(boosterFin1Location.z, -0.00996453, EPSILON);
|
||||
|
||||
final InstanceContext boosterFinContext2 = finContextList.get(5);
|
||||
assertThat(boosterFinContext2.component.getClass(), equalTo(TrapezoidFinSet.class));
|
||||
assertThat(boosterFinContext2.instanceNumber, equalTo(2));
|
||||
final Coordinate boosterFin2Location = boosterFinContext2.getLocation();
|
||||
assertEquals(boosterFin2Location.x, 1.044, EPSILON);
|
||||
assertEquals(boosterFin2Location.y, -0.08696453, EPSILON);
|
||||
assertEquals(boosterFin2Location.z, 0.03718814, EPSILON);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,7 +7,6 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.rocketcomponent.position.AngleMethod;
|
||||
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
@ -35,12 +34,12 @@ public class RocketTest extends BaseTestCase {
|
||||
FlightConfigurationId fcid4 = config4.getId();
|
||||
|
||||
assertThat("fcids should match: ", config1.getId().key, equalTo(fcid4.key));
|
||||
assertThat("Configurations should be different: "+config1.toDebug()+"=?="+config4.toDebug(), config1.instanceNumber, not( config4.instanceNumber));
|
||||
assertThat("Configurations should be different: "+config1.toDebug()+"=?="+config4.toDebug(), config1.configurationInstanceId, not( config4.configurationInstanceId));
|
||||
|
||||
FlightConfiguration config5 = rkt2.getFlightConfiguration(config2.getId());
|
||||
FlightConfigurationId fcid5 = config5.getId();
|
||||
assertThat("fcids should match: ", config2.getId(), equalTo(fcid5));
|
||||
assertThat("Configurations should bef different match: "+config2.toDebug()+"=?="+config5.toDebug(), config2.instanceNumber, not( config5.instanceNumber));
|
||||
assertThat("Configurations should bef different match: "+config2.toDebug()+"=?="+config5.toDebug(), config2.configurationInstanceId, not( config5.configurationInstanceId));
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user