Extract common functionality to DataBranch

This commit is contained in:
SiboVG 2024-08-17 16:10:03 +02:00
parent 9554bf3fe2
commit 621ee57cc2
2 changed files with 194 additions and 198 deletions

View File

@ -1,14 +1,133 @@
package info.openrocket.core.simulation;
import info.openrocket.core.util.ArrayList;
import info.openrocket.core.util.ModID;
import info.openrocket.core.util.Monitorable;
import info.openrocket.core.util.Mutable;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A branch of data / collection of data points for a specific type of data.
* @param <T> the type of data in this branch
*/
public interface DataBranch<T extends DataType> extends Monitorable {
public abstract class DataBranch<T extends DataType> implements Monitorable {
protected final String name;
protected final Map<T, ArrayList<Double>> values = new LinkedHashMap<>();
protected final Map<T, Double> maxValues = new HashMap<>();
protected final Map<T, Double> minValues = new HashMap<>();
protected final Mutable mutable = new Mutable();
protected ModID modID = ModID.INVALID;
/**
* Sole constructor. Defines the name of the DataBranch and at least one variable type.
*
* @param name the name of this DataBranch.
* @param types data types to include (must include at least one type).
*/
@SafeVarargs
public DataBranch(String name, T... types) {
if (types.length == 0) {
throw new IllegalArgumentException("Must specify at least one data type.");
}
this.name = name;
for (T t : types) {
addType(t);
}
}
public DataBranch(String name) {
this.name = name;
}
protected void addType(T type) {
if (values.containsKey(type)) {
throw new IllegalArgumentException("Value type " + type + " already exists.");
}
values.put(type, new ArrayList<>());
minValues.put(type, Double.NaN);
maxValues.put(type, Double.NaN);
}
/**
* Adds a new point into the data branch. The value for all types is set to NaN by default.
*
* @throws IllegalStateException if this object has been made immutable.
*/
public void addPoint() {
mutable.check();
for (Map.Entry<T, ArrayList<Double>> entry : values.entrySet()) {
sanityCheckValues(entry.getKey(), Double.NaN);
entry.getValue().add(Double.NaN);
}
modID = new ModID();
}
private void sanityCheckValues(T type, Double value) {
ArrayList<Double> list = values.get(type);
if (list == null) {
list = new info.openrocket.core.util.ArrayList<>();
int n = getLength();
for (int i = 0; i < n; i++) {
list.add(Double.NaN);
}
values.put(type, list);
minValues.put(type, value);
maxValues.put(type, value);
}
}
/**
* Set the value for a specific data type at the latest point. New variable types can be
* added to the FlightDataBranch transparently.
*
* @param type the variable to set.
* @param value the value to set.
* @throws IllegalStateException if this object has been made immutable.
*/
public void setValue(T type, double value) {
mutable.check();
ArrayList<Double> list = values.computeIfAbsent(type, k -> {
ArrayList<Double> newList = new ArrayList<>();
int n = getLength();
for (int i = 0; i < n; i++) {
newList.add(Double.NaN);
}
minValues.put(k, Double.NaN);
maxValues.put(k, Double.NaN);
return newList;
});
if (list.size() > 0) {
list.set(list.size() - 1, value);
}
double min = minValues.get(type);
double max = maxValues.get(type);
if (Double.isNaN(min) || (value < min)) {
minValues.put(type, value);
}
if (Double.isNaN(max) || (value > max)) {
maxValues.put(type, value);
}
modID = new ModID();
}
/**
* Return an array of values for the specified variable type.
*
@ -16,7 +135,12 @@ public interface DataBranch<T extends DataType> extends Monitorable {
* @return a list of the variable values, or <code>null</code> if
* the variable type hasn't been added to this branch.
*/
List<Double> get(T type);
public List<Double> get(T type) {
ArrayList<Double> list = values.get(type);
if (list == null)
return null;
return list.clone();
}
/**
* Return the value of the specified type at the specified index.
@ -24,7 +148,16 @@ public interface DataBranch<T extends DataType> extends Monitorable {
* @param index the data index of the value
* @return the value at the specified index
*/
Double getByIndex(T type, int index);
public Double getByIndex(T type, int index) {
if (index < 0 || index >= getLength()) {
throw new IllegalArgumentException("Index out of bounds");
}
ArrayList<Double> list = values.get(type);
if (list == null) {
return null;
}
return list.get(index);
}
/**
* Return the last value of the specified type in the branch, or NaN if the type is
@ -33,7 +166,12 @@ public interface DataBranch<T extends DataType> extends Monitorable {
* @param type the parameter type.
* @return the last value in this branch, or NaN.
*/
double getLast(T type);
public double getLast(T type) {
ArrayList<Double> list = values.get(type);
if (list == null || list.isEmpty())
return Double.NaN;
return list.get(list.size() - 1);
}
/**
* Return the minimum value of the specified type in the branch, or NaN if the type
@ -42,7 +180,12 @@ public interface DataBranch<T extends DataType> extends Monitorable {
* @param type the parameter type.
* @return the minimum value in this branch, or NaN.
*/
double getMinimum(T type);
public double getMinimum(T type) {
Double v = minValues.get(type);
if (v == null)
return Double.NaN;
return v;
}
/**
* Return the maximum value of the specified type in the branch, or NaN if the type
@ -51,21 +194,59 @@ public interface DataBranch<T extends DataType> extends Monitorable {
* @param type the parameter type.
* @return the maximum value in this branch, or NaN.
*/
double getMaximum(T type);
public double getMaximum(T type) {
Double v = maxValues.get(type);
if (v == null)
return Double.NaN;
return v;
}
/**
* Return the number of data points in this branch.
*/
int getLength();
public int getLength() {
for (ArrayList<Double> doubles : values.values()) {
return doubles.size();
}
return 0;
}
/**
* Return the variable types included in this branch. The types are sorted in their
* natural order.
*/
T[] getTypes();
@SuppressWarnings("unchecked")
public T[] getTypes() {
Set<T> keySet = values.keySet();
T[] array = (T[]) Array.newInstance(keySet.iterator().next().getClass(), keySet.size());
keySet.toArray(array);
Arrays.sort(array);
return array;
}
/**
* Return the branch name.
*/
String getName();
public String getName() {
return name;
}
/**
* Make this FlightDataBranch immutable. Any calls to the set methods that would
* modify this object will after this call throw an <code>IllegalStateException</code>.
*/
public void immute() {
mutable.immute();
}
/**
* Return whether this branch is still mutable.
*/
public boolean isMutable() {
return mutable.isMutable();
}
public ModID getModID() {
return modID;
}
}

View File

@ -1,8 +1,5 @@
package info.openrocket.core.simulation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -11,8 +8,6 @@ import info.openrocket.core.rocketcomponent.Rocket;
import info.openrocket.core.rocketcomponent.RocketComponent;
import info.openrocket.core.util.ArrayList;
import info.openrocket.core.util.ModID;
import info.openrocket.core.util.Monitorable;
import info.openrocket.core.util.Mutable;
/**
* A single branch of flight data. The data is ordered based on some variable, typically time.
@ -29,31 +24,11 @@ import info.openrocket.core.util.Mutable;
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class FlightDataBranch implements DataBranch<FlightDataType> {
/** The name of this flight data branch. */
private final String name;
private final Map<FlightDataType, ArrayList<Double>> values = new LinkedHashMap<>();
private final Map<FlightDataType, Double> maxValues = new HashMap<>();
private final Map<FlightDataType, Double> minValues = new HashMap<>();
/**
* time for the rocket to reach apogee if the flight had been no recovery deployment
*/
public class FlightDataBranch extends DataBranch<FlightDataType> {
private double timeToOptimumAltitude = Double.NaN;
/**
* Altitude the rocket would reach if there had been no recovery deployment.
*/
private double optimumAltitude = Double.NaN;
private final ArrayList<FlightEvent> events = new ArrayList<>();
private final Mutable mutable = new Mutable();
private ModID modID = ModID.INVALID;
/**
* Sole constructor. Defines the name of the FlightDataBranch and at least one variable type.
*
@ -61,22 +36,7 @@ public class FlightDataBranch implements DataBranch<FlightDataType> {
* @param types data types to include (must include at least one type).
*/
public FlightDataBranch(String name, FlightDataType... types) {
if (types.length == 0) {
throw new IllegalArgumentException("Must specify at least one data type.");
}
this.name = name;
for (FlightDataType t : types) {
if (values.containsKey(t)) {
throw new IllegalArgumentException("Value type " + t + " specified multiple " +
"times in constructor.");
}
values.put(t, new ArrayList<>());
minValues.put(t, Double.NaN);
maxValues.put(t, Double.NaN);
}
super(name, types);
}
/**
@ -89,7 +49,7 @@ public class FlightDataBranch implements DataBranch<FlightDataType> {
* @param parent the parent branch to copy data from.
*/
public FlightDataBranch(String name, RocketComponent srcComponent, FlightDataBranch parent) {
this.name = name;
super(name);
// Copy all the values from the parent
copyValuesFromBranch(parent, srcComponent);
@ -99,72 +59,13 @@ public class FlightDataBranch implements DataBranch<FlightDataType> {
* Makes an 'empty' flight data branch which has no data but all built in data types are defined.
*/
public FlightDataBranch() {
name = "Empty branch";
super("Empty branch");
for (FlightDataType type : FlightDataType.ALL_TYPES) {
this.setValue(type, Double.NaN);
}
this.immute();
}
/**
* Adds a new point into the data branch. The value for all types is set to NaN by default.
*
* @throws IllegalStateException if this object has been made immutable.
*/
public void addPoint() {
mutable.check();
for (Map.Entry<FlightDataType, ArrayList<Double>> entry : values.entrySet()) {
sanityCheckValues(entry.getKey(), Double.NaN);
entry.getValue().add(Double.NaN);
}
modID = new ModID();
}
private void sanityCheckValues(FlightDataType type, Double value) {
ArrayList<Double> list = values.get(type);
if (list == null) {
list = new ArrayList<>();
int n = getLength();
for (int i = 0; i < n; i++) {
list.add(Double.NaN);
}
values.put(type, list);
minValues.put(type, value);
maxValues.put(type, value);
}
}
/**
* Set the value for a specific data type at the latest point. New variable types can be
* added to the FlightDataBranch transparently.
*
* @param type the variable to set.
* @param value the value to set.
* @throws IllegalStateException if this object has been made immutable.
*/
public void setValue(FlightDataType type, double value) {
mutable.check();
sanityCheckValues(type, value);
ArrayList<Double> list = values.get(type);
if (list.size() > 0) {
list.set(list.size() - 1, value);
}
double min = minValues.get(type);
double max = maxValues.get(type);
if (Double.isNaN(min) || (value < min)) {
minValues.put(type, value);
}
if (Double.isNaN(max) || (value > max)) {
maxValues.put(type, value);
}
modID = new ModID();
}
/**
* Clears all the current values in the branch and copies the values from the given branch.
@ -228,70 +129,6 @@ public class FlightDataBranch implements DataBranch<FlightDataType> {
}
}
@Override
public String getName() {
return name;
}
@Override
public FlightDataType[] getTypes() {
FlightDataType[] array = values.keySet().toArray(new FlightDataType[0]);
Arrays.sort(array);
return array;
}
@Override
public int getLength() {
for (ArrayList<Double> doubles : values.values()) {
return doubles.size();
}
return 0;
}
@Override
public List<Double> get(FlightDataType type) {
ArrayList<Double> list = values.get(type);
if (list == null)
return null;
return list.clone();
}
@Override
public Double getByIndex(FlightDataType type, int index) {
if (index < 0 || index >= getLength()) {
throw new IllegalArgumentException("Index out of bounds");
}
ArrayList<Double> list = values.get(type);
if (list == null) {
return null;
}
return list.get(index);
}
@Override
public double getLast(FlightDataType type) {
ArrayList<Double> list = values.get(type);
if (list == null || list.isEmpty())
return Double.NaN;
return list.get(list.size() - 1);
}
@Override
public double getMinimum(FlightDataType type) {
Double v = minValues.get(type);
if (v == null)
return Double.NaN;
return v;
}
@Override
public double getMaximum(FlightDataType type) {
Double v = maxValues.get(type);
if (v == null)
return Double.NaN;
return v;
}
/**
* @return the timeToOptimumAltitude
@ -387,28 +224,6 @@ public class FlightDataBranch implements DataBranch<FlightDataType> {
}
return retval;
}
/**
* Make this FlightDataBranch immutable. Any calls to the set methods that would
* modify this object will after this call throw an <code>IllegalStateException</code>.
*/
public void immute() {
mutable.immute();
}
/**
* Return whether this branch is still mutable.
*/
public boolean isMutable() {
return mutable.isMutable();
}
@Override
public ModID getModID() {
return modID;
}
public FlightDataBranch clone() {
FlightDataType[] types = getTypes();