enhanced motor selection dialog
This commit is contained in:
parent
e4d56f51ef
commit
e80b64b53c
@ -11,7 +11,7 @@
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="lib-extra/RXTXcomm.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/junit-4.7.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jfreechart-1.0.13.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jfreechart-1.0.13.jar" sourcepath="/home/sampo/Projects/lib/jfreechart-1.0.13/source"/>
|
||||
<classpathentry kind="lib" path="lib/jcommon-1.0.16.jar">
|
||||
<accessrules>
|
||||
<accessrule kind="nonaccessible" pattern="org/jfree/util/Log"/>
|
||||
|
11
ChangeLog
11
ChangeLog
@ -1,6 +1,15 @@
|
||||
2010-07-21 Sampo Niskanen
|
||||
|
||||
* Implemented enhanced motor selection dialog
|
||||
* Background motor loading & startup time optimization
|
||||
|
||||
2010-07-20 Doug Pedrick
|
||||
|
||||
* [BUG] Exception when loading Rocksim files
|
||||
|
||||
2010-07-19 Sampo Niskanen
|
||||
|
||||
* Small bug fixes
|
||||
* [BUG] Various small bug fixes
|
||||
|
||||
2010-07-18 Sampo Niskanen
|
||||
|
||||
|
@ -28,6 +28,9 @@ Debugging options
|
||||
openrocket.debug.menu
|
||||
If defined the "Debug" menu will be displayed in the main application window.
|
||||
|
||||
openrocket.debug.prefs
|
||||
If defined a new, clean set of preferences will be used (does not overwrite the existing preferences).
|
||||
|
||||
openrocket.debug.bugurl
|
||||
URL used for sending bug reports.
|
||||
|
||||
|
18
run.sh
Executable file
18
run.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# This script runs the version of OpenRocket compiled by Eclipse from
|
||||
# the bin/ directory. You can provide Java arguments and OpenRocket
|
||||
# arguments.
|
||||
#
|
||||
|
||||
JAVAOPTS=""
|
||||
|
||||
while echo "$1" | grep -q "^-" ; do
|
||||
JAVAOPTS="$JAVAOPTS $1"
|
||||
shift
|
||||
done
|
||||
|
||||
|
||||
java -cp bin/:lib/miglayout15-swing.jar:lib/jcommon-1.0.16.jar:lib/jfreechart-1.0.13.jar:. $JAVAOPTS net.sf.openrocket.startup.Startup "$@"
|
||||
|
@ -1,20 +1,9 @@
|
||||
package net.sf.openrocket.database;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.sf.openrocket.file.GeneralMotorLoader;
|
||||
import net.sf.openrocket.file.Loader;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.material.MaterialStorage;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.ConfigurationException;
|
||||
import net.sf.openrocket.util.JarUtil;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Prefs;
|
||||
|
||||
@ -28,12 +17,7 @@ public class Databases {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
/* Static implementations of specific databases: */
|
||||
/**
|
||||
* The motor database.
|
||||
* TODO: HIGH: Must cast to (Loader) to works, very ugly. In practice returns only ThrustCurveMotors currently.
|
||||
*/
|
||||
public static final Database<ThrustCurveMotor> MOTOR = new Database<ThrustCurveMotor>((Loader) new GeneralMotorLoader());
|
||||
|
||||
|
||||
/**
|
||||
* A database of bulk materials (with bulk densities).
|
||||
*/
|
||||
@ -49,34 +33,6 @@ public class Databases {
|
||||
|
||||
|
||||
|
||||
// TODO: HIGH: loading the thrust curves and other databases
|
||||
static {
|
||||
final String filePattern = ".*\\.([eE][nN][gG]|[rR][sS][eE])$";
|
||||
try {
|
||||
MOTOR.loadJarDirectory("datafiles/thrustcurves/", filePattern);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Could not read thrust curves from JAR: " + e.getMessage());
|
||||
|
||||
// Try to find directory as a system resource
|
||||
File dir;
|
||||
URL url = ClassLoader.getSystemResource("datafiles/thrustcurves/");
|
||||
|
||||
try {
|
||||
dir = JarUtil.urlToFile(url);
|
||||
} catch (Exception e1) {
|
||||
dir = new File("datafiles/thrustcurves/");
|
||||
}
|
||||
|
||||
try {
|
||||
MOTOR.loadDirectory(dir, filePattern);
|
||||
} catch (IOException e1) {
|
||||
System.out.println("Could not read thrust curves from directory either.");
|
||||
throw new ConfigurationException("Couldn't read thrust curves from either " +
|
||||
"JAR file or system resource directory", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
|
||||
// Add default materials
|
||||
@ -228,40 +184,5 @@ public class Databases {
|
||||
return Material.newMaterial(type, name, density, userDefined);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return all motor in the database matching a search criteria. Any search criteria that
|
||||
* is null or NaN is ignored.
|
||||
*
|
||||
* @param type the motor type, or null.
|
||||
* @param manufacturer the manufacturer, or null.
|
||||
* @param designation the designation, or null.
|
||||
* @param diameter the diameter, or NaN.
|
||||
* @param length the length, or NaN.
|
||||
* @return an array of all the matching motors.
|
||||
*/
|
||||
public static Motor[] findMotors(Motor.Type type, String manufacturer, String designation, double diameter, double length) {
|
||||
ArrayList<Motor> results = new ArrayList<Motor>();
|
||||
|
||||
for (ThrustCurveMotor m : MOTOR) {
|
||||
boolean match = true;
|
||||
if (type != null && type != m.getMotorType())
|
||||
match = false;
|
||||
else if (manufacturer != null && !m.getManufacturer().matches(manufacturer))
|
||||
match = false;
|
||||
else if (designation != null && !designation.equalsIgnoreCase(m.getDesignation()))
|
||||
match = false;
|
||||
else if (!Double.isNaN(diameter) && (Math.abs(diameter - m.getDiameter()) > 0.0015))
|
||||
match = false;
|
||||
else if (!Double.isNaN(length) && (Math.abs(length - m.getLength()) > 0.0015))
|
||||
match = false;
|
||||
|
||||
if (match)
|
||||
results.add(m);
|
||||
}
|
||||
|
||||
return results.toArray(new Motor[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,14 +13,14 @@ import java.util.regex.Pattern;
|
||||
|
||||
import net.sf.openrocket.motor.DesignationComparator;
|
||||
import net.sf.openrocket.motor.Manufacturer;
|
||||
import net.sf.openrocket.motor.MotorDigest;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorDigest;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.motor.Motor.Type;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
|
||||
public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
|
||||
|
||||
// Comparators:
|
||||
private static final Collator COLLATOR = Collator.getInstance(Locale.US);
|
||||
static {
|
||||
@ -28,12 +28,12 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
}
|
||||
private static final DesignationComparator DESIGNATION_COMPARATOR = new DesignationComparator();
|
||||
private static final ThrustCurveMotorComparator comparator = new ThrustCurveMotorComparator();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private final ArrayList<ThrustCurveMotor> motors = new ArrayList<ThrustCurveMotor>();
|
||||
private final Map<ThrustCurveMotor, String> digestMap =
|
||||
new IdentityHashMap<ThrustCurveMotor, String>();
|
||||
new IdentityHashMap<ThrustCurveMotor, String>();
|
||||
|
||||
private final List<Double> delays = new ArrayList<Double>();
|
||||
|
||||
@ -45,7 +45,7 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
private Motor.Type type = Motor.Type.UNKNOWN;
|
||||
|
||||
|
||||
|
||||
|
||||
public void addMotor(ThrustCurveMotor motor) {
|
||||
|
||||
// Check for first insertion
|
||||
@ -60,8 +60,8 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
// Verify that the motor can be added
|
||||
if (!matches(motor)) {
|
||||
throw new IllegalArgumentException("Motor does not match the set:" +
|
||||
" manufacturer="+manufacturer +
|
||||
" designation="+designation +
|
||||
" manufacturer=" + manufacturer +
|
||||
" designation=" + designation +
|
||||
" diameter=" + diameter +
|
||||
" length=" + length +
|
||||
" set_size=" + motors.size() +
|
||||
@ -71,6 +71,12 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
// Update the type if now known
|
||||
if (type == Motor.Type.UNKNOWN) {
|
||||
type = motor.getMotorType();
|
||||
// Add "Plugged" option if hybrid
|
||||
if (type == Motor.Type.HYBRID) {
|
||||
if (!delays.contains(Motor.PLUGGED)) {
|
||||
delays.add(Motor.PLUGGED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change the simplified designation if necessary
|
||||
@ -79,23 +85,25 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
}
|
||||
|
||||
// Add the standard delays
|
||||
for (double d: motor.getStandardDelays()) {
|
||||
for (double d : motor.getStandardDelays()) {
|
||||
d = Math.rint(d);
|
||||
delays.add(d);
|
||||
if (!delays.contains(d)) {
|
||||
delays.add(d);
|
||||
}
|
||||
}
|
||||
Collections.sort(delays);
|
||||
|
||||
|
||||
|
||||
// Check whether to add as new motor or overwrite existing
|
||||
final String digest = MotorDigest.digestMotor(motor);
|
||||
for (int index = 0; index < motors.size(); index++) {
|
||||
Motor m = motors.get(index);
|
||||
|
||||
|
||||
if (digest.equals(digestMap.get(m)) &&
|
||||
motor.getDesignation().equals(m.getDesignation())) {
|
||||
|
||||
|
||||
// Match found, check which one to keep (or both) based on comment
|
||||
String newCmt = motor.getDescription().replaceAll("\\s+"," ").trim();
|
||||
String newCmt = motor.getDescription().replaceAll("\\s+", " ").trim();
|
||||
String oldCmt = m.getDescription().replaceAll("\\s+", " ").trim();
|
||||
|
||||
if (newCmt.length() == 0 || newCmt.equals(oldCmt)) {
|
||||
@ -123,7 +131,7 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
public boolean matches(ThrustCurveMotor m) {
|
||||
if (motors.isEmpty())
|
||||
return true;
|
||||
|
||||
|
||||
if (manufacturer != m.getManufacturer())
|
||||
return false;
|
||||
|
||||
@ -133,7 +141,7 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
if (!MathUtil.equals(length, m.getLength()))
|
||||
return false;
|
||||
|
||||
if ((type != Type.UNKNOWN) && (m.getMotorType()!= Type.UNKNOWN) &&
|
||||
if ((type != Type.UNKNOWN) && (m.getMotorType() != Type.UNKNOWN) &&
|
||||
(type != m.getMotorType())) {
|
||||
return false;
|
||||
}
|
||||
@ -143,7 +151,7 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ThrustCurveMotor> getMotors() {
|
||||
@ -151,6 +159,10 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
}
|
||||
|
||||
|
||||
public int getMotorCount() {
|
||||
return motors.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the standard delays applicable to this motor type. This is a union of
|
||||
@ -160,8 +172,8 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
public List<Double> getDelays() {
|
||||
return Collections.unmodifiableList(delays);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the manufacturer of this motor type.
|
||||
* @return the manufacturer
|
||||
@ -169,8 +181,8 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
public Manufacturer getManufacturer() {
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the designation of this motor type. This is either the exact or simplified
|
||||
* designation, depending on what motors have been added.
|
||||
@ -179,8 +191,8 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
public String getDesignation() {
|
||||
return designation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the diameter of this motor type.
|
||||
* @return the diameter
|
||||
@ -188,8 +200,8 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
public double getDiameter() {
|
||||
return diameter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the length of this motor type.
|
||||
* @return the length
|
||||
@ -197,8 +209,8 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
public double getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the type of this motor type. If any of the added motors has a type different
|
||||
* from UNKNOWN, then that type will be returned.
|
||||
@ -207,12 +219,21 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
public Motor.Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ThrustCurveMotorSet[" + manufacturer + " " + designation +
|
||||
", type=" + type + ", count=" + motors.size() + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static final Pattern SIMPLIFY_PATTERN = Pattern.compile("^[0-9]*[ -]*([A-Z][0-9]+).*");
|
||||
|
||||
/**
|
||||
* Simplify a motor designation, if possible. This attempts to reduce the designation
|
||||
* into a simple letter + number notation for the impulse class and average thrust.
|
||||
@ -235,7 +256,7 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
* Comparator for deciding in which order to display matching motors.
|
||||
*/
|
||||
private static class ThrustCurveMotorComparator implements Comparator<ThrustCurveMotor> {
|
||||
|
||||
|
||||
@Override
|
||||
public int compare(ThrustCurveMotor o1, ThrustCurveMotor o2) {
|
||||
// 1. Designation
|
||||
@ -253,15 +274,15 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(ThrustCurveMotorSet other) {
|
||||
|
||||
|
||||
int value;
|
||||
|
||||
// 1. Manufacturer
|
||||
value = COLLATOR.compare(this.manufacturer.getDisplayName(),
|
||||
value = COLLATOR.compare(this.manufacturer.getDisplayName(),
|
||||
other.manufacturer.getDisplayName());
|
||||
if (value != 0)
|
||||
return value;
|
||||
@ -272,12 +293,12 @@ public class ThrustCurveMotorSet implements Comparable<ThrustCurveMotorSet> {
|
||||
return value;
|
||||
|
||||
// 3. Diameter
|
||||
value = (int)((this.diameter - other.diameter)*1000000);
|
||||
value = (int) ((this.diameter - other.diameter) * 1000000);
|
||||
if (value != 0)
|
||||
return value;
|
||||
|
||||
|
||||
// 4. Length
|
||||
value = (int)((this.length - other.length)*1000000);
|
||||
value = (int) ((this.length - other.length) * 1000000);
|
||||
return value;
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
@ -14,27 +15,29 @@ import net.sf.openrocket.startup.Application;
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public abstract class MotorSetDatabase {
|
||||
public abstract class ThrustCurveMotorSetDatabase {
|
||||
|
||||
private static final LogHelper logger = Application.getLogger();
|
||||
|
||||
private List<ThrustCurveMotorSet> motorSets;
|
||||
|
||||
protected List<ThrustCurveMotorSet> motorSets;
|
||||
|
||||
private volatile boolean startedLoading = false;
|
||||
private volatile boolean endedLoading = false;
|
||||
private final boolean asynchronous;
|
||||
|
||||
/** Set to true the first time {@link #blockUntilLoaded()} is called. */
|
||||
protected volatile boolean inUse = false;
|
||||
|
||||
/**
|
||||
* Sole constructor.
|
||||
*
|
||||
* @param asynchronous whether to load motors asynchronously in a background thread.
|
||||
*/
|
||||
public MotorSetDatabase(boolean asynchronous) {
|
||||
public ThrustCurveMotorSetDatabase(boolean asynchronous) {
|
||||
this.asynchronous = asynchronous;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a list of the ThrustCurveMotorSet objects. The list is in sorted order and
|
||||
* is unmodifiable.
|
||||
@ -46,9 +49,46 @@ public abstract class MotorSetDatabase {
|
||||
return motorSets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return all motors in the database matching a search criteria. Any search criteria that
|
||||
* is null or NaN is ignored.
|
||||
*
|
||||
* @param type the motor type, or null.
|
||||
* @param manufacturer the manufacturer, or null.
|
||||
* @param designation the designation, or null.
|
||||
* @param diameter the diameter, or NaN.
|
||||
* @param length the length, or NaN.
|
||||
* @return a list of all the matching motors.
|
||||
*/
|
||||
public List<ThrustCurveMotor> findMotors(Motor.Type type, String manufacturer, String designation,
|
||||
double diameter, double length) {
|
||||
blockUntilLoaded();
|
||||
ArrayList<ThrustCurveMotor> results = new ArrayList<ThrustCurveMotor>();
|
||||
|
||||
for (ThrustCurveMotorSet set : motorSets) {
|
||||
for (ThrustCurveMotor m : set.getMotors()) {
|
||||
boolean match = true;
|
||||
if (type != null && type != set.getType())
|
||||
match = false;
|
||||
else if (manufacturer != null && !m.getManufacturer().matches(manufacturer))
|
||||
match = false;
|
||||
else if (designation != null && !designation.equalsIgnoreCase(m.getDesignation()))
|
||||
match = false;
|
||||
else if (!Double.isNaN(diameter) && (Math.abs(diameter - m.getDiameter()) > 0.0015))
|
||||
match = false;
|
||||
else if (!Double.isNaN(length) && (Math.abs(length - m.getLength()) > 0.0015))
|
||||
match = false;
|
||||
|
||||
if (match)
|
||||
results.add(m);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a motor to the database. If a matching ThrustCurveMototSet is found,
|
||||
@ -59,7 +99,7 @@ public abstract class MotorSetDatabase {
|
||||
*/
|
||||
protected void addMotor(ThrustCurveMotor motor) {
|
||||
// Iterate from last to first, as this is most likely to hit early when loading files
|
||||
for (int i = motorSets.size()-1; i>= 0; i--) {
|
||||
for (int i = motorSets.size() - 1; i >= 0; i--) {
|
||||
ThrustCurveMotorSet set = motorSets.get(i);
|
||||
if (set.matches(motor)) {
|
||||
set.addMotor(motor);
|
||||
@ -73,9 +113,9 @@ public abstract class MotorSetDatabase {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Start loading the motors. If asynchronous
|
||||
*
|
||||
@ -102,7 +142,17 @@ public abstract class MotorSetDatabase {
|
||||
public boolean isLoaded() {
|
||||
return endedLoading;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Mark that this database is in use or a place is waiting for the database to
|
||||
* become loaded. This can be used in conjunction with {@link #isLoaded()} to load
|
||||
* the database without blocking.
|
||||
*/
|
||||
public void setInUse() {
|
||||
inUse = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Block the current thread until loading of the motors has been completed.
|
||||
@ -110,6 +160,7 @@ public abstract class MotorSetDatabase {
|
||||
* @throws IllegalStateException if startLoading() has not been called.
|
||||
*/
|
||||
public void blockUntilLoaded() {
|
||||
inUse = true;
|
||||
if (!startedLoading) {
|
||||
throw new IllegalStateException("startLoading() has not been called");
|
||||
}
|
||||
@ -125,7 +176,7 @@ public abstract class MotorSetDatabase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Used for loading the motor database. This method will be called in a background
|
||||
@ -135,7 +186,7 @@ public abstract class MotorSetDatabase {
|
||||
protected abstract void loadMotors();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates the motor list, calls {@link #loadMotors()}, sorts the list and marks
|
||||
* the motors as loaded. This method is called either synchronously or from the
|
||||
@ -150,9 +201,9 @@ public abstract class MotorSetDatabase {
|
||||
}
|
||||
Collections.sort(motorSets);
|
||||
motorSets = Collections.unmodifiableList(motorSets);
|
||||
synchronized (MotorSetDatabase.this) {
|
||||
synchronized (ThrustCurveMotorSetDatabase.this) {
|
||||
endedLoading = true;
|
||||
MotorSetDatabase.this.notifyAll();
|
||||
ThrustCurveMotorSetDatabase.this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,5 +219,5 @@ public abstract class MotorSetDatabase {
|
||||
performMotorLoading();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -23,6 +23,7 @@ import net.sf.openrocket.file.simplesax.PlainTextHandler;
|
||||
import net.sf.openrocket.file.simplesax.SimpleSAX;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.rocketcomponent.BodyComponent;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.Bulkhead;
|
||||
@ -67,6 +68,7 @@ import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.GUISimulationConditions;
|
||||
import net.sf.openrocket.simulation.FlightEvent.Type;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
@ -988,21 +990,22 @@ class MotorHandler extends ElementHandler {
|
||||
warnings.add(Warning.fromString("No motor specified, ignoring."));
|
||||
return null;
|
||||
}
|
||||
Motor[] motors = Databases.findMotors(type, manufacturer, designation, diameter, length);
|
||||
if (motors.length == 0) {
|
||||
List<ThrustCurveMotor> motors = Application.getMotorSetDatabase().findMotors(type, manufacturer,
|
||||
designation, diameter, length);
|
||||
if (motors.size() == 0) {
|
||||
String str = "No motor with designation '" + designation + "'";
|
||||
if (manufacturer != null)
|
||||
str += " for manufacturer '" + manufacturer + "'";
|
||||
warnings.add(Warning.fromString(str + " found."));
|
||||
return null;
|
||||
}
|
||||
if (motors.length > 1) {
|
||||
if (motors.size() > 1) {
|
||||
String str = "Multiple motors with designation '" + designation + "'";
|
||||
if (manufacturer != null)
|
||||
str += " for manufacturer '" + manufacturer + "'";
|
||||
warnings.add(Warning.fromString(str + " found, one chosen arbitrarily."));
|
||||
}
|
||||
return motors[0];
|
||||
return motors.get(0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -32,42 +32,43 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
private static final String CONFIGDIALOGPACKAGE = "net.sf.openrocket.gui.configdialog";
|
||||
private static final String CONFIGDIALOGPOSTFIX = "Config";
|
||||
|
||||
|
||||
private static ComponentConfigDialog dialog = null;
|
||||
|
||||
private static ComponentConfigDialog dialog = null;
|
||||
|
||||
|
||||
private OpenRocketDocument document = null;
|
||||
private RocketComponent component = null;
|
||||
private RocketComponentConfig configurator = null;
|
||||
|
||||
private final Window parent;
|
||||
|
||||
private ComponentConfigDialog(Window parent, OpenRocketDocument document,
|
||||
private ComponentConfigDialog(Window parent, OpenRocketDocument document,
|
||||
RocketComponent component) {
|
||||
super(parent);
|
||||
this.parent = parent;
|
||||
|
||||
setComponent(document, component);
|
||||
|
||||
GUIUtil.setDisposableDialogOptions(this, null);
|
||||
|
||||
// Set window position according to preferences, and set prefs when moving
|
||||
Point position = Prefs.getWindowPosition(this.getClass());
|
||||
if (position == null)
|
||||
this.setLocationByPlatform(true);
|
||||
else
|
||||
if (position != null) {
|
||||
this.setLocationByPlatform(false);
|
||||
this.setLocation(position);
|
||||
|
||||
}
|
||||
|
||||
this.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentMoved(ComponentEvent e) {
|
||||
Prefs.setWindowPosition(ComponentConfigDialog.this.getClass(),
|
||||
Prefs.setWindowPosition(ComponentConfigDialog.this.getClass(),
|
||||
ComponentConfigDialog.this.getLocation());
|
||||
}
|
||||
});
|
||||
|
||||
GUIUtil.setDisposableDialogOptions(this, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the component being configured. The listening connections of the old configurator
|
||||
* will be removed and the new ones created.
|
||||
@ -78,10 +79,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
if (this.document != null) {
|
||||
this.document.getRocket().removeComponentChangeListener(this);
|
||||
}
|
||||
|
||||
|
||||
if (configurator != null) {
|
||||
// Remove listeners by setting all applicable models to null
|
||||
GUIUtil.setNullModels(configurator); // null-safe
|
||||
GUIUtil.setNullModels(configurator); // null-safe
|
||||
}
|
||||
|
||||
this.document = document;
|
||||
@ -92,11 +93,11 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
this.setContentPane(configurator);
|
||||
configurator.updateFields();
|
||||
|
||||
setTitle(component.getComponentName()+" configuration");
|
||||
|
||||
// Dimension pref = getPreferredSize();
|
||||
// Dimension real = getSize();
|
||||
// if (pref.width > real.width || pref.height > real.height)
|
||||
setTitle(component.getComponentName() + " configuration");
|
||||
|
||||
// Dimension pref = getPreferredSize();
|
||||
// Dimension real = getSize();
|
||||
// if (pref.width > real.width || pref.height > real.height)
|
||||
this.pack();
|
||||
}
|
||||
|
||||
@ -104,15 +105,15 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
* Return the configurator panel of the current component.
|
||||
*/
|
||||
private RocketComponentConfig getDialogContents() {
|
||||
Constructor<? extends RocketComponentConfig> c =
|
||||
findDialogContentsConstructor(component);
|
||||
Constructor<? extends RocketComponentConfig> c =
|
||||
findDialogContentsConstructor(component);
|
||||
if (c != null) {
|
||||
try {
|
||||
return (RocketComponentConfig) c.newInstance(component);
|
||||
} catch (InstantiationException e) {
|
||||
throw new BugException("BUG in constructor reflection",e);
|
||||
throw new BugException("BUG in constructor reflection", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new BugException("BUG in constructor reflection",e);
|
||||
throw new BugException("BUG in constructor reflection", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Reflection.handleWrappedException(e);
|
||||
}
|
||||
@ -120,16 +121,15 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
|
||||
// Should never be reached, since RocketComponentConfig should catch all
|
||||
// components without their own configurator.
|
||||
throw new BugException("Unable to find any configurator for "+component);
|
||||
throw new BugException("Unable to find any configurator for " + component);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the Constructor of the given component's config dialog panel in
|
||||
* CONFIGDIALOGPACKAGE.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Constructor<? extends RocketComponentConfig>
|
||||
findDialogContentsConstructor(RocketComponent component) {
|
||||
private static Constructor<? extends RocketComponentConfig> findDialogContentsConstructor(RocketComponent component) {
|
||||
Class<?> currentclass;
|
||||
String currentclassname;
|
||||
String configclassname;
|
||||
@ -143,23 +143,24 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
int index = currentclassname.lastIndexOf('.');
|
||||
if (index >= 0)
|
||||
currentclassname = currentclassname.substring(index + 1);
|
||||
configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
|
||||
CONFIGDIALOGPOSTFIX;
|
||||
configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
|
||||
CONFIGDIALOGPOSTFIX;
|
||||
|
||||
try {
|
||||
configclass = Class.forName(configclassname);
|
||||
c = (Constructor<? extends RocketComponentConfig>)
|
||||
configclass.getConstructor(RocketComponent.class);
|
||||
configclass.getConstructor(RocketComponent.class);
|
||||
return c;
|
||||
} catch (Exception ignore) { }
|
||||
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
currentclass = currentclass.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////// Static dialog /////////
|
||||
|
||||
@ -170,7 +171,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
* @param document the document to configure.
|
||||
* @param component the component to configure.
|
||||
*/
|
||||
public static void showDialog(Window parent, OpenRocketDocument document,
|
||||
public static void showDialog(Window parent, OpenRocketDocument document,
|
||||
RocketComponent component) {
|
||||
if (dialog != null)
|
||||
dialog.dispose();
|
||||
@ -178,11 +179,11 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
dialog = new ComponentConfigDialog(parent, document, component);
|
||||
dialog.setVisible(true);
|
||||
|
||||
document.addUndoPosition("Modify "+component.getComponentName());
|
||||
document.addUndoPosition("Modify " + component.getComponentName());
|
||||
}
|
||||
|
||||
|
||||
/* package */
|
||||
/* package */
|
||||
static void showDialog(RocketComponent component) {
|
||||
showDialog(dialog.parent, dialog.document, component);
|
||||
}
|
||||
@ -194,7 +195,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
if (dialog != null)
|
||||
dialog.setVisible(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add an undo position for the current document. This is intended for use only
|
||||
@ -202,7 +203,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
*
|
||||
* @param description Description of the undoable action
|
||||
*/
|
||||
/*package*/ static void addUndoPosition(String description) {
|
||||
/*package*/static void addUndoPosition(String description) {
|
||||
if (dialog == null) {
|
||||
throw new IllegalStateException("Dialog not open, report bug!");
|
||||
}
|
||||
@ -221,16 +222,16 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
|
||||
* Returns whether the singleton configuration dialog is currently visible or not.
|
||||
*/
|
||||
public static boolean isDialogVisible() {
|
||||
return (dialog!=null) && (dialog.isVisible());
|
||||
return (dialog != null) && (dialog.isVisible());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void componentChanged(ComponentChangeEvent e) {
|
||||
if (e.isTreeChange() || e.isUndoChange()) {
|
||||
|
||||
// Hide dialog in case of tree or undo change
|
||||
dialog.setVisible(false);
|
||||
|
||||
|
||||
} else {
|
||||
/*
|
||||
* TODO: HIGH: The line below has caused a NullPointerException (without null check)
|
||||
|
@ -13,6 +13,7 @@ import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
@ -25,7 +26,7 @@ import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
|
||||
import net.sf.openrocket.gui.components.BasicSlider;
|
||||
import net.sf.openrocket.gui.components.StyledLabel;
|
||||
import net.sf.openrocket.gui.components.UnitSelector;
|
||||
import net.sf.openrocket.gui.dialogs.MotorChooserDialog;
|
||||
import net.sf.openrocket.gui.dialogs.motor.MotorChooserDialog;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.rocketcomponent.Configuration;
|
||||
@ -150,9 +151,9 @@ public class MotorConfig extends JPanel {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String id = configuration.getMotorConfigurationID();
|
||||
|
||||
// TODO: HIGH: Assumes only ThrustCurveMotors exist
|
||||
MotorChooserDialog dialog = new MotorChooserDialog((ThrustCurveMotor) mount.getMotor(id),
|
||||
mount.getMotorDelay(id), mount.getMotorMountDiameter());
|
||||
MotorChooserDialog dialog = new MotorChooserDialog(mount.getMotor(id),
|
||||
mount.getMotorDelay(id), mount.getMotorMountDiameter(),
|
||||
SwingUtilities.getWindowAncestor(MotorConfig.this));
|
||||
dialog.setVisible(true);
|
||||
Motor m = dialog.getSelectedMotor();
|
||||
double d = dialog.getSelectedDelay();
|
||||
@ -204,7 +205,7 @@ public class MotorConfig extends JPanel {
|
||||
} else {
|
||||
String str = "";
|
||||
if (m instanceof ThrustCurveMotor)
|
||||
str = ((ThrustCurveMotor) m).getManufacturer() + "";
|
||||
str = ((ThrustCurveMotor) m).getManufacturer() + " ";
|
||||
str += m.getDesignation(mount.getMotorDelay(id));
|
||||
motorLabel.setText(str);
|
||||
}
|
||||
|
@ -26,9 +26,9 @@ import javax.swing.table.TableColumnModel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.gui.dialogs.motor.MotorChooserDialog;
|
||||
import net.sf.openrocket.gui.main.BasicFrame;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
@ -317,10 +317,8 @@ public class EditMotorConfigurationDialog extends JDialog {
|
||||
if (currentID == null || currentMount == null)
|
||||
return;
|
||||
|
||||
// TODO: HIGH: Assumes only ThrustCurveMotors exist
|
||||
MotorChooserDialog dialog = new MotorChooserDialog((ThrustCurveMotor) currentMount.getMotor(currentID),
|
||||
currentMount.getMotorDelay(currentID), currentMount.getMotorMountDiameter(),
|
||||
this);
|
||||
MotorChooserDialog dialog = new MotorChooserDialog(currentMount.getMotor(currentID),
|
||||
currentMount.getMotorDelay(currentID), currentMount.getMotorMountDiameter(), this);
|
||||
dialog.setVisible(true);
|
||||
Motor m = dialog.getSelectedMotor();
|
||||
double d = dialog.getSelectedDelay();
|
||||
|
@ -1,665 +0,0 @@
|
||||
package net.sf.openrocket.gui.dialogs;
|
||||
|
||||
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Font;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.RowFilter;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.database.Databases;
|
||||
import net.sf.openrocket.gui.components.StyledLabel;
|
||||
import net.sf.openrocket.motor.DesignationComparator;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.unit.Value;
|
||||
import net.sf.openrocket.unit.ValueComparator;
|
||||
import net.sf.openrocket.util.GUIUtil;
|
||||
import net.sf.openrocket.util.Prefs;
|
||||
|
||||
/*
|
||||
* TODO: HIGH: Only supports ThrustCurveMotors
|
||||
*/
|
||||
public class MotorChooserDialog extends JDialog {
|
||||
|
||||
private static final int SHOW_ALL = 0;
|
||||
private static final int SHOW_SMALLER = 1;
|
||||
private static final int SHOW_EXACT = 2;
|
||||
private static final String[] SHOW_DESCRIPTIONS = {
|
||||
"Show all motors",
|
||||
"Show motors with diameter less than that of the motor mount",
|
||||
"Show motors with diameter equal to that of the motor mount"
|
||||
};
|
||||
private static final int SHOW_MAX = 2;
|
||||
|
||||
private final JTextField searchField;
|
||||
private String[] searchTerms = new String[0];
|
||||
|
||||
private final double diameter;
|
||||
|
||||
private ThrustCurveMotor selectedMotor = null;
|
||||
private double selectedDelay = 0;
|
||||
|
||||
private JTable table;
|
||||
private TableRowSorter<TableModel> sorter;
|
||||
private JComboBox delayBox;
|
||||
private MotorDatabaseModel model;
|
||||
|
||||
private boolean okClicked = false;
|
||||
|
||||
|
||||
public MotorChooserDialog(double diameter) {
|
||||
this(null, 5, diameter, null);
|
||||
}
|
||||
|
||||
public MotorChooserDialog(ThrustCurveMotor current, double delay, double diameter) {
|
||||
this(current, delay, diameter, null);
|
||||
}
|
||||
|
||||
public MotorChooserDialog(ThrustCurveMotor current, double delay, double diameter, Window owner) {
|
||||
super(owner, "Select a rocket motor", Dialog.ModalityType.APPLICATION_MODAL);
|
||||
|
||||
JButton button;
|
||||
|
||||
this.selectedMotor = current;
|
||||
this.selectedDelay = delay;
|
||||
this.diameter = diameter;
|
||||
|
||||
JPanel panel = new JPanel(new MigLayout("fill", "[grow][]"));
|
||||
|
||||
// Label
|
||||
JLabel label = new JLabel("Select a rocket motor:");
|
||||
label.setFont(label.getFont().deriveFont(Font.BOLD));
|
||||
panel.add(label, "growx");
|
||||
|
||||
label = new JLabel("Motor mount diameter: " +
|
||||
UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(diameter));
|
||||
panel.add(label, "gapleft para, wrap paragraph");
|
||||
|
||||
|
||||
// Diameter selection
|
||||
JComboBox combo = new JComboBox(SHOW_DESCRIPTIONS);
|
||||
combo.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JComboBox cb = (JComboBox) e.getSource();
|
||||
int sel = cb.getSelectedIndex();
|
||||
if ((sel < 0) || (sel > SHOW_MAX))
|
||||
sel = SHOW_ALL;
|
||||
switch (sel) {
|
||||
case SHOW_ALL:
|
||||
sorter.setRowFilter(new MotorRowFilterAll());
|
||||
break;
|
||||
|
||||
case SHOW_SMALLER:
|
||||
sorter.setRowFilter(new MotorRowFilterSmaller());
|
||||
break;
|
||||
|
||||
case SHOW_EXACT:
|
||||
sorter.setRowFilter(new MotorRowFilterExact());
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (false) : "Should not occur.";
|
||||
}
|
||||
Prefs.putChoise("MotorDiameterMatch", sel);
|
||||
setSelectionVisible();
|
||||
}
|
||||
});
|
||||
panel.add(combo, "growx 1000");
|
||||
|
||||
|
||||
|
||||
label = new JLabel("Search:");
|
||||
panel.add(label, "gapleft para, split 2");
|
||||
|
||||
searchField = new JTextField();
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
private void update() {
|
||||
String text = searchField.getText().trim();
|
||||
String[] split = text.split("\\s+");
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
for (String s : split) {
|
||||
s = s.trim().toLowerCase();
|
||||
if (s.length() > 0) {
|
||||
list.add(s);
|
||||
}
|
||||
}
|
||||
searchTerms = list.toArray(new String[0]);
|
||||
sorter.sort();
|
||||
}
|
||||
});
|
||||
panel.add(searchField, "growx 1, wrap");
|
||||
|
||||
|
||||
|
||||
// Table, overridden to show meaningful tooltip texts
|
||||
model = new MotorDatabaseModel(current);
|
||||
table = new JTable(model) {
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent e) {
|
||||
java.awt.Point p = e.getPoint();
|
||||
int colIndex = columnAtPoint(p);
|
||||
int viewRow = rowAtPoint(p);
|
||||
if (viewRow < 0)
|
||||
return null;
|
||||
int rowIndex = convertRowIndexToModel(viewRow);
|
||||
ThrustCurveMotor motor = model.getMotor(rowIndex);
|
||||
|
||||
if (colIndex < 0 || colIndex >= MotorColumns.values().length)
|
||||
return null;
|
||||
|
||||
return MotorColumns.values()[colIndex].getToolTipText(motor);
|
||||
}
|
||||
};
|
||||
|
||||
// Set comparators and widths
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
sorter = new TableRowSorter<TableModel>(model);
|
||||
for (int i = 0; i < MotorColumns.values().length; i++) {
|
||||
MotorColumns column = MotorColumns.values()[i];
|
||||
sorter.setComparator(i, column.getComparator());
|
||||
table.getColumnModel().getColumn(i).setPreferredWidth(column.getWidth());
|
||||
}
|
||||
table.setRowSorter(sorter);
|
||||
|
||||
// Set selection and double-click listeners
|
||||
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
int row = table.getSelectedRow();
|
||||
if (row >= 0) {
|
||||
row = table.convertRowIndexToModel(row);
|
||||
ThrustCurveMotor m = model.getMotor(row);
|
||||
// TODO: HIGH: equals or == ?
|
||||
if (!m.equals(selectedMotor)) {
|
||||
selectedMotor = model.getMotor(row);
|
||||
setDelays(true); // Reset delay times
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
|
||||
okClicked = true;
|
||||
MotorChooserDialog.this.setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
// (Current selection and scrolling performed later)
|
||||
|
||||
JScrollPane scrollpane = new JScrollPane();
|
||||
scrollpane.setViewportView(table);
|
||||
panel.add(scrollpane, "spanx, grow, width :700:, height :300:, wrap paragraph");
|
||||
|
||||
|
||||
// Ejection delay
|
||||
panel.add(new JLabel("Select ejection charge delay:"), "spanx, split 3, gap rel");
|
||||
|
||||
delayBox = new JComboBox();
|
||||
delayBox.setEditable(true);
|
||||
delayBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JComboBox cb = (JComboBox) e.getSource();
|
||||
String sel = (String) cb.getSelectedItem();
|
||||
if (sel.equalsIgnoreCase("None")) {
|
||||
selectedDelay = Motor.PLUGGED;
|
||||
} else {
|
||||
try {
|
||||
selectedDelay = Double.parseDouble(sel);
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
}
|
||||
setDelays(false);
|
||||
}
|
||||
});
|
||||
panel.add(delayBox, "gapright unrel");
|
||||
panel.add(new StyledLabel("(Number of seconds or \"None\")", -1), "wrap para");
|
||||
setDelays(false);
|
||||
|
||||
|
||||
JButton okButton = new JButton("OK");
|
||||
okButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
okClicked = true;
|
||||
MotorChooserDialog.this.setVisible(false);
|
||||
}
|
||||
});
|
||||
panel.add(okButton, "spanx, split, tag ok");
|
||||
|
||||
button = new JButton("Cancel");
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
MotorChooserDialog.this.setVisible(false);
|
||||
}
|
||||
});
|
||||
panel.add(button, "tag cancel");
|
||||
|
||||
|
||||
// Sets the filter:
|
||||
int showMode = Prefs.getChoise("MotorDiameterMatch", SHOW_MAX, SHOW_EXACT);
|
||||
combo.setSelectedIndex(showMode);
|
||||
|
||||
|
||||
this.add(panel);
|
||||
this.pack();
|
||||
// this.setAlwaysOnTop(true);
|
||||
|
||||
this.setLocationByPlatform(true);
|
||||
GUIUtil.setDisposableDialogOptions(this, okButton);
|
||||
|
||||
// Table can be scrolled only after pack() has been called
|
||||
setSelectionVisible();
|
||||
|
||||
// Focus the search field
|
||||
searchField.grabFocus();
|
||||
}
|
||||
|
||||
private void setSelectionVisible() {
|
||||
if (selectedMotor != null) {
|
||||
int index = table.convertRowIndexToView(model.getIndex(selectedMotor));
|
||||
table.getSelectionModel().setSelectionInterval(index, index);
|
||||
Rectangle rect = table.getCellRect(index, 0, true);
|
||||
rect = new Rectangle(rect.x, rect.y - 100, rect.width, rect.height + 200);
|
||||
table.scrollRectToVisible(rect);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the values in the delay combo box. If <code>reset</code> is <code>true</code>
|
||||
* then sets the selected value as the value closest to selectedDelay, otherwise
|
||||
* leaves selection alone.
|
||||
*/
|
||||
private void setDelays(boolean reset) {
|
||||
if (selectedMotor == null) {
|
||||
|
||||
delayBox.setModel(new DefaultComboBoxModel(new String[] { "None" }));
|
||||
delayBox.setSelectedIndex(0);
|
||||
|
||||
} else {
|
||||
|
||||
double[] delays = selectedMotor.getStandardDelays();
|
||||
String[] delayStrings = new String[delays.length];
|
||||
double currentDelay = selectedDelay; // Store current setting locally
|
||||
|
||||
for (int i = 0; i < delays.length; i++) {
|
||||
delayStrings[i] = ThrustCurveMotor.getDelayString(delays[i], "None");
|
||||
}
|
||||
delayBox.setModel(new DefaultComboBoxModel(delayStrings));
|
||||
|
||||
if (reset) {
|
||||
|
||||
// Find and set the closest value
|
||||
double closest = Double.NaN;
|
||||
for (int i = 0; i < delays.length; i++) {
|
||||
// if-condition to always become true for NaN
|
||||
if (!(Math.abs(delays[i] - currentDelay) > Math.abs(closest - currentDelay))) {
|
||||
closest = delays[i];
|
||||
}
|
||||
}
|
||||
if (!Double.isNaN(closest)) {
|
||||
selectedDelay = closest;
|
||||
delayBox.setSelectedItem(ThrustCurveMotor.getDelayString(closest, "None"));
|
||||
} else {
|
||||
delayBox.setSelectedItem("None");
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
selectedDelay = currentDelay;
|
||||
delayBox.setSelectedItem(ThrustCurveMotor.getDelayString(currentDelay, "None"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ThrustCurveMotor getSelectedMotor() {
|
||||
if (!okClicked)
|
||||
return null;
|
||||
return selectedMotor;
|
||||
}
|
||||
|
||||
|
||||
public double getSelectedDelay() {
|
||||
return selectedDelay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////// JTable elements ////////////////
|
||||
|
||||
|
||||
/**
|
||||
* Enum defining the table columns.
|
||||
*/
|
||||
private enum MotorColumns {
|
||||
MANUFACTURER("Manufacturer", 100) {
|
||||
@Override
|
||||
public String getValue(ThrustCurveMotor m) {
|
||||
return m.getManufacturer().getDisplayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return Collator.getInstance();
|
||||
}
|
||||
},
|
||||
DESIGNATION("Designation") {
|
||||
@Override
|
||||
public String getValue(ThrustCurveMotor m) {
|
||||
return m.getDesignation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return new DesignationComparator();
|
||||
}
|
||||
},
|
||||
TYPE("Type") {
|
||||
@Override
|
||||
public String getValue(ThrustCurveMotor m) {
|
||||
return m.getMotorType().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return Collator.getInstance();
|
||||
}
|
||||
},
|
||||
DIAMETER("Diameter") {
|
||||
@Override
|
||||
public Object getValue(ThrustCurveMotor m) {
|
||||
return new Value(m.getDiameter(), UnitGroup.UNITS_MOTOR_DIMENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return ValueComparator.INSTANCE;
|
||||
}
|
||||
},
|
||||
LENGTH("Length") {
|
||||
@Override
|
||||
public Object getValue(ThrustCurveMotor m) {
|
||||
return new Value(m.getLength(), UnitGroup.UNITS_MOTOR_DIMENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return ValueComparator.INSTANCE;
|
||||
}
|
||||
},
|
||||
IMPULSE("Impulse") {
|
||||
@Override
|
||||
public Object getValue(ThrustCurveMotor m) {
|
||||
return new Value(m.getTotalImpulseEstimate(), UnitGroup.UNITS_IMPULSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return ValueComparator.INSTANCE;
|
||||
}
|
||||
},
|
||||
TIME("Burn time") {
|
||||
@Override
|
||||
public Object getValue(ThrustCurveMotor m) {
|
||||
return new Value(m.getBurnTimeEstimate(), UnitGroup.UNITS_SHORT_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return ValueComparator.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final String title;
|
||||
private final int width;
|
||||
|
||||
MotorColumns(String title) {
|
||||
this(title, 50);
|
||||
}
|
||||
|
||||
MotorColumns(String title, int width) {
|
||||
this.title = title;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
|
||||
public abstract Object getValue(ThrustCurveMotor m);
|
||||
|
||||
public abstract Comparator<?> getComparator();
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public String getToolTipText(ThrustCurveMotor m) {
|
||||
String tip = "<html>";
|
||||
tip += "<b>" + m.toString() + "</b>";
|
||||
tip += " (" + m.getMotorType().getDescription() + ")<br><hr>";
|
||||
|
||||
String desc = m.getDescription().trim();
|
||||
if (desc.length() > 0) {
|
||||
tip += "<i>" + desc.replace("\n", "<br>") + "</i><br><hr>";
|
||||
}
|
||||
|
||||
tip += ("Diameter: " +
|
||||
UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(m.getDiameter()) +
|
||||
"<br>");
|
||||
tip += ("Length: " +
|
||||
UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(m.getLength()) +
|
||||
"<br>");
|
||||
tip += ("Maximum thrust: " +
|
||||
UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(m.getMaxThrustEstimate()) +
|
||||
"<br>");
|
||||
tip += ("Average thrust: " +
|
||||
UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(m.getAverageThrustEstimate()) +
|
||||
"<br>");
|
||||
tip += ("Burn time: " +
|
||||
UnitGroup.UNITS_SHORT_TIME.getDefaultUnit()
|
||||
.toStringUnit(m.getBurnTimeEstimate()) + "<br>");
|
||||
tip += ("Total impulse: " +
|
||||
UnitGroup.UNITS_IMPULSE.getDefaultUnit()
|
||||
.toStringUnit(m.getTotalImpulseEstimate()) + "<br>");
|
||||
tip += ("Launch mass: " +
|
||||
UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(m.getLaunchCG().weight) +
|
||||
"<br>");
|
||||
tip += ("Empty mass: " +
|
||||
UnitGroup.UNITS_MASS.getDefaultUnit()
|
||||
.toStringUnit(m.getEmptyCG().weight));
|
||||
return tip;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The JTable model. Includes an extra motor, given in the constructor,
|
||||
* if it is not already in the database.
|
||||
*/
|
||||
private class MotorDatabaseModel extends AbstractTableModel {
|
||||
private final ThrustCurveMotor extra;
|
||||
|
||||
public MotorDatabaseModel(ThrustCurveMotor current) {
|
||||
if (Databases.MOTOR.contains(current))
|
||||
extra = null;
|
||||
else
|
||||
extra = current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return MotorColumns.values().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
if (extra == null)
|
||||
return Databases.MOTOR.size();
|
||||
else
|
||||
return Databases.MOTOR.size() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
MotorColumns column = getColumn(columnIndex);
|
||||
if (extra == null) {
|
||||
return column.getValue(Databases.MOTOR.get(rowIndex));
|
||||
} else {
|
||||
if (rowIndex == 0)
|
||||
return column.getValue(extra);
|
||||
else
|
||||
return column.getValue(Databases.MOTOR.get(rowIndex - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
return getColumn(columnIndex).getTitle();
|
||||
}
|
||||
|
||||
|
||||
public ThrustCurveMotor getMotor(int rowIndex) {
|
||||
if (extra == null) {
|
||||
return Databases.MOTOR.get(rowIndex);
|
||||
} else {
|
||||
if (rowIndex == 0)
|
||||
return extra;
|
||||
else
|
||||
return Databases.MOTOR.get(rowIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndex(ThrustCurveMotor m) {
|
||||
if (extra == null) {
|
||||
return Databases.MOTOR.indexOf(m);
|
||||
} else {
|
||||
if (extra.equals(m))
|
||||
return 0;
|
||||
else
|
||||
return Databases.MOTOR.indexOf(m) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private MotorColumns getColumn(int index) {
|
||||
return MotorColumns.values()[index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////// Row filters
|
||||
|
||||
/**
|
||||
* Abstract adapter class.
|
||||
*/
|
||||
private abstract class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
@Override
|
||||
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
|
||||
int index = entry.getIdentifier();
|
||||
ThrustCurveMotor m = model.getMotor(index);
|
||||
return filterByDiameter(m) && filterByString(m);
|
||||
}
|
||||
|
||||
public abstract boolean filterByDiameter(ThrustCurveMotor m);
|
||||
|
||||
|
||||
public boolean filterByString(ThrustCurveMotor m) {
|
||||
main: for (String s : searchTerms) {
|
||||
for (MotorColumns col : MotorColumns.values()) {
|
||||
String str = col.getValue(m).toString().toLowerCase();
|
||||
if (str.indexOf(s) >= 0)
|
||||
continue main;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all motors.
|
||||
*/
|
||||
private class MotorRowFilterAll extends MotorRowFilter {
|
||||
@Override
|
||||
public boolean filterByDiameter(ThrustCurveMotor m) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show motors smaller than the mount.
|
||||
*/
|
||||
private class MotorRowFilterSmaller extends MotorRowFilter {
|
||||
@Override
|
||||
public boolean filterByDiameter(ThrustCurveMotor m) {
|
||||
return (m.getDiameter() <= diameter + 0.0004);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show motors that fit the mount.
|
||||
*/
|
||||
private class MotorRowFilterExact extends MotorRowFilter {
|
||||
@Override
|
||||
public boolean filterByDiameter(ThrustCurveMotor m) {
|
||||
return ((m.getDiameter() <= diameter + 0.0004) && (m.getDiameter() >= diameter - 0.0015));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package net.sf.openrocket.gui.dialogs;
|
||||
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.Timer;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.GUIUtil;
|
||||
|
||||
public class MotorDatabaseLoadingDialog extends JDialog {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
|
||||
private MotorDatabaseLoadingDialog(Window parent) {
|
||||
super(parent, "Loading motors", ModalityType.APPLICATION_MODAL);
|
||||
|
||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||
panel.add(new JLabel("Loading motors..."), "wrap para");
|
||||
|
||||
JProgressBar progress = new JProgressBar();
|
||||
progress.setIndeterminate(true);
|
||||
panel.add(progress, "growx");
|
||||
|
||||
this.add(panel);
|
||||
this.pack();
|
||||
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
||||
this.setLocationByPlatform(true);
|
||||
GUIUtil.setWindowIcons(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the motor database is loaded and block until it is.
|
||||
* An uncloseable modal dialog window is opened while loading.
|
||||
*
|
||||
* @param parent the parent window for the dialog, or <code>null</code>
|
||||
*/
|
||||
public static void check(Window parent) {
|
||||
final ThrustCurveMotorSetDatabase db = Application.getMotorSetDatabase();
|
||||
if (db.isLoaded())
|
||||
return;
|
||||
|
||||
log.info(1, "Motor database not loaded yet, displaying dialog");
|
||||
|
||||
final MotorDatabaseLoadingDialog dialog = new MotorDatabaseLoadingDialog(parent);
|
||||
|
||||
final Timer timer = new Timer(100, new ActionListener() {
|
||||
private int count = 0;
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
count++;
|
||||
if (db.isLoaded()) {
|
||||
log.debug("Database loaded, closing dialog");
|
||||
dialog.setVisible(false);
|
||||
} else if (count % 10 == 0) {
|
||||
log.debug("Database not loaded, count=" + count);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
db.setInUse();
|
||||
timer.start();
|
||||
dialog.setVisible(true);
|
||||
timer.stop();
|
||||
|
||||
log.debug("Motor database now loaded");
|
||||
}
|
||||
|
||||
}
|
12
src/net/sf/openrocket/gui/dialogs/motor/CloseableDialog.java
Normal file
12
src/net/sf/openrocket/gui/dialogs/motor/CloseableDialog.java
Normal file
@ -0,0 +1,12 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor;
|
||||
|
||||
public interface CloseableDialog {
|
||||
|
||||
/**
|
||||
* Close this dialog.
|
||||
*
|
||||
* @param ok whether "OK" should be considered to have been clicked.
|
||||
*/
|
||||
public void close(boolean ok);
|
||||
|
||||
}
|
112
src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java
Normal file
112
src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java
Normal file
@ -0,0 +1,112 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor;
|
||||
|
||||
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.gui.dialogs.MotorDatabaseLoadingDialog;
|
||||
import net.sf.openrocket.gui.dialogs.motor.thrustcurve.ThrustCurveMotorSelectionPanel;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.util.GUIUtil;
|
||||
|
||||
public class MotorChooserDialog extends JDialog implements CloseableDialog {
|
||||
|
||||
private final ThrustCurveMotorSelectionPanel selectionPanel;
|
||||
|
||||
private boolean okClicked = false;
|
||||
|
||||
|
||||
public MotorChooserDialog(Motor current, double delay, double diameter, Window owner) {
|
||||
super(owner, "Select a rocket motor", Dialog.ModalityType.APPLICATION_MODAL);
|
||||
|
||||
// Check that the motor database has been loaded properly
|
||||
MotorDatabaseLoadingDialog.check(null);
|
||||
|
||||
|
||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||
|
||||
selectionPanel = new ThrustCurveMotorSelectionPanel((ThrustCurveMotor) current, delay, diameter);
|
||||
|
||||
panel.add(selectionPanel, "grow, wrap para");
|
||||
|
||||
|
||||
// OK / Cancel buttons
|
||||
|
||||
JButton okButton = new JButton("OK");
|
||||
okButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
close(true);
|
||||
}
|
||||
});
|
||||
panel.add(okButton, "tag ok, spanx, split");
|
||||
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
close(false);
|
||||
}
|
||||
});
|
||||
panel.add(cancelButton, "tag cancel");
|
||||
|
||||
this.add(panel);
|
||||
|
||||
this.setModal(true);
|
||||
this.pack();
|
||||
this.setLocationByPlatform(true);
|
||||
GUIUtil.setDisposableDialogOptions(this, okButton);
|
||||
|
||||
JComponent focus = selectionPanel.getDefaultFocus();
|
||||
if (focus != null) {
|
||||
focus.grabFocus();
|
||||
}
|
||||
|
||||
// Set the closeable dialog after all initialization
|
||||
selectionPanel.setCloseableDialog(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the motor selected by this chooser dialog, or <code>null</code> if the selection has been aborted.
|
||||
*
|
||||
* @return the selected motor, or <code>null</code> if no motor has been selected or the selection was canceled.
|
||||
*/
|
||||
public Motor getSelectedMotor() {
|
||||
if (!okClicked)
|
||||
return null;
|
||||
return selectionPanel.getSelectedMotor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the selected ejection charge delay.
|
||||
*
|
||||
* @return the selected ejection charge delay.
|
||||
*/
|
||||
public double getSelectedDelay() {
|
||||
return selectionPanel.getSelectedDelay();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void close(boolean ok) {
|
||||
okClicked = ok;
|
||||
this.setVisible(false);
|
||||
|
||||
Motor selected = getSelectedMotor();
|
||||
if (okClicked && selected != null) {
|
||||
selectionPanel.selectedMotor(selected);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
38
src/net/sf/openrocket/gui/dialogs/motor/MotorSelector.java
Normal file
38
src/net/sf/openrocket/gui/dialogs/motor/MotorSelector.java
Normal file
@ -0,0 +1,38 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
|
||||
public interface MotorSelector {
|
||||
|
||||
/**
|
||||
* Return the currently selected motor.
|
||||
*
|
||||
* @return the currently selected motor, or <code>null</code> if no motor is selected.
|
||||
*/
|
||||
public Motor getSelectedMotor();
|
||||
|
||||
/**
|
||||
* Return the currently selected ejection charge delay.
|
||||
*
|
||||
* @return the currently selected ejection charge delay.
|
||||
*/
|
||||
public double getSelectedDelay();
|
||||
|
||||
/**
|
||||
* Return the component that should have the default focus in this motor selector panel.
|
||||
*
|
||||
* @return the component that should have default focus, or <code>null</code> for none.
|
||||
*/
|
||||
public JComponent getDefaultFocus();
|
||||
|
||||
/**
|
||||
* Notify that the provided motor has been selected. This can be used to store preference
|
||||
* data for later usage.
|
||||
*
|
||||
* @param m the motor that was selected.
|
||||
*/
|
||||
public void selectedMotor(Motor m);
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
|
||||
class MotorHolder {
|
||||
|
||||
private final ThrustCurveMotor motor;
|
||||
private final int index;
|
||||
|
||||
public MotorHolder(ThrustCurveMotor motor, int index) {
|
||||
this.motor = motor;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public ThrustCurveMotor getMotor() {
|
||||
return motor;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return motor.getDesignation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof MotorHolder))
|
||||
return false;
|
||||
MotorHolder other = (MotorHolder) obj;
|
||||
return this.motor.equals(other.motor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return motor.hashCode();
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSet;
|
||||
import net.sf.openrocket.motor.DesignationComparator;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.unit.Value;
|
||||
import net.sf.openrocket.unit.ValueComparator;
|
||||
|
||||
/**
|
||||
* Enum defining the table columns.
|
||||
*/
|
||||
enum ThrustCurveMotorColumns {
|
||||
MANUFACTURER("Manufacturer", 100) {
|
||||
@Override
|
||||
public String getValue(ThrustCurveMotorSet m) {
|
||||
return m.getManufacturer().getDisplayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return Collator.getInstance();
|
||||
}
|
||||
},
|
||||
DESIGNATION("Designation") {
|
||||
@Override
|
||||
public String getValue(ThrustCurveMotorSet m) {
|
||||
return m.getDesignation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return new DesignationComparator();
|
||||
}
|
||||
},
|
||||
TYPE("Type") {
|
||||
@Override
|
||||
public String getValue(ThrustCurveMotorSet m) {
|
||||
return m.getType().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return Collator.getInstance();
|
||||
}
|
||||
},
|
||||
DIAMETER("Diameter") {
|
||||
@Override
|
||||
public Object getValue(ThrustCurveMotorSet m) {
|
||||
return new Value(m.getDiameter(), UnitGroup.UNITS_MOTOR_DIMENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return ValueComparator.INSTANCE;
|
||||
}
|
||||
},
|
||||
LENGTH("Length") {
|
||||
@Override
|
||||
public Object getValue(ThrustCurveMotorSet m) {
|
||||
return new Value(m.getLength(), UnitGroup.UNITS_MOTOR_DIMENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<?> getComparator() {
|
||||
return ValueComparator.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final String title;
|
||||
private final int width;
|
||||
|
||||
ThrustCurveMotorColumns(String title) {
|
||||
this(title, 50);
|
||||
}
|
||||
|
||||
ThrustCurveMotorColumns(String title, int width) {
|
||||
this.title = title;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
|
||||
public abstract Object getValue(ThrustCurveMotorSet m);
|
||||
|
||||
public abstract Comparator<?> getComparator();
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public String getToolTipText(ThrustCurveMotor m) {
|
||||
String tip = "<html>";
|
||||
tip += "<b>" + m.toString() + "</b>";
|
||||
tip += " (" + m.getMotorType().getDescription() + ")<br><hr>";
|
||||
|
||||
String desc = m.getDescription().trim();
|
||||
if (desc.length() > 0) {
|
||||
tip += "<i>" + desc.replace("\n", "<br>") + "</i><br><hr>";
|
||||
}
|
||||
|
||||
tip += ("Diameter: " +
|
||||
UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(m.getDiameter()) +
|
||||
"<br>");
|
||||
tip += ("Length: " +
|
||||
UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(m.getLength()) +
|
||||
"<br>");
|
||||
tip += ("Maximum thrust: " +
|
||||
UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(m.getMaxThrustEstimate()) +
|
||||
"<br>");
|
||||
tip += ("Average thrust: " +
|
||||
UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(m.getAverageThrustEstimate()) +
|
||||
"<br>");
|
||||
tip += ("Burn time: " +
|
||||
UnitGroup.UNITS_SHORT_TIME.getDefaultUnit()
|
||||
.toStringUnit(m.getBurnTimeEstimate()) + "<br>");
|
||||
tip += ("Total impulse: " +
|
||||
UnitGroup.UNITS_IMPULSE.getDefaultUnit()
|
||||
.toStringUnit(m.getTotalImpulseEstimate()) + "<br>");
|
||||
tip += ("Launch mass: " +
|
||||
UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(m.getLaunchCG().weight) +
|
||||
"<br>");
|
||||
tip += ("Empty mass: " +
|
||||
UnitGroup.UNITS_MASS.getDefaultUnit()
|
||||
.toStringUnit(m.getEmptyCG().weight));
|
||||
return tip;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSet;
|
||||
|
||||
class ThrustCurveMotorDatabaseModel extends AbstractTableModel {
|
||||
private final List<ThrustCurveMotorSet> database;
|
||||
|
||||
public ThrustCurveMotorDatabaseModel(List<ThrustCurveMotorSet> database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return ThrustCurveMotorColumns.values().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return database.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
ThrustCurveMotorColumns column = getColumn(columnIndex);
|
||||
return column.getValue(database.get(rowIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
return getColumn(columnIndex).getTitle();
|
||||
}
|
||||
|
||||
|
||||
public ThrustCurveMotorSet getMotorSet(int rowIndex) {
|
||||
return database.get(rowIndex);
|
||||
}
|
||||
|
||||
|
||||
public int getIndex(ThrustCurveMotorSet m) {
|
||||
return database.indexOf(m);
|
||||
}
|
||||
|
||||
private ThrustCurveMotorColumns getColumn(int index) {
|
||||
return ThrustCurveMotorColumns.values()[index];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSet;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.GUIUtil;
|
||||
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.plot.PlotOrientation;
|
||||
import org.jfree.chart.plot.XYPlot;
|
||||
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
|
||||
import org.jfree.data.xy.XYSeries;
|
||||
import org.jfree.data.xy.XYSeriesCollection;
|
||||
|
||||
public class ThrustCurveMotorPlotDialog extends JDialog {
|
||||
|
||||
public ThrustCurveMotorPlotDialog(ThrustCurveMotorSet motorSet, ThrustCurveMotor selectedMotor, Window parent) {
|
||||
super(parent, "Motor thrust curves", ModalityType.APPLICATION_MODAL);
|
||||
|
||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||
|
||||
// Thrust curve plot
|
||||
JFreeChart chart = ChartFactory.createXYLineChart(
|
||||
"Motor thrust curves", // title
|
||||
"Time / " + UnitGroup.UNITS_SHORT_TIME.getDefaultUnit().getUnit(), // xAxisLabel
|
||||
"Thrust / " + UnitGroup.UNITS_FORCE.getDefaultUnit().getUnit(), // yAxisLabel
|
||||
null, // dataset
|
||||
PlotOrientation.VERTICAL,
|
||||
true, // legend
|
||||
true, // tooltips
|
||||
false // urls
|
||||
);
|
||||
|
||||
|
||||
// Add the data and formatting to the plot
|
||||
XYPlot plot = chart.getXYPlot();
|
||||
|
||||
chart.setBackgroundPaint(panel.getBackground());
|
||||
plot.setBackgroundPaint(Color.WHITE);
|
||||
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
|
||||
plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
|
||||
|
||||
ChartPanel chartPanel = new ChartPanel(chart,
|
||||
false, // properties
|
||||
true, // save
|
||||
false, // print
|
||||
true, // zoom
|
||||
true); // tooltips
|
||||
chartPanel.setMouseWheelEnabled(true);
|
||||
chartPanel.setEnforceFileExtensions(true);
|
||||
chartPanel.setInitialDelay(500);
|
||||
|
||||
StandardXYItemRenderer renderer = new StandardXYItemRenderer();
|
||||
renderer.setBaseShapesVisible(true);
|
||||
renderer.setBaseShapesFilled(true);
|
||||
plot.setRenderer(renderer);
|
||||
|
||||
|
||||
// Create the plot data set
|
||||
XYSeriesCollection dataset = new XYSeriesCollection();
|
||||
List<ThrustCurveMotor> motors = motorSet.getMotors();
|
||||
|
||||
// Selected thrust curve
|
||||
int index = motors.indexOf(selectedMotor);
|
||||
int n = 0;
|
||||
dataset.addSeries(generateSeries(selectedMotor));
|
||||
renderer.setSeriesStroke(n, new BasicStroke(1.5f));
|
||||
if (index >= 0) {
|
||||
renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(index));
|
||||
}
|
||||
n++;
|
||||
|
||||
// Other thrust curves
|
||||
for (int i = 0; i < motors.size(); i++) {
|
||||
if (i == index)
|
||||
continue;
|
||||
|
||||
ThrustCurveMotor m = motors.get(i);
|
||||
dataset.addSeries(generateSeries(m));
|
||||
renderer.setSeriesStroke(n, new BasicStroke(1.5f));
|
||||
renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(i));
|
||||
renderer.setSeriesShape(n, new Rectangle());
|
||||
n++;
|
||||
}
|
||||
|
||||
plot.setDataset(dataset);
|
||||
|
||||
panel.add(chartPanel, "width 600:600:, height 400:400:, grow, wrap para");
|
||||
|
||||
|
||||
// Close button
|
||||
JButton close = new JButton("Close");
|
||||
close.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ThrustCurveMotorPlotDialog.this.setVisible(false);
|
||||
}
|
||||
});
|
||||
panel.add(close, "right, tag close");
|
||||
|
||||
|
||||
this.add(panel);
|
||||
|
||||
this.pack();
|
||||
GUIUtil.setDisposableDialogOptions(this, null);
|
||||
}
|
||||
|
||||
|
||||
private XYSeries generateSeries(ThrustCurveMotor motor) {
|
||||
XYSeries series = new XYSeries(motor.getManufacturer() + " " + motor.getDesignation());
|
||||
double[] time = motor.getTimePoints();
|
||||
double[] thrust = motor.getThrustPoints();
|
||||
|
||||
for (int j = 0; j < time.length; j++) {
|
||||
series.add(time[j], thrust[j]);
|
||||
}
|
||||
return series;
|
||||
}
|
||||
}
|
@ -0,0 +1,892 @@
|
||||
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Font;
|
||||
import java.awt.Paint;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JLayeredPane;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.RowFilter;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.TableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSet;
|
||||
import net.sf.openrocket.gui.components.StyledLabel;
|
||||
import net.sf.openrocket.gui.components.StyledLabel.Style;
|
||||
import net.sf.openrocket.gui.dialogs.motor.CloseableDialog;
|
||||
import net.sf.openrocket.gui.dialogs.motor.MotorSelector;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorDigest;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.GUIUtil;
|
||||
import net.sf.openrocket.util.Icons;
|
||||
import net.sf.openrocket.util.Prefs;
|
||||
|
||||
import org.jfree.chart.ChartColor;
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.ChartPanel;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.axis.ValueAxis;
|
||||
import org.jfree.chart.plot.PlotOrientation;
|
||||
import org.jfree.chart.plot.XYPlot;
|
||||
import org.jfree.chart.title.TextTitle;
|
||||
import org.jfree.data.xy.XYSeries;
|
||||
import org.jfree.data.xy.XYSeriesCollection;
|
||||
|
||||
public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelector {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
private static final int SHOW_ALL = 0;
|
||||
private static final int SHOW_SMALLER = 1;
|
||||
private static final int SHOW_EXACT = 2;
|
||||
private static final String[] SHOW_DESCRIPTIONS = {
|
||||
"Show all motors",
|
||||
"Show motors with diameter less than that of the motor mount",
|
||||
"Show motors with diameter equal to that of the motor mount"
|
||||
};
|
||||
private static final int SHOW_MAX = 2;
|
||||
|
||||
private static final int ZOOM_ICON_POSITION_NEGATIVE_X = 50;
|
||||
private static final int ZOOM_ICON_POSITION_POSITIVE_Y = 12;
|
||||
|
||||
private static final Paint[] CURVE_COLORS = ChartColor.createDefaultPaintArray();
|
||||
|
||||
private static final Color NO_COMMENT_COLOR = Color.GRAY;
|
||||
private static final Color WITH_COMMENT_COLOR = Color.BLACK;
|
||||
|
||||
private final List<ThrustCurveMotorSet> database;
|
||||
|
||||
private final double diameter;
|
||||
private CloseableDialog dialog = null;
|
||||
|
||||
|
||||
private final ThrustCurveMotorDatabaseModel model;
|
||||
private final JTable table;
|
||||
private final TableRowSorter<TableModel> sorter;
|
||||
|
||||
private final JTextField searchField;
|
||||
private String[] searchTerms = new String[0];
|
||||
|
||||
|
||||
private final JLabel curveSelectionLabel;
|
||||
private final JComboBox curveSelectionBox;
|
||||
private final DefaultComboBoxModel curveSelectionModel;
|
||||
|
||||
private final JLabel totalImpulseLabel;
|
||||
private final JLabel avgThrustLabel;
|
||||
private final JLabel maxThrustLabel;
|
||||
private final JLabel burnTimeLabel;
|
||||
private final JLabel launchMassLabel;
|
||||
private final JLabel emptyMassLabel;
|
||||
private final JLabel dataPointsLabel;
|
||||
|
||||
private final JTextArea comment;
|
||||
private final Font noCommentFont;
|
||||
private final Font withCommentFont;
|
||||
|
||||
private final JFreeChart chart;
|
||||
private final ChartPanel chartPanel;
|
||||
private final JLabel zoomIcon;
|
||||
|
||||
private final JComboBox delayBox;
|
||||
|
||||
private ThrustCurveMotor selectedMotor;
|
||||
private ThrustCurveMotorSet selectedMotorSet;
|
||||
private double selectedDelay;
|
||||
|
||||
|
||||
/**
|
||||
* Sole constructor.
|
||||
*
|
||||
* @param current the currently selected ThrustCurveMotor, or <code>null</code> for none.
|
||||
* @param delay the currently selected ejection charge delay.
|
||||
* @param diameter the diameter of the motor mount.
|
||||
*/
|
||||
public ThrustCurveMotorSelectionPanel(ThrustCurveMotor current, double delay, double diameter) {
|
||||
super(new MigLayout("fill", "[grow][]"));
|
||||
|
||||
this.diameter = diameter;
|
||||
|
||||
|
||||
// Construct the database (adding the current motor if not in the db already)
|
||||
List<ThrustCurveMotorSet> db;
|
||||
db = Application.getMotorSetDatabase().getMotorSets();
|
||||
|
||||
// If current motor is not found in db, add a new ThrustCurveMotorSet containing it
|
||||
if (current != null) {
|
||||
selectedMotor = current;
|
||||
for (ThrustCurveMotorSet motorSet : db) {
|
||||
if (motorSet.getMotors().contains(current)) {
|
||||
selectedMotorSet = motorSet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selectedMotorSet == null) {
|
||||
db = new ArrayList<ThrustCurveMotorSet>(db);
|
||||
ThrustCurveMotorSet extra = new ThrustCurveMotorSet();
|
||||
extra.addMotor(current);
|
||||
selectedMotorSet = extra;
|
||||
db.add(extra);
|
||||
Collections.sort(db);
|
||||
}
|
||||
}
|
||||
database = db;
|
||||
|
||||
|
||||
|
||||
//// GUI
|
||||
|
||||
JPanel panel;
|
||||
JLabel label;
|
||||
|
||||
panel = new JPanel(new MigLayout("fill"));
|
||||
this.add(panel, "grow");
|
||||
|
||||
|
||||
|
||||
// Selection label
|
||||
label = new StyledLabel("Select rocket motor:", Style.BOLD);
|
||||
panel.add(label, "spanx, wrap para");
|
||||
|
||||
// Diameter selection
|
||||
JComboBox filterComboBox = new JComboBox(SHOW_DESCRIPTIONS);
|
||||
filterComboBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JComboBox cb = (JComboBox) e.getSource();
|
||||
int sel = cb.getSelectedIndex();
|
||||
if ((sel < 0) || (sel > SHOW_MAX))
|
||||
sel = SHOW_ALL;
|
||||
switch (sel) {
|
||||
case SHOW_ALL:
|
||||
sorter.setRowFilter(new MotorRowFilterAll());
|
||||
break;
|
||||
|
||||
case SHOW_SMALLER:
|
||||
sorter.setRowFilter(new MotorRowFilterSmaller());
|
||||
break;
|
||||
|
||||
case SHOW_EXACT:
|
||||
sorter.setRowFilter(new MotorRowFilterExact());
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (false) : "Should not occur.";
|
||||
}
|
||||
Prefs.putChoise("MotorDiameterMatch", sel);
|
||||
scrollSelectionVisible();
|
||||
}
|
||||
});
|
||||
panel.add(filterComboBox, "spanx, growx, wrap para");
|
||||
|
||||
|
||||
|
||||
// Motor selection table
|
||||
model = new ThrustCurveMotorDatabaseModel(database);
|
||||
table = new JTable(model);
|
||||
|
||||
|
||||
// Set comparators and widths
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
sorter = new TableRowSorter<TableModel>(model);
|
||||
for (int i = 0; i < ThrustCurveMotorColumns.values().length; i++) {
|
||||
ThrustCurveMotorColumns column = ThrustCurveMotorColumns.values()[i];
|
||||
sorter.setComparator(i, column.getComparator());
|
||||
table.getColumnModel().getColumn(i).setPreferredWidth(column.getWidth());
|
||||
}
|
||||
table.setRowSorter(sorter);
|
||||
|
||||
// Set selection and double-click listeners
|
||||
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
int row = table.getSelectedRow();
|
||||
if (row >= 0) {
|
||||
row = table.convertRowIndexToModel(row);
|
||||
ThrustCurveMotorSet motorSet = model.getMotorSet(row);
|
||||
log.user("Selected table row " + row + ": " + motorSet);
|
||||
if (motorSet != selectedMotorSet) {
|
||||
select(selectMotor(motorSet));
|
||||
}
|
||||
} else {
|
||||
log.user("Selected table row " + row + ", nothing selected");
|
||||
}
|
||||
}
|
||||
});
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
|
||||
if (dialog != null) {
|
||||
dialog.close(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
JScrollPane scrollpane = new JScrollPane();
|
||||
scrollpane.setViewportView(table);
|
||||
panel.add(scrollpane, "grow, width :500:, height :300:, spanx, wrap para");
|
||||
|
||||
|
||||
|
||||
|
||||
// Motor mount diameter label
|
||||
label = new StyledLabel("Motor mount diameter: " +
|
||||
UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(diameter));
|
||||
panel.add(label, "gapright 30lp, spanx, split");
|
||||
|
||||
|
||||
|
||||
// Search field
|
||||
label = new StyledLabel("Search:");
|
||||
panel.add(label, "");
|
||||
|
||||
searchField = new JTextField();
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
private void update() {
|
||||
String text = searchField.getText().trim();
|
||||
String[] split = text.split("\\s+");
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
for (String s : split) {
|
||||
s = s.trim().toLowerCase();
|
||||
if (s.length() > 0) {
|
||||
list.add(s);
|
||||
}
|
||||
}
|
||||
searchTerms = list.toArray(new String[0]);
|
||||
sorter.sort();
|
||||
scrollSelectionVisible();
|
||||
}
|
||||
});
|
||||
panel.add(searchField, "growx, wrap");
|
||||
|
||||
|
||||
|
||||
// Vertical split
|
||||
this.add(panel, "grow");
|
||||
this.add(new JSeparator(JSeparator.VERTICAL), "growy, gap para para");
|
||||
panel = new JPanel(new MigLayout("fill"));
|
||||
|
||||
|
||||
|
||||
// Thrust curve selection
|
||||
curveSelectionLabel = new JLabel("Select thrust curve:");
|
||||
panel.add(curveSelectionLabel);
|
||||
|
||||
curveSelectionModel = new DefaultComboBoxModel();
|
||||
curveSelectionBox = new JComboBox(curveSelectionModel);
|
||||
curveSelectionBox.setRenderer(new CurveSelectionRenderer(curveSelectionBox.getRenderer()));
|
||||
curveSelectionBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Object value = curveSelectionBox.getSelectedItem();
|
||||
if (value != null) {
|
||||
select(((MotorHolder) value).getMotor());
|
||||
}
|
||||
}
|
||||
});
|
||||
panel.add(curveSelectionBox, "growx, wrap para");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Ejection charge delay
|
||||
panel.add(new JLabel("Ejection charge delay:"));
|
||||
|
||||
delayBox = new JComboBox();
|
||||
delayBox.setEditable(true);
|
||||
delayBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JComboBox cb = (JComboBox) e.getSource();
|
||||
String sel = (String) cb.getSelectedItem();
|
||||
if (sel.equalsIgnoreCase("None")) {
|
||||
selectedDelay = Motor.PLUGGED;
|
||||
} else {
|
||||
try {
|
||||
selectedDelay = Double.parseDouble(sel);
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
}
|
||||
setDelays(false);
|
||||
}
|
||||
});
|
||||
panel.add(delayBox, "growx, wrap rel");
|
||||
panel.add(new StyledLabel("(Number of seconds or \"None\")", -3), "skip, wrap para");
|
||||
setDelays(false);
|
||||
|
||||
|
||||
panel.add(new JSeparator(), "spanx, growx, wrap para");
|
||||
|
||||
|
||||
|
||||
// Thrust curve info
|
||||
panel.add(new JLabel("Total impulse:"));
|
||||
totalImpulseLabel = new JLabel();
|
||||
panel.add(totalImpulseLabel, "wrap");
|
||||
|
||||
panel.add(new JLabel("Avg. thrust:"));
|
||||
avgThrustLabel = new JLabel();
|
||||
panel.add(avgThrustLabel, "wrap");
|
||||
|
||||
panel.add(new JLabel("Max. thrust:"));
|
||||
maxThrustLabel = new JLabel();
|
||||
panel.add(maxThrustLabel, "wrap");
|
||||
|
||||
panel.add(new JLabel("Burn time:"));
|
||||
burnTimeLabel = new JLabel();
|
||||
panel.add(burnTimeLabel, "wrap");
|
||||
|
||||
panel.add(new JLabel("Launch mass:"));
|
||||
launchMassLabel = new JLabel();
|
||||
panel.add(launchMassLabel, "wrap");
|
||||
|
||||
panel.add(new JLabel("Empty mass:"));
|
||||
emptyMassLabel = new JLabel();
|
||||
panel.add(emptyMassLabel, "wrap");
|
||||
|
||||
panel.add(new JLabel("Data points:"));
|
||||
dataPointsLabel = new JLabel();
|
||||
panel.add(dataPointsLabel, "wrap para");
|
||||
|
||||
|
||||
comment = new JTextArea(5, 5);
|
||||
GUIUtil.changeFontSize(comment, -2);
|
||||
withCommentFont = comment.getFont();
|
||||
noCommentFont = withCommentFont.deriveFont(Font.ITALIC);
|
||||
comment.setLineWrap(true);
|
||||
comment.setWrapStyleWord(true);
|
||||
comment.setEditable(false);
|
||||
scrollpane = new JScrollPane(comment);
|
||||
panel.add(scrollpane, "spanx, growx, wrap para");
|
||||
|
||||
|
||||
|
||||
|
||||
// Thrust curve plot
|
||||
chart = ChartFactory.createXYLineChart(
|
||||
null, // title
|
||||
null, // xAxisLabel
|
||||
null, // yAxisLabel
|
||||
null, // dataset
|
||||
PlotOrientation.VERTICAL,
|
||||
false, // legend
|
||||
false, // tooltips
|
||||
false // urls
|
||||
);
|
||||
|
||||
|
||||
// Add the data and formatting to the plot
|
||||
XYPlot plot = chart.getXYPlot();
|
||||
|
||||
changeLabelFont(plot.getRangeAxis(), -2);
|
||||
changeLabelFont(plot.getDomainAxis(), -2);
|
||||
|
||||
chart.setTitle(new TextTitle("Thrust curve:", this.getFont()));
|
||||
chart.setBackgroundPaint(this.getBackground());
|
||||
plot.setBackgroundPaint(Color.WHITE);
|
||||
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
|
||||
plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
|
||||
|
||||
chartPanel = new ChartPanel(chart,
|
||||
false, // properties
|
||||
false, // save
|
||||
false, // print
|
||||
false, // zoom
|
||||
false); // tooltips
|
||||
chartPanel.setMouseZoomable(false);
|
||||
chartPanel.setPopupMenu(null);
|
||||
chartPanel.setMouseWheelEnabled(false);
|
||||
chartPanel.setRangeZoomable(false);
|
||||
chartPanel.setDomainZoomable(false);
|
||||
|
||||
chartPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
chartPanel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (selectedMotor == null || selectedMotorSet == null)
|
||||
return;
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
// Open plot dialog
|
||||
ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(selectedMotorSet, selectedMotor,
|
||||
SwingUtilities.getWindowAncestor(ThrustCurveMotorSelectionPanel.this));
|
||||
plotDialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JLayeredPane layer = new CustomLayeredPane();
|
||||
|
||||
layer.setBorder(BorderFactory.createLineBorder(Color.BLUE));
|
||||
|
||||
layer.add(chartPanel, (Integer) 0);
|
||||
|
||||
zoomIcon = new JLabel(Icons.ZOOM_IN);
|
||||
zoomIcon.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
layer.add(zoomIcon, (Integer) 1);
|
||||
|
||||
|
||||
panel.add(layer, "width 300:300:, height 180:180:, grow, spanx");
|
||||
|
||||
|
||||
|
||||
this.add(panel, "grow");
|
||||
|
||||
|
||||
|
||||
// Sets the filter:
|
||||
int showMode = Prefs.getChoise("MotorDiameterMatch", SHOW_MAX, SHOW_EXACT);
|
||||
filterComboBox.setSelectedIndex(showMode);
|
||||
|
||||
|
||||
// Update the panel data
|
||||
updateData();
|
||||
selectedDelay = delay;
|
||||
setDelays(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Motor getSelectedMotor() {
|
||||
return selectedMotor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getSelectedDelay() {
|
||||
return selectedDelay;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JComponent getDefaultFocus() {
|
||||
return searchField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectedMotor(Motor motorSelection) {
|
||||
if (!(motorSelection instanceof ThrustCurveMotor)) {
|
||||
log.error("Received argument that was not ThrustCurveMotor: " + motorSelection);
|
||||
return;
|
||||
}
|
||||
|
||||
ThrustCurveMotor motor = (ThrustCurveMotor) motorSelection;
|
||||
ThrustCurveMotorSet set = findMotorSet(motor);
|
||||
if (set == null) {
|
||||
log.error("Could not find set for motor:" + motorSelection);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store selected motor in preferences node, set all others to false
|
||||
Preferences prefs = Prefs.getNode(Prefs.PREFERRED_THRUST_CURVE_MOTOR_NODE);
|
||||
for (ThrustCurveMotor m : set.getMotors()) {
|
||||
String digest = MotorDigest.digestMotor(m);
|
||||
prefs.putBoolean(digest, m == motor);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCloseableDialog(CloseableDialog dialog) {
|
||||
this.dialog = dialog;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void changeLabelFont(ValueAxis axis, float size) {
|
||||
Font font = axis.getTickLabelFont();
|
||||
font = font.deriveFont(font.getSize2D() + size);
|
||||
axis.setTickLabelFont(font);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when a different motor is selected from within the panel.
|
||||
*/
|
||||
private void select(ThrustCurveMotor motor) {
|
||||
if (selectedMotor == motor)
|
||||
return;
|
||||
|
||||
ThrustCurveMotorSet set = findMotorSet(motor);
|
||||
if (set == null) {
|
||||
throw new BugException("Could not find motor from database, motor=" + motor);
|
||||
}
|
||||
|
||||
boolean updateDelays = (selectedMotorSet != set);
|
||||
|
||||
selectedMotor = motor;
|
||||
selectedMotorSet = set;
|
||||
updateData();
|
||||
if (updateDelays) {
|
||||
setDelays(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateData() {
|
||||
|
||||
if (selectedMotorSet == null) {
|
||||
// No motor selected
|
||||
curveSelectionModel.removeAllElements();
|
||||
curveSelectionBox.setEnabled(false);
|
||||
curveSelectionLabel.setEnabled(false);
|
||||
totalImpulseLabel.setText("");
|
||||
avgThrustLabel.setText("");
|
||||
maxThrustLabel.setText("");
|
||||
burnTimeLabel.setText("");
|
||||
launchMassLabel.setText("");
|
||||
emptyMassLabel.setText("");
|
||||
dataPointsLabel.setText("");
|
||||
setComment("");
|
||||
chart.getXYPlot().setDataset(new XYSeriesCollection());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
List<ThrustCurveMotor> motors = selectedMotorSet.getMotors();
|
||||
final int index = motors.indexOf(selectedMotor);
|
||||
|
||||
curveSelectionModel.removeAllElements();
|
||||
for (int i = 0; i < motors.size(); i++) {
|
||||
curveSelectionModel.addElement(new MotorHolder(motors.get(i), i));
|
||||
}
|
||||
curveSelectionBox.setSelectedIndex(index);
|
||||
|
||||
if (motors.size() > 1) {
|
||||
curveSelectionBox.setEnabled(true);
|
||||
curveSelectionLabel.setEnabled(true);
|
||||
} else {
|
||||
curveSelectionBox.setEnabled(false);
|
||||
curveSelectionLabel.setEnabled(false);
|
||||
}
|
||||
|
||||
totalImpulseLabel.setText(UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(
|
||||
selectedMotor.getTotalImpulseEstimate()));
|
||||
avgThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(
|
||||
selectedMotor.getAverageThrustEstimate()));
|
||||
maxThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(
|
||||
selectedMotor.getMaxThrustEstimate()));
|
||||
burnTimeLabel.setText(UnitGroup.UNITS_SHORT_TIME.getDefaultUnit().toStringUnit(
|
||||
selectedMotor.getBurnTimeEstimate()));
|
||||
launchMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
|
||||
selectedMotor.getLaunchCG().weight));
|
||||
emptyMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
|
||||
selectedMotor.getEmptyCG().weight));
|
||||
dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1));
|
||||
|
||||
setComment(selectedMotor.getDescription());
|
||||
|
||||
|
||||
// Update the plot
|
||||
XYPlot plot = chart.getXYPlot();
|
||||
|
||||
XYSeriesCollection dataset = new XYSeriesCollection();
|
||||
for (int i = 0; i < motors.size(); i++) {
|
||||
ThrustCurveMotor m = motors.get(i);
|
||||
|
||||
XYSeries series = new XYSeries("Thrust");
|
||||
double[] time = m.getTimePoints();
|
||||
double[] thrust = m.getThrustPoints();
|
||||
|
||||
for (int j = 0; j < time.length; j++) {
|
||||
series.add(time[j], thrust[j]);
|
||||
}
|
||||
|
||||
dataset.addSeries(series);
|
||||
|
||||
boolean selected = (i == index);
|
||||
plot.getRenderer().setSeriesStroke(i, new BasicStroke(selected ? 3 : 1));
|
||||
plot.getRenderer().setSeriesPaint(i, getColor(i));
|
||||
}
|
||||
|
||||
plot.setDataset(dataset);
|
||||
}
|
||||
|
||||
|
||||
private void setComment(String s) {
|
||||
s = s.trim();
|
||||
if (s.length() == 0) {
|
||||
comment.setText("No description available.");
|
||||
comment.setFont(noCommentFont);
|
||||
comment.setForeground(NO_COMMENT_COLOR);
|
||||
} else {
|
||||
comment.setText(s);
|
||||
comment.setFont(withCommentFont);
|
||||
comment.setForeground(WITH_COMMENT_COLOR);
|
||||
}
|
||||
comment.setCaretPosition(0);
|
||||
}
|
||||
|
||||
private void scrollSelectionVisible() {
|
||||
if (selectedMotorSet != null) {
|
||||
int index = table.convertRowIndexToView(model.getIndex(selectedMotorSet));
|
||||
System.out.println("index=" + index);
|
||||
table.getSelectionModel().setSelectionInterval(index, index);
|
||||
Rectangle rect = table.getCellRect(index, 0, true);
|
||||
rect = new Rectangle(rect.x, rect.y - 100, rect.width, rect.height + 200);
|
||||
table.scrollRectToVisible(rect);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Color getColor(int index) {
|
||||
return (Color) CURVE_COLORS[index % CURVE_COLORS.length];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the ThrustCurveMotorSet that contains a motor.
|
||||
*
|
||||
* @param motor the motor to look for.
|
||||
* @return the ThrustCurveMotorSet, or null if not found.
|
||||
*/
|
||||
private ThrustCurveMotorSet findMotorSet(ThrustCurveMotor motor) {
|
||||
for (ThrustCurveMotorSet set : database) {
|
||||
if (set.getMotors().contains(motor)) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Select the default motor from this ThrustCurveMotorSet. This uses primarily motors
|
||||
* that the user has previously used, and secondarily a heuristic method of selecting which
|
||||
* thrust curve seems to be better or more reliable.
|
||||
*
|
||||
* @param set the motor set
|
||||
* @return the default motor in this set
|
||||
*/
|
||||
private ThrustCurveMotor selectMotor(ThrustCurveMotorSet set) {
|
||||
if (set.getMotorCount() == 0) {
|
||||
throw new BugException("Attempting to select motor from empty ThrustCurveMotorSet: " + set);
|
||||
}
|
||||
if (set.getMotorCount() == 1) {
|
||||
return set.getMotors().get(0);
|
||||
}
|
||||
|
||||
// Find which motor has been used the most recently
|
||||
Preferences prefs = Prefs.getNode(Prefs.PREFERRED_THRUST_CURVE_MOTOR_NODE);
|
||||
for (ThrustCurveMotor m : set.getMotors()) {
|
||||
String digest = MotorDigest.digestMotor(m);
|
||||
if (prefs.getBoolean(digest, false)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
// No motor has been used, use heuristics to select motor
|
||||
// TODO: CRITICAL: Heuristics
|
||||
return set.getMotors().get(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the values in the delay combo box. If <code>reset</code> is <code>true</code>
|
||||
* then sets the selected value as the value closest to selectedDelay, otherwise
|
||||
* leaves selection alone.
|
||||
*/
|
||||
private void setDelays(boolean reset) {
|
||||
if (selectedMotor == null) {
|
||||
|
||||
delayBox.setModel(new DefaultComboBoxModel(new String[] { "None" }));
|
||||
delayBox.setSelectedIndex(0);
|
||||
|
||||
} else {
|
||||
|
||||
List<Double> delays = selectedMotorSet.getDelays();
|
||||
String[] delayStrings = new String[delays.size()];
|
||||
double currentDelay = selectedDelay; // Store current setting locally
|
||||
|
||||
for (int i = 0; i < delays.size(); i++) {
|
||||
delayStrings[i] = ThrustCurveMotor.getDelayString(delays.get(i), "None");
|
||||
}
|
||||
delayBox.setModel(new DefaultComboBoxModel(delayStrings));
|
||||
|
||||
if (reset) {
|
||||
|
||||
// Find and set the closest value
|
||||
double closest = Double.NaN;
|
||||
for (int i = 0; i < delays.size(); i++) {
|
||||
// if-condition to always become true for NaN
|
||||
if (!(Math.abs(delays.get(i) - currentDelay) > Math.abs(closest - currentDelay))) {
|
||||
closest = delays.get(i);
|
||||
}
|
||||
}
|
||||
if (!Double.isNaN(closest)) {
|
||||
selectedDelay = closest;
|
||||
delayBox.setSelectedItem(ThrustCurveMotor.getDelayString(closest, "None"));
|
||||
} else {
|
||||
delayBox.setSelectedItem("None");
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
selectedDelay = currentDelay;
|
||||
delayBox.setSelectedItem(ThrustCurveMotor.getDelayString(currentDelay, "None"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////
|
||||
|
||||
|
||||
private class CurveSelectionRenderer implements ListCellRenderer {
|
||||
|
||||
private final ListCellRenderer renderer;
|
||||
|
||||
public CurveSelectionRenderer(ListCellRenderer renderer) {
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList list, Object value, int index,
|
||||
boolean isSelected, boolean cellHasFocus) {
|
||||
|
||||
Component c = renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
if (value instanceof MotorHolder) {
|
||||
MotorHolder m = (MotorHolder) value;
|
||||
c.setForeground(getColor(m.getIndex()));
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//////// Row filters
|
||||
|
||||
/**
|
||||
* Abstract adapter class.
|
||||
*/
|
||||
private abstract class MotorRowFilter extends RowFilter<TableModel, Integer> {
|
||||
@Override
|
||||
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
|
||||
int index = entry.getIdentifier();
|
||||
ThrustCurveMotorSet m = model.getMotorSet(index);
|
||||
return filterByDiameter(m) && filterByString(m);
|
||||
}
|
||||
|
||||
public abstract boolean filterByDiameter(ThrustCurveMotorSet m);
|
||||
|
||||
|
||||
public boolean filterByString(ThrustCurveMotorSet m) {
|
||||
main: for (String s : searchTerms) {
|
||||
for (ThrustCurveMotorColumns col : ThrustCurveMotorColumns.values()) {
|
||||
String str = col.getValue(m).toString().toLowerCase();
|
||||
if (str.indexOf(s) >= 0)
|
||||
continue main;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all motors.
|
||||
*/
|
||||
private class MotorRowFilterAll extends MotorRowFilter {
|
||||
@Override
|
||||
public boolean filterByDiameter(ThrustCurveMotorSet m) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show motors smaller than the mount.
|
||||
*/
|
||||
private class MotorRowFilterSmaller extends MotorRowFilter {
|
||||
@Override
|
||||
public boolean filterByDiameter(ThrustCurveMotorSet m) {
|
||||
return (m.getDiameter() <= diameter + 0.0004);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show motors that fit the mount.
|
||||
*/
|
||||
private class MotorRowFilterExact extends MotorRowFilter {
|
||||
@Override
|
||||
public boolean filterByDiameter(ThrustCurveMotorSet m) {
|
||||
return ((m.getDiameter() <= diameter + 0.0004) && (m.getDiameter() >= diameter - 0.0015));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Custom layered pane that sets the bounds of the components on every layout.
|
||||
*/
|
||||
public class CustomLayeredPane extends JLayeredPane {
|
||||
@Override
|
||||
public void doLayout() {
|
||||
synchronized (getTreeLock()) {
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
chartPanel.setBounds(0, 0, w, h);
|
||||
zoomIcon.setBounds(w - ZOOM_ICON_POSITION_NEGATIVE_X, ZOOM_ICON_POSITION_POSITIVE_Y, 50, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
||||
package net.sf.openrocket.gui.plot;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
@ -62,6 +63,8 @@ import org.jfree.ui.TextAnchor;
|
||||
|
||||
public class PlotDialog extends JDialog {
|
||||
|
||||
private static final float PLOT_STROKE_WIDTH = 1.5f;
|
||||
|
||||
private static final Color DEFAULT_EVENT_COLOR = new Color(0, 0, 0);
|
||||
private static final Map<FlightEvent.Type, Color> EVENT_COLORS =
|
||||
new HashMap<FlightEvent.Type, Color>();
|
||||
@ -221,6 +224,9 @@ public class PlotDialog extends JDialog {
|
||||
ModifiedXYItemRenderer r = new ModifiedXYItemRenderer();
|
||||
r.setBaseShapesVisible(initialShowPoints);
|
||||
r.setBaseShapesFilled(true);
|
||||
for (int j = 0; j < data[i].getSeriesCount(); j++) {
|
||||
r.setSeriesStroke(j, new BasicStroke(PLOT_STROKE_WIDTH));
|
||||
}
|
||||
renderers.add(r);
|
||||
plot.setRenderer(axisno, r);
|
||||
plot.mapDatasetToRangeAxis(axisno, axisno);
|
||||
|
@ -4,7 +4,9 @@ import java.text.Collator;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.Inertia;
|
||||
@ -12,6 +14,7 @@ import net.sf.openrocket.util.MathUtil;
|
||||
|
||||
|
||||
public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor> {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
public static final double MAX_THRUST = 10e6;
|
||||
|
||||
@ -171,7 +174,12 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* NOTE: In most cases you want to examine the motor type of the ThrustCurveMotorSet,
|
||||
* not the ThrustCurveMotor itself.
|
||||
*/
|
||||
@Override
|
||||
public Type getMotorType() {
|
||||
return type;
|
||||
@ -391,6 +399,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor> {
|
||||
private int modID = 0;
|
||||
|
||||
public ThrustCurveMotorInstance() {
|
||||
log.debug("ThrustCurveMotor: Creating motor instance of " + ThrustCurveMotor.this);
|
||||
position = 0;
|
||||
prevTime = 0;
|
||||
instThrust = 0;
|
||||
@ -434,22 +443,18 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor> {
|
||||
@Override
|
||||
public void step(double nextTime, double acceleration, AtmosphericConditions cond) {
|
||||
|
||||
System.out.println("MOTOR: Stepping instance " + this + " to time " + nextTime);
|
||||
|
||||
if (!(nextTime >= prevTime)) {
|
||||
// Also catches NaN
|
||||
throw new IllegalArgumentException("Stepping backwards in time, current=" +
|
||||
prevTime + " new=" + nextTime);
|
||||
}
|
||||
if (MathUtil.equals(prevTime, nextTime)) {
|
||||
System.out.println("Same time as earlier");
|
||||
return;
|
||||
}
|
||||
|
||||
modID++;
|
||||
|
||||
if (position >= time.length - 1) {
|
||||
System.out.println("Thrust has ended");
|
||||
// Thrust has ended
|
||||
prevTime = nextTime;
|
||||
stepThrust = 0;
|
||||
@ -501,19 +506,10 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor> {
|
||||
if (position < time.length - 1) {
|
||||
nextCG = MathUtil.map(nextTime, time[position], time[position + 1],
|
||||
cg[position], cg[position + 1]);
|
||||
|
||||
System.out.println("nextTime=" + nextTime +
|
||||
" time[position]=" + time[position] +
|
||||
" time[position+1]=" + time[position + 1] +
|
||||
" mass[position]=" + cg[position].weight * 1000 +
|
||||
" mass[position+1]=" + cg[position + 1].weight * 1000 +
|
||||
" result=" + nextCG.weight * 1000 +
|
||||
" position=" + position);
|
||||
} else {
|
||||
nextCG = cg[cg.length - 1];
|
||||
}
|
||||
stepCG = instCG.add(nextCG).multiply(0.5);
|
||||
System.out.println("instMass=" + instCG.weight + " nextMass=" + nextCG.weight + " stepMass=" + stepCG.weight);
|
||||
instCG = nextCG;
|
||||
|
||||
// Update time
|
||||
|
@ -412,14 +412,12 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
if (status.getSimulationTime() < status.getStartWarningTime())
|
||||
warnings = null;
|
||||
|
||||
System.out.println("flightConditions=" + store.flightConditions);
|
||||
|
||||
|
||||
// Calculate aerodynamic forces
|
||||
store.forces = status.getSimulationConditions().getAerodynamicCalculator()
|
||||
.getAerodynamicForces(status.getConfiguration(), store.flightConditions, warnings);
|
||||
|
||||
System.out.println("CP=" + store.forces.getCP());
|
||||
|
||||
|
||||
// Add very small randomization to yaw & pitch moments to prevent over-perfect flight
|
||||
// TODO: HIGH: This should rather be performed as a listener
|
||||
store.forces.setCm(store.forces.getCm() + (PITCH_YAW_RANDOM * 2 * (random.nextDouble() - 0.5)));
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.sf.openrocket.startup;
|
||||
|
||||
import net.sf.openrocket.database.MotorSetDatabase;
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.logging.LogLevel;
|
||||
import net.sf.openrocket.logging.LogLevelBufferLogger;
|
||||
@ -16,7 +16,7 @@ public final class Application {
|
||||
private static LogHelper logger;
|
||||
private static LogLevelBufferLogger logBuffer;
|
||||
|
||||
private static MotorSetDatabase motorSetDatabase;
|
||||
private static ThrustCurveMotorSetDatabase motorSetDatabase;
|
||||
|
||||
// Initialize the logger to something sane for testing without executing Startup
|
||||
static {
|
||||
@ -68,14 +68,14 @@ public final class Application {
|
||||
/**
|
||||
* Return the database of all thrust curves loaded into the system.
|
||||
*/
|
||||
public static MotorSetDatabase getMotorSetDatabase() {
|
||||
public static ThrustCurveMotorSetDatabase getMotorSetDatabase() {
|
||||
return motorSetDatabase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database of thrust curves loaded into the system.
|
||||
*/
|
||||
public static void setMotorSetDatabase(MotorSetDatabase motorSetDatabase) {
|
||||
public static void setMotorSetDatabase(ThrustCurveMotorSetDatabase motorSetDatabase) {
|
||||
Application.motorSetDatabase = motorSetDatabase;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
@ -17,7 +18,8 @@ import javax.swing.ToolTipManager;
|
||||
import net.sf.openrocket.communication.UpdateInfo;
|
||||
import net.sf.openrocket.communication.UpdateInfoRetriever;
|
||||
import net.sf.openrocket.database.Databases;
|
||||
import net.sf.openrocket.database.MotorSetDatabase;
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSet;
|
||||
import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
|
||||
import net.sf.openrocket.file.DirectoryIterator;
|
||||
import net.sf.openrocket.file.GeneralMotorLoader;
|
||||
import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
|
||||
@ -55,9 +57,11 @@ public class Startup {
|
||||
|
||||
private static final String THRUSTCURVE_DIRECTORY = "datafiles/thrustcurves/";
|
||||
|
||||
|
||||
/** Block motor loading for this many milliseconds */
|
||||
private static AtomicInteger blockLoading = new AtomicInteger(Integer.MAX_VALUE);
|
||||
|
||||
|
||||
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
|
||||
// Initialize logging first so we can use it
|
||||
@ -67,10 +71,12 @@ public class Startup {
|
||||
checkHead();
|
||||
|
||||
// Check that we're running a good version of a JRE
|
||||
log.info("Checking JRE compatibility");
|
||||
VersionHelper.checkVersion();
|
||||
VersionHelper.checkOpenJDK();
|
||||
|
||||
// Run the actual startup method in the EDT since it can use progress dialogs etc.
|
||||
log.info("Running main");
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -78,6 +84,10 @@ public class Startup {
|
||||
}
|
||||
});
|
||||
|
||||
log.info("Startup complete");
|
||||
|
||||
// Block motor loading for 2 seconds to allow window painting
|
||||
blockLoading.set(2000);
|
||||
}
|
||||
|
||||
|
||||
@ -86,21 +96,26 @@ public class Startup {
|
||||
private static void runMain(String[] args) {
|
||||
|
||||
// Initialize the splash screen with version info
|
||||
log.info("Initializing the splash screen");
|
||||
Splash.init();
|
||||
|
||||
// Setup the uncaught exception handler
|
||||
log.info("Registering exception handler");
|
||||
ExceptionHandler.registerExceptionHandler();
|
||||
|
||||
// Start update info fetching
|
||||
final UpdateInfoRetriever updateInfo;
|
||||
if (Prefs.getCheckUpdates()) {
|
||||
log.info("Starting update check");
|
||||
updateInfo = new UpdateInfoRetriever();
|
||||
updateInfo.start();
|
||||
} else {
|
||||
log.info("Update check disabled");
|
||||
updateInfo = null;
|
||||
}
|
||||
|
||||
// Set the best available look-and-feel
|
||||
log.info("Setting best LAF");
|
||||
GUIUtil.setBestLAF();
|
||||
|
||||
// Set tooltip delay time. Tooltips are used in MotorChooserDialog extensively.
|
||||
@ -111,15 +126,18 @@ public class Startup {
|
||||
|
||||
// Load motors etc.
|
||||
// TODO: HIGH: Use new motor loading
|
||||
// loadMotor();
|
||||
log.info("Loading databases");
|
||||
loadMotor();
|
||||
Databases.fakeMethod();
|
||||
|
||||
// Starting action (load files or open new document)
|
||||
log.info("Opening main application window");
|
||||
if (!handleCommandLine(args)) {
|
||||
BasicFrame.newAction();
|
||||
}
|
||||
|
||||
// Check whether update info has been fetched or whether it needs more time
|
||||
log.info("Checking update status");
|
||||
checkUpdateStatus(updateInfo);
|
||||
}
|
||||
|
||||
@ -129,23 +147,48 @@ public class Startup {
|
||||
|
||||
log.info("Starting motor loading from " + THRUSTCURVE_DIRECTORY +
|
||||
" in background thread.");
|
||||
MotorSetDatabase db = new MotorSetDatabase(true) {
|
||||
ThrustCurveMotorSetDatabase db = new ThrustCurveMotorSetDatabase(true) {
|
||||
|
||||
@Override
|
||||
protected void loadMotors() {
|
||||
|
||||
log.info("Blocking motor loading while starting up");
|
||||
|
||||
// Block for 100ms a time until timeout or database in use
|
||||
while (!inUse && blockLoading.addAndGet(-100) > 0) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Blocking ended, inUse=" + inUse + " slowLoadingCount=" + blockLoading.get());
|
||||
|
||||
log.info("Started to load motors from " + THRUSTCURVE_DIRECTORY);
|
||||
long t0 = System.currentTimeMillis();
|
||||
|
||||
int fileCount = 0;
|
||||
int thrustCurveCount = 0;
|
||||
int distinctMotorCount = 0;
|
||||
int distinctThrustCurveCount = 0;
|
||||
|
||||
GeneralMotorLoader loader = new GeneralMotorLoader();
|
||||
DirectoryIterator iterator =
|
||||
DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY,
|
||||
new SimpleFileFilter("", false, "eng", "rkt"));
|
||||
DirectoryIterator iterator = DirectoryIterator.findDirectory(THRUSTCURVE_DIRECTORY,
|
||||
new SimpleFileFilter("", false, "eng", "rse"));
|
||||
if (iterator == null) {
|
||||
throw new IllegalStateException("No thrust curves found, " +
|
||||
"distribution built wrong");
|
||||
throw new IllegalStateException("No thrust curves found, distribution built wrong");
|
||||
}
|
||||
while (iterator.hasNext()) {
|
||||
final Pair<String, InputStream> input = iterator.next();
|
||||
log.debug("Loading motors from file " + input.getU());
|
||||
fileCount++;
|
||||
try {
|
||||
List<Motor> motors = loader.load(input.getV(), input.getU());
|
||||
if (motors.size() == 0) {
|
||||
log.warn("No motors found in file " + input.getU());
|
||||
}
|
||||
for (Motor m : motors) {
|
||||
thrustCurveCount++;
|
||||
this.addMotor((ThrustCurveMotor) m);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
@ -157,7 +200,19 @@ public class Startup {
|
||||
log.error("IOException when closing InputStream", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
long t1 = System.currentTimeMillis();
|
||||
|
||||
// Count statistics
|
||||
distinctMotorCount = motorSets.size();
|
||||
for (ThrustCurveMotorSet set : motorSets) {
|
||||
distinctThrustCurveCount += set.getMotorCount();
|
||||
}
|
||||
log.info("Motor loading done, took " + (t1 - t0) + " ms to load "
|
||||
+ fileCount + " files containing " + thrustCurveCount + " thrust curves which contained "
|
||||
+ distinctMotorCount + " distinct motors with " + distinctThrustCurveCount + " thrust curves.");
|
||||
}
|
||||
|
||||
};
|
||||
@ -240,6 +295,8 @@ public class Startup {
|
||||
*/
|
||||
private static void checkHead() {
|
||||
|
||||
log.info("Checking for graphics head");
|
||||
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
log.error("Application is headless.");
|
||||
System.err.println();
|
||||
|
@ -4,73 +4,74 @@ import net.sf.openrocket.logging.LogHelper;
|
||||
|
||||
public class VersionHelper {
|
||||
|
||||
private static final LogHelper logger = Application.getLogger();
|
||||
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
private static final int REQUIRED_MAJOR_VERSION = 1;
|
||||
private static final int REQUIRED_MINOR_VERSION = 6;
|
||||
|
||||
// OpenJDK 1.6.0_0-b16 is known to work, 1.6.0_0-b12 does not
|
||||
private static final String BAD_OPENJDK_VERSION = "^1.6.0_0-b([0-9]|1[1-5])$";
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check that the JRE version is high enough.
|
||||
*/
|
||||
static void checkVersion() {
|
||||
|
||||
String[] version = System.getProperty("java.specification.version", "").split("\\.");
|
||||
|
||||
String jreName = System.getProperty("java.vm.name", "(unknown)");
|
||||
String jreVersion = System.getProperty("java.runtime.version", "(unknown)");
|
||||
String jreVendor = System.getProperty("java.vendor", "(unknown)");
|
||||
|
||||
logger.info("Running JRE " + jreName + " version " + jreVersion + " by " + jreVendor);
|
||||
log.info("Running JRE " + jreName + " version " + jreVersion + " by " + jreVendor);
|
||||
|
||||
int major, minor;
|
||||
|
||||
|
||||
try {
|
||||
major = Integer.parseInt(version[0]);
|
||||
minor = Integer.parseInt(version[1]);
|
||||
|
||||
if (major < REQUIRED_MAJOR_VERSION ||
|
||||
if (major < REQUIRED_MAJOR_VERSION ||
|
||||
(major == REQUIRED_MAJOR_VERSION && minor < REQUIRED_MINOR_VERSION)) {
|
||||
Startup.error(new String[] {"Java SE version 6 is required to run OpenRocket.",
|
||||
Startup.error(new String[] { "Java SE version 6 is required to run OpenRocket.",
|
||||
"You are currently running " + jreName + " version " +
|
||||
jreVersion + " by " + jreVendor});
|
||||
jreVersion + " by " + jreVendor });
|
||||
}
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
Startup.confirm(new String[] {"The Java version in use could not be detected.",
|
||||
Startup.confirm(new String[] { "The Java version in use could not be detected.",
|
||||
"OpenRocket requires at least Java SE 6.",
|
||||
"Continue anyway?"});
|
||||
"Continue anyway?" });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether OpenJDK is being used, and if it is warn the user about
|
||||
* problems and confirm whether to continue.
|
||||
*/
|
||||
static void checkOpenJDK() {
|
||||
|
||||
if (System.getProperty("java.runtime.name", "").toLowerCase().indexOf("icedtea")>=0 ||
|
||||
System.getProperty("java.vm.name", "").toLowerCase().indexOf("openjdk")>=0) {
|
||||
|
||||
if (System.getProperty("java.runtime.name", "").toLowerCase().indexOf("icedtea") >= 0 ||
|
||||
System.getProperty("java.vm.name", "").toLowerCase().indexOf("openjdk") >= 0) {
|
||||
|
||||
String jreName = System.getProperty("java.vm.name", "(unknown)");
|
||||
String jreVersion = System.getProperty("java.runtime.version", "(unknown)");
|
||||
String jreVendor = System.getProperty("java.vendor", "(unknown)");
|
||||
|
||||
if (jreVersion.matches(BAD_OPENJDK_VERSION)) {
|
||||
|
||||
Startup.confirm(new String[] {"Old versions of OpenJDK are known to have problems " +
|
||||
if (jreVersion.matches(BAD_OPENJDK_VERSION)) {
|
||||
|
||||
Startup.confirm(new String[] { "Old versions of OpenJDK are known to have problems " +
|
||||
"running OpenRocket.",
|
||||
" ",
|
||||
"You are currently running " + jreName + " version " +
|
||||
jreVersion + " by " + jreVendor,
|
||||
"Do you want to continue?"});
|
||||
jreVersion + " by " + jreVendor,
|
||||
"Do you want to continue?" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -6,20 +6,17 @@ package net.sf.openrocket.util;
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class BugException extends FatalException {
|
||||
|
||||
public BugException() {
|
||||
}
|
||||
|
||||
|
||||
public BugException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public BugException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
public BugException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.sf.openrocket.util;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Font;
|
||||
import java.awt.Image;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.Point;
|
||||
@ -96,8 +97,9 @@ public class GUIUtil {
|
||||
|
||||
/**
|
||||
* Set suitable options for a single-use disposable dialog. This includes
|
||||
* setting ESC to close the dialog and adding the appropriate window icons.
|
||||
* If defaultButton is provided, it is set to the default button action.
|
||||
* setting ESC to close the dialog, adding the appropriate window icons and
|
||||
* setting the location based on the platform. If defaultButton is provided,
|
||||
* it is set to the default button action.
|
||||
* <p>
|
||||
* The default button must be already attached to the dialog.
|
||||
*
|
||||
@ -108,6 +110,7 @@ public class GUIUtil {
|
||||
installEscapeCloseOperation(dialog);
|
||||
setWindowIcons(dialog);
|
||||
addModelNullingListener(dialog);
|
||||
dialog.setLocationByPlatform(true);
|
||||
if (defaultButton != null) {
|
||||
setDefaultButton(defaultButton);
|
||||
}
|
||||
@ -238,6 +241,19 @@ public class GUIUtil {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes the size of the font of the specified component by the given amount.
|
||||
*
|
||||
* @param component the component for which to change the font
|
||||
* @param size the change in the font size
|
||||
*/
|
||||
public static void changeFontSize(JComponent component, float size) {
|
||||
Font font = component.getFont();
|
||||
font = font.deriveFont(font.getSize2D() + size);
|
||||
component.setFont(font);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Traverses recursively the component tree, and sets all applicable component
|
||||
* models to null, so as to remove the listener connections. After calling this
|
||||
|
@ -10,10 +10,17 @@ import javax.swing.ImageIcon;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.gui.main.ExceptionHandler;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
|
||||
public class Icons {
|
||||
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
static {
|
||||
log.debug("Starting to load icons");
|
||||
}
|
||||
|
||||
/**
|
||||
* Icons used for showing the status of a simulation (up to date, out of date, etc).
|
||||
*/
|
||||
@ -34,7 +41,7 @@ public class Icons {
|
||||
SIMULATION_LISTENER_OK = SIMULATION_STATUS_ICON_MAP.get(Simulation.Status.UPTODATE);
|
||||
SIMULATION_LISTENER_ERROR = SIMULATION_STATUS_ICON_MAP.get(Simulation.Status.OUTDATED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final Icon FILE_NEW = loadImageIcon("pix/icons/document-new.png", "New document");
|
||||
public static final Icon FILE_OPEN = loadImageIcon("pix/icons/document-open.png", "Open document");
|
||||
@ -50,15 +57,17 @@ public class Icons {
|
||||
public static final Icon EDIT_COPY = loadImageIcon("pix/icons/edit-copy.png", "Copy");
|
||||
public static final Icon EDIT_PASTE = loadImageIcon("pix/icons/edit-paste.png", "Paste");
|
||||
public static final Icon EDIT_DELETE = loadImageIcon("pix/icons/edit-delete.png", "Delete");
|
||||
|
||||
|
||||
public static final Icon ZOOM_IN = loadImageIcon("pix/icons/zoom-in.png", "Zoom in");
|
||||
public static final Icon ZOOM_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out");
|
||||
|
||||
|
||||
public static final Icon PREFERENCES = loadImageIcon("pix/icons/preferences.png", "Preferences");
|
||||
|
||||
|
||||
public static final Icon DELETE = loadImageIcon("pix/icons/delete.png", "Delete");
|
||||
|
||||
|
||||
static {
|
||||
log.debug("Icons loaded");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an ImageIcon from the specified file. The file is obtained as a system
|
||||
|
@ -42,21 +42,21 @@ public class Prefs {
|
||||
/**
|
||||
* Whether to use the debug-node instead of the normal node.
|
||||
*/
|
||||
public static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG;
|
||||
static {
|
||||
DEBUG = (System.getProperty("openrocket.debug.prefs") != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to clear all preferences at application startup. This has an effect only
|
||||
* if DEBUG is true.
|
||||
*/
|
||||
public static final boolean CLEARPREFS = true;
|
||||
private static final boolean CLEARPREFS = true;
|
||||
|
||||
/**
|
||||
* The node name to use in the Java preferences storage.
|
||||
*/
|
||||
public static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
|
||||
|
||||
|
||||
public static final String DEFAULT_BUILD_SOURCE = "default";
|
||||
private static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
|
||||
|
||||
|
||||
/*
|
||||
@ -128,6 +128,11 @@ public class Prefs {
|
||||
private static final String CHECK_UPDATES = "CheckUpdates";
|
||||
public static final String LAST_UPDATE = "LastUpdateVersion";
|
||||
|
||||
|
||||
// Node names
|
||||
public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
|
||||
|
||||
|
||||
/**
|
||||
* Node to this application's preferences.
|
||||
* @deprecated Use the static methods instead.
|
||||
@ -137,6 +142,7 @@ public class Prefs {
|
||||
private static final Preferences PREFNODE;
|
||||
|
||||
|
||||
// Clear the preferences if debug mode and clearprefs is defined
|
||||
static {
|
||||
Preferences root = Preferences.userRoot();
|
||||
if (DEBUG && CLEARPREFS) {
|
||||
@ -193,16 +199,27 @@ public class Prefs {
|
||||
//////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* Return the OpenRocket version number.
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return BuildPropertyHolder.BUILD_VERSION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the OpenRocket build source (e.g. "default" or "Debian")
|
||||
*/
|
||||
public static String getBuildSource() {
|
||||
return BuildPropertyHolder.BUILD_SOURCE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the OpenRocket unique ID.
|
||||
*
|
||||
* @return a random ID string that stays constant between OpenRocket executions
|
||||
*/
|
||||
public static String getUniqueID() {
|
||||
String id = PREFNODE.get("id", null);
|
||||
if (id == null) {
|
||||
@ -214,7 +231,10 @@ public class Prefs {
|
||||
|
||||
|
||||
|
||||
public static void storeVersion() {
|
||||
/**
|
||||
* Store the current OpenRocket version into the preferences to allow for preferences migration.
|
||||
*/
|
||||
private static void storeVersion() {
|
||||
PREFNODE.put("OpenRocketVersion", getVersion());
|
||||
}
|
||||
|
||||
@ -249,27 +269,66 @@ public class Prefs {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a string preference.
|
||||
*
|
||||
* @param key the preference key.
|
||||
* @param def the default if no preference is stored
|
||||
* @return the preference value
|
||||
*/
|
||||
public static String getString(String key, String def) {
|
||||
return PREFNODE.get(key, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a string preference.
|
||||
*
|
||||
* @param key the preference key
|
||||
* @param value the value to set
|
||||
*/
|
||||
public static void putString(String key, String value) {
|
||||
PREFNODE.put(key, value);
|
||||
storeVersion();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a boolean preference.
|
||||
*
|
||||
* @param key the preference key
|
||||
* @param def the default if no preference is stored
|
||||
* @return the preference value
|
||||
*/
|
||||
public static boolean getBoolean(String key, boolean def) {
|
||||
return PREFNODE.getBoolean(key, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a boolean preference.
|
||||
*
|
||||
* @param key the preference key
|
||||
* @param value the value to set
|
||||
*/
|
||||
public static void putBoolean(String key, boolean value) {
|
||||
PREFNODE.putBoolean(key, value);
|
||||
storeVersion();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a preferences object for the specified node name.
|
||||
*
|
||||
* @param nodeName the node name
|
||||
* @return the preferences object for that node
|
||||
*/
|
||||
public static Preferences getNode(String nodeName) {
|
||||
return PREFNODE.node(nodeName);
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
public static boolean getCheckUpdates() {
|
||||
return PREFNODE.getBoolean(CHECK_UPDATES, BuildPropertyHolder.DEFAULT_CHECK_UPDATES);
|
||||
@ -280,9 +339,6 @@ public class Prefs {
|
||||
storeVersion();
|
||||
}
|
||||
|
||||
|
||||
//////////////////
|
||||
|
||||
public static File getDefaultDirectory() {
|
||||
String file = PREFNODE.get("defaultDirectory", null);
|
||||
if (file == null)
|
||||
|
@ -3,7 +3,6 @@ package net.sf.openrocket.util;
|
||||
import java.awt.Color;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.openrocket.database.Databases;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.material.Material.Type;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
@ -30,6 +29,7 @@ import net.sf.openrocket.rocketcomponent.FinSet.CrossSection;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
|
||||
import net.sf.openrocket.rocketcomponent.Transition.Shape;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
public class TestRockets {
|
||||
|
||||
@ -38,18 +38,18 @@ public class TestRockets {
|
||||
|
||||
|
||||
public TestRockets(String key) {
|
||||
|
||||
|
||||
if (key == null) {
|
||||
Random rnd = new Random();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int n = rnd.nextInt(62);
|
||||
if (n < 10) {
|
||||
sb.append((char)('0'+n));
|
||||
sb.append((char) ('0' + n));
|
||||
} else if (n < 36) {
|
||||
sb.append((char)('A'+n-10));
|
||||
sb.append((char) ('A' + n - 10));
|
||||
} else {
|
||||
sb.append((char)('a'+n-36));
|
||||
sb.append((char) ('a' + n - 36));
|
||||
}
|
||||
}
|
||||
key = sb.toString();
|
||||
@ -57,10 +57,10 @@ public class TestRockets {
|
||||
|
||||
this.key = key;
|
||||
this.rnd = new Random(key.hashCode());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new test rocket based on the value 'key'. The rocket utilizes most of the
|
||||
* properties and features available. The same key always returns the same rocket,
|
||||
@ -82,12 +82,12 @@ public class TestRockets {
|
||||
rocket.setRevision("Rocket revision " + key);
|
||||
rocket.setName(key);
|
||||
|
||||
|
||||
|
||||
Stage stage = new Stage();
|
||||
setBasics(stage);
|
||||
rocket.addChild(stage);
|
||||
|
||||
|
||||
|
||||
NoseCone nose = new NoseCone();
|
||||
setBasics(stage);
|
||||
nose.setAftRadius(rnd(0.03));
|
||||
@ -99,13 +99,13 @@ public class TestRockets {
|
||||
nose.setClipped(rnd.nextBoolean());
|
||||
nose.setThickness(rnd(0.002));
|
||||
nose.setFilled(rnd.nextBoolean());
|
||||
nose.setForeRadius(rnd(0.1)); // Unset
|
||||
nose.setForeRadius(rnd(0.1)); // Unset
|
||||
nose.setLength(rnd(0.15));
|
||||
nose.setShapeParameter(rnd(0.5));
|
||||
nose.setType((Shape) randomEnum(Shape.class));
|
||||
stage.addChild(nose);
|
||||
|
||||
|
||||
|
||||
Transition shoulder = new Transition();
|
||||
setBasics(shoulder);
|
||||
shoulder.setAftRadius(rnd(0.06));
|
||||
@ -129,21 +129,21 @@ public class TestRockets {
|
||||
shoulder.setType((Shape) randomEnum(Shape.class));
|
||||
stage.addChild(shoulder);
|
||||
|
||||
|
||||
|
||||
BodyTube body = new BodyTube();
|
||||
setBasics(body);
|
||||
body.setThickness(rnd(0.002));
|
||||
body.setFilled(rnd.nextBoolean());
|
||||
body.setIgnitionDelay(rnd.nextDouble()*3);
|
||||
body.setIgnitionDelay(rnd.nextDouble() * 3);
|
||||
body.setIgnitionEvent((IgnitionEvent) randomEnum(IgnitionEvent.class));
|
||||
body.setLength(rnd(0.3));
|
||||
body.setMotorMount(rnd.nextBoolean());
|
||||
body.setMotorOverhang(rnd.nextGaussian()*0.03);
|
||||
body.setMotorOverhang(rnd.nextGaussian() * 0.03);
|
||||
body.setRadius(rnd(0.06));
|
||||
body.setRadiusAutomatic(rnd.nextBoolean());
|
||||
stage.addChild(body);
|
||||
|
||||
|
||||
|
||||
Transition boattail = new Transition();
|
||||
setBasics(boattail);
|
||||
boattail.setAftRadius(rnd(0.03));
|
||||
@ -177,9 +177,9 @@ public class TestRockets {
|
||||
mass.setRadius(rnd(0.05));
|
||||
nose.addChild(mass);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
@ -187,13 +187,13 @@ public class TestRockets {
|
||||
private void setBasics(RocketComponent c) {
|
||||
c.setComment(c.getComponentName() + " comment " + key);
|
||||
c.setName(c.getComponentName() + " name " + key);
|
||||
|
||||
|
||||
c.setCGOverridden(rnd.nextBoolean());
|
||||
c.setMassOverridden(rnd.nextBoolean());
|
||||
c.setOverrideCGX(rnd(0.2));
|
||||
c.setOverrideMass(rnd(0.05));
|
||||
c.setOverrideSubcomponents(rnd.nextBoolean());
|
||||
|
||||
|
||||
if (c.isMassive()) {
|
||||
// Only massive components are drawn
|
||||
c.setColor(randomColor());
|
||||
@ -201,23 +201,23 @@ public class TestRockets {
|
||||
}
|
||||
|
||||
if (c instanceof ExternalComponent) {
|
||||
ExternalComponent e = (ExternalComponent)c;
|
||||
ExternalComponent e = (ExternalComponent) c;
|
||||
e.setFinish((Finish) randomEnum(Finish.class));
|
||||
double d = rnd(100);
|
||||
e.setMaterial(Material.newMaterial(Type.BULK, "Testmat "+d, d, rnd.nextBoolean()));
|
||||
e.setMaterial(Material.newMaterial(Type.BULK, "Testmat " + d, d, rnd.nextBoolean()));
|
||||
}
|
||||
|
||||
if (c instanceof InternalComponent) {
|
||||
InternalComponent i = (InternalComponent)c;
|
||||
InternalComponent i = (InternalComponent) c;
|
||||
i.setRelativePosition((Position) randomEnum(Position.class));
|
||||
i.setPositionValue(rnd(0.3));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private double rnd(double scale) {
|
||||
return (rnd.nextDouble()*0.2+0.9) * scale;
|
||||
return (rnd.nextDouble() * 0.2 + 0.9) * scale;
|
||||
}
|
||||
|
||||
private Color randomColor() {
|
||||
@ -231,20 +231,19 @@ public class TestRockets {
|
||||
|
||||
return values[rnd.nextInt(values.length)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public Rocket makeSmallFlyable() {
|
||||
double noseconeLength=0.10,noseconeRadius=0.01;
|
||||
double bodytubeLength=0.20,bodytubeRadius=0.01,bodytubeThickness=0.001;
|
||||
|
||||
int finCount=3;
|
||||
double finRootChord=0.04,finTipChord=0.05,finSweep=0.01,finThickness=0.003, finHeight=0.03;
|
||||
double noseconeLength = 0.10, noseconeRadius = 0.01;
|
||||
double bodytubeLength = 0.20, bodytubeRadius = 0.01, bodytubeThickness = 0.001;
|
||||
|
||||
int finCount = 3;
|
||||
double finRootChord = 0.04, finTipChord = 0.05, finSweep = 0.01, finThickness = 0.003, finHeight = 0.03;
|
||||
|
||||
|
||||
Rocket rocket;
|
||||
Stage stage;
|
||||
NoseCone nosecone;
|
||||
@ -254,21 +253,21 @@ public class TestRockets {
|
||||
rocket = new Rocket();
|
||||
stage = new Stage();
|
||||
stage.setName("Stage1");
|
||||
|
||||
nosecone = new NoseCone(Transition.Shape.ELLIPSOID,noseconeLength,noseconeRadius);
|
||||
bodytube = new BodyTube(bodytubeLength,bodytubeRadius,bodytubeThickness);
|
||||
|
||||
finset = new TrapezoidFinSet(finCount,finRootChord,finTipChord,finSweep,finHeight);
|
||||
|
||||
nosecone = new NoseCone(Transition.Shape.ELLIPSOID, noseconeLength, noseconeRadius);
|
||||
bodytube = new BodyTube(bodytubeLength, bodytubeRadius, bodytubeThickness);
|
||||
|
||||
finset = new TrapezoidFinSet(finCount, finRootChord, finTipChord, finSweep, finHeight);
|
||||
|
||||
|
||||
// Stage construction
|
||||
rocket.addChild(stage);
|
||||
|
||||
|
||||
|
||||
// Component construction
|
||||
stage.addChild(nosecone);
|
||||
stage.addChild(bodytube);
|
||||
|
||||
|
||||
bodytube.addChild(finset);
|
||||
|
||||
Material material = Prefs.getDefaultComponentMaterial(null, Material.Type.BULK);
|
||||
@ -279,22 +278,18 @@ public class TestRockets {
|
||||
String id = rocket.newMotorConfigurationID();
|
||||
bodytube.setMotorMount(true);
|
||||
|
||||
for (Motor m: Databases.MOTOR) {
|
||||
if (m.getDesignation().equals("B4")) {
|
||||
bodytube.setMotor(id, m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Motor m = Application.getMotorSetDatabase().findMotors(null, null, "B4", Double.NaN, Double.NaN).get(0);
|
||||
bodytube.setMotor(id, m);
|
||||
bodytube.setMotorOverhang(0.005);
|
||||
rocket.getDefaultConfiguration().setMotorConfigurationID(id);
|
||||
|
||||
rocket.getDefaultConfiguration().setAllStages();
|
||||
|
||||
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Rocket makeBigBlue() {
|
||||
Rocket rocket;
|
||||
Stage stage;
|
||||
@ -306,11 +301,11 @@ public class TestRockets {
|
||||
rocket = new Rocket();
|
||||
stage = new Stage();
|
||||
stage.setName("Stage1");
|
||||
|
||||
nosecone = new NoseCone(Transition.Shape.ELLIPSOID,0.105,0.033);
|
||||
|
||||
nosecone = new NoseCone(Transition.Shape.ELLIPSOID, 0.105, 0.033);
|
||||
nosecone.setThickness(0.001);
|
||||
bodytube = new BodyTube(0.69,0.033,0.001);
|
||||
|
||||
bodytube = new BodyTube(0.69, 0.033, 0.001);
|
||||
|
||||
finset = new FreeformFinSet();
|
||||
try {
|
||||
finset.setPoints(new Coordinate[] {
|
||||
@ -326,46 +321,42 @@ public class TestRockets {
|
||||
finset.setThickness(0.003);
|
||||
finset.setFinCount(4);
|
||||
|
||||
finset.setCantAngle(0*Math.PI/180);
|
||||
System.err.println("Fin cant angle: "+(finset.getCantAngle() * 180/Math.PI));
|
||||
finset.setCantAngle(0 * Math.PI / 180);
|
||||
System.err.println("Fin cant angle: " + (finset.getCantAngle() * 180 / Math.PI));
|
||||
|
||||
mcomp = new MassComponent(0.2,0.03,0.045 + 0.060);
|
||||
mcomp = new MassComponent(0.2, 0.03, 0.045 + 0.060);
|
||||
mcomp.setRelativePosition(Position.TOP);
|
||||
mcomp.setPositionValue(0);
|
||||
|
||||
// Stage construction
|
||||
rocket.addChild(stage);
|
||||
rocket.setPerfectFinish(false);
|
||||
|
||||
|
||||
|
||||
// Component construction
|
||||
stage.addChild(nosecone);
|
||||
stage.addChild(bodytube);
|
||||
|
||||
|
||||
bodytube.addChild(finset);
|
||||
|
||||
bodytube.addChild(mcomp);
|
||||
|
||||
// Material material = new Material("Test material", 500);
|
||||
// nosecone.setMaterial(material);
|
||||
// bodytube.setMaterial(material);
|
||||
// finset.setMaterial(material);
|
||||
// Material material = new Material("Test material", 500);
|
||||
// nosecone.setMaterial(material);
|
||||
// bodytube.setMaterial(material);
|
||||
// finset.setMaterial(material);
|
||||
|
||||
String id = rocket.newMotorConfigurationID();
|
||||
bodytube.setMotorMount(true);
|
||||
|
||||
for (Motor m: Databases.MOTOR) {
|
||||
if (m.getDesignation().equals("F12J")) {
|
||||
bodytube.setMotor(id, m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Motor m = Application.getMotorSetDatabase().findMotors(null, null, "F12J", Double.NaN, Double.NaN).get(0);
|
||||
bodytube.setMotor(id, m);
|
||||
bodytube.setMotorOverhang(0.005);
|
||||
rocket.getDefaultConfiguration().setMotorConfigurationID(id);
|
||||
|
||||
rocket.getDefaultConfiguration().setAllStages();
|
||||
|
||||
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
@ -385,29 +376,29 @@ public class TestRockets {
|
||||
rocket = new Rocket();
|
||||
stage = new Stage();
|
||||
stage.setName("Stage1");
|
||||
|
||||
nosecone = new NoseCone(Transition.Shape.OGIVE,0.53,R);
|
||||
|
||||
nosecone = new NoseCone(Transition.Shape.OGIVE, 0.53, R);
|
||||
nosecone.setThickness(0.005);
|
||||
nosecone.setMassOverridden(true);
|
||||
nosecone.setOverrideMass(0.588);
|
||||
stage.addChild(nosecone);
|
||||
|
||||
tube1 = new BodyTube(0.505,R,0.005);
|
||||
tube1 = new BodyTube(0.505, R, 0.005);
|
||||
tube1.setMassOverridden(true);
|
||||
tube1.setOverrideMass(0.366);
|
||||
stage.addChild(tube1);
|
||||
|
||||
tube2 = new BodyTube(0.605,R,0.005);
|
||||
tube2 = new BodyTube(0.605, R, 0.005);
|
||||
tube2.setMassOverridden(true);
|
||||
tube2.setOverrideMass(0.427);
|
||||
stage.addChild(tube2);
|
||||
|
||||
tube3 = new BodyTube(1.065,R,0.005);
|
||||
tube3 = new BodyTube(1.065, R, 0.005);
|
||||
tube3.setMassOverridden(true);
|
||||
tube3.setOverrideMass(0.730);
|
||||
stage.addChild(tube3);
|
||||
|
||||
|
||||
|
||||
LaunchLug lug = new LaunchLug();
|
||||
tube1.addChild(lug);
|
||||
|
||||
@ -421,7 +412,7 @@ public class TestRockets {
|
||||
coupler.setPositionValue(-0.14);
|
||||
tube1.addChild(coupler);
|
||||
|
||||
|
||||
|
||||
// Parachute
|
||||
MassComponent mass = new MassComponent(0.05, 0.05, 0.280);
|
||||
mass.setRelativePosition(Position.TOP);
|
||||
@ -440,7 +431,7 @@ public class TestRockets {
|
||||
mass.setPositionValue(0.25);
|
||||
tube1.addChild(mass);
|
||||
|
||||
|
||||
|
||||
auxfinset = new TrapezoidFinSet();
|
||||
auxfinset.setName("CONTROL");
|
||||
auxfinset.setFinCount(2);
|
||||
@ -452,12 +443,12 @@ public class TestRockets {
|
||||
auxfinset.setCrossSection(CrossSection.AIRFOIL);
|
||||
auxfinset.setRelativePosition(Position.TOP);
|
||||
auxfinset.setPositionValue(0.28);
|
||||
auxfinset.setBaseRotation(Math.PI/2);
|
||||
auxfinset.setBaseRotation(Math.PI / 2);
|
||||
tube1.addChild(auxfinset);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
coupler = new TubeCoupler();
|
||||
coupler.setOuterRadiusAutomatic(true);
|
||||
coupler.setLength(0.28);
|
||||
@ -467,8 +458,8 @@ public class TestRockets {
|
||||
coupler.setOverrideMass(0.360);
|
||||
tube2.addChild(coupler);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Parachute
|
||||
mass = new MassComponent(0.1, 0.05, 0.028);
|
||||
mass.setRelativePosition(Position.TOP);
|
||||
@ -489,17 +480,17 @@ public class TestRockets {
|
||||
mass.setPositionValue(0.19);
|
||||
tube2.addChild(mass);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
InnerTube inner = new InnerTube();
|
||||
inner.setOuterRadius(0.08/2);
|
||||
inner.setInnerRadius(0.0762/2);
|
||||
inner.setOuterRadius(0.08 / 2);
|
||||
inner.setInnerRadius(0.0762 / 2);
|
||||
inner.setLength(0.86);
|
||||
inner.setMassOverridden(true);
|
||||
inner.setOverrideMass(0.388);
|
||||
tube3.addChild(inner);
|
||||
|
||||
|
||||
|
||||
CenteringRing center = new CenteringRing();
|
||||
center.setInnerRadiusAutomatic(true);
|
||||
center.setOuterRadiusAutomatic(true);
|
||||
@ -510,7 +501,7 @@ public class TestRockets {
|
||||
center.setPositionValue(0);
|
||||
tube3.addChild(center);
|
||||
|
||||
|
||||
|
||||
center = new CenteringRing();
|
||||
center.setInnerRadiusAutomatic(true);
|
||||
center.setOuterRadiusAutomatic(true);
|
||||
@ -521,7 +512,7 @@ public class TestRockets {
|
||||
center.setPositionValue(0.28);
|
||||
tube3.addChild(center);
|
||||
|
||||
|
||||
|
||||
center = new CenteringRing();
|
||||
center.setInnerRadiusAutomatic(true);
|
||||
center.setOuterRadiusAutomatic(true);
|
||||
@ -532,10 +523,10 @@ public class TestRockets {
|
||||
center.setPositionValue(0.83);
|
||||
tube3.addChild(center);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finset = new TrapezoidFinSet();
|
||||
finset.setRootChord(0.495);
|
||||
finset.setTipChord(0.1);
|
||||
@ -544,40 +535,36 @@ public class TestRockets {
|
||||
finset.setSweep(0.3);
|
||||
finset.setRelativePosition(Position.BOTTOM);
|
||||
finset.setPositionValue(-0.03);
|
||||
finset.setBaseRotation(Math.PI/2);
|
||||
finset.setBaseRotation(Math.PI / 2);
|
||||
tube3.addChild(finset);
|
||||
|
||||
|
||||
finset.setCantAngle(0 * Math.PI / 180);
|
||||
System.err.println("Fin cant angle: " + (finset.getCantAngle() * 180 / Math.PI));
|
||||
|
||||
finset.setCantAngle(0*Math.PI/180);
|
||||
System.err.println("Fin cant angle: "+(finset.getCantAngle() * 180/Math.PI));
|
||||
|
||||
|
||||
|
||||
// Stage construction
|
||||
rocket.addChild(stage);
|
||||
rocket.setPerfectFinish(false);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
String id = rocket.newMotorConfigurationID();
|
||||
tube3.setMotorMount(true);
|
||||
|
||||
for (Motor m: Databases.MOTOR) {
|
||||
if (m.getDesignation().equals("L540")) {
|
||||
tube3.setMotor(id, m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Motor m = Application.getMotorSetDatabase().findMotors(null, null, "L540", Double.NaN, Double.NaN).get(0);
|
||||
tube3.setMotor(id, m);
|
||||
tube3.setMotorOverhang(0.02);
|
||||
rocket.getDefaultConfiguration().setMotorConfigurationID(id);
|
||||
|
||||
// tube3.setIgnitionEvent(MotorMount.IgnitionEvent.NEVER);
|
||||
|
||||
// tube3.setIgnitionEvent(MotorMount.IgnitionEvent.NEVER);
|
||||
|
||||
rocket.getDefaultConfiguration().setAllStages();
|
||||
|
||||
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public class MotorSetDatabaseTest {
|
||||
@Test
|
||||
public void testMotorLoading() {
|
||||
|
||||
MotorSetDatabase db = new MotorSetDatabase(true) {
|
||||
ThrustCurveMotorSetDatabase db = new ThrustCurveMotorSetDatabase(true) {
|
||||
@Override
|
||||
protected void loadMotors() {
|
||||
try {
|
||||
|
Loading…
x
Reference in New Issue
Block a user