enhanced motor selection dialog

This commit is contained in:
Sampo Niskanen 2010-07-22 07:21:37 +00:00
parent e4d56f51ef
commit e80b64b53c
34 changed files with 2298 additions and 1310 deletions

View File

@ -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"/>

View File

@ -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

View File

@ -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
View 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 "$@"

View File

@ -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]);
}
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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();

View File

@ -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));
}
}
}

View File

@ -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");
}
}

View 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);
}

View 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);
}
}
}

View 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);
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)));

View File

@ -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;
}

View File

@ -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();

View File

@ -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?" });
}
}
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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 {