Added support for Unit 'in/64' which is inches with fractional representation. DoubleModel has one FIXME indicating that we need to reenable min & max in the abstract spinner model.
This commit is contained in:
parent
7b093f79f8
commit
bd680c7e14
@ -9,13 +9,13 @@ import javax.swing.JSpinner;
|
|||||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class SpinnerEditor extends JSpinner.NumberEditor {
|
//public class SpinnerEditor extends JSpinner.NumberEditor {
|
||||||
//public class SpinnerEditor extends JSpinner.DefaultEditor {
|
public class SpinnerEditor extends JSpinner.DefaultEditor {
|
||||||
|
|
||||||
public SpinnerEditor(JSpinner spinner) {
|
public SpinnerEditor(JSpinner spinner) {
|
||||||
//super(spinner);
|
super(spinner);
|
||||||
super(spinner,"0.0##");
|
//super(spinner,"0.0##");
|
||||||
//getTextField().setEditable(true);
|
getTextField().setEditable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ import java.util.EventListener;
|
|||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.AbstractSpinnerModel;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.BoundedRangeModel;
|
import javax.swing.BoundedRangeModel;
|
||||||
import javax.swing.SpinnerModel;
|
import javax.swing.SpinnerModel;
|
||||||
import javax.swing.SpinnerNumberModel;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ import net.sf.openrocket.unit.Unit;
|
|||||||
import net.sf.openrocket.unit.UnitGroup;
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
import net.sf.openrocket.util.BugException;
|
import net.sf.openrocket.util.BugException;
|
||||||
import net.sf.openrocket.util.ChangeSource;
|
import net.sf.openrocket.util.ChangeSource;
|
||||||
|
import net.sf.openrocket.util.FractionUtil;
|
||||||
import net.sf.openrocket.util.Invalidatable;
|
import net.sf.openrocket.util.Invalidatable;
|
||||||
import net.sf.openrocket.util.Invalidator;
|
import net.sf.openrocket.util.Invalidator;
|
||||||
import net.sf.openrocket.util.MathUtil;
|
import net.sf.openrocket.util.MathUtil;
|
||||||
@ -47,23 +48,23 @@ import net.sf.openrocket.util.StateChangeListener;
|
|||||||
|
|
||||||
public class DoubleModel implements StateChangeListener, ChangeSource, Invalidatable {
|
public class DoubleModel implements StateChangeListener, ChangeSource, Invalidatable {
|
||||||
private static final LogHelper log = Application.getLogger();
|
private static final LogHelper log = Application.getLogger();
|
||||||
|
|
||||||
|
|
||||||
public static final DoubleModel ZERO = new DoubleModel(0);
|
public static final DoubleModel ZERO = new DoubleModel(0);
|
||||||
|
|
||||||
//////////// JSpinner Model ////////////
|
//////////// JSpinner Model ////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model suitable for JSpinner using JSpinner.NumberEditor. It extends SpinnerNumberModel
|
* Model suitable for JSpinner using JSpinner.NumberEditor. It extends SpinnerNumberModel
|
||||||
* to be compatible with the NumberEditor, but only has the necessary methods defined.
|
* to be compatible with the NumberEditor, but only has the necessary methods defined.
|
||||||
*/
|
*/
|
||||||
private class ValueSpinnerModel extends SpinnerNumberModel implements Invalidatable {
|
public class ValueSpinnerModel extends AbstractSpinnerModel implements Invalidatable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return currentUnit.toUnit(DoubleModel.this.getValue());
|
return currentUnit.toString(DoubleModel.this.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValue(Object value) {
|
public void setValue(Object value) {
|
||||||
if (firing > 0) {
|
if (firing > 0) {
|
||||||
@ -72,16 +73,28 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
" value=" + value + ", currently firing events");
|
" value=" + value + ", currently firing events");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Number num = (Number) value;
|
Number num = 0;
|
||||||
|
if ( value instanceof Number ) {
|
||||||
|
num = (Number)value;
|
||||||
|
} else if ( value instanceof String ) {
|
||||||
|
try {
|
||||||
|
String newValString = (String)value;
|
||||||
|
num = FractionUtil.parseFraction(newValString);
|
||||||
|
}
|
||||||
|
catch ( java.lang.NumberFormatException nfex ) {
|
||||||
|
num = 0.0d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double newValue = num.doubleValue();
|
double newValue = num.doubleValue();
|
||||||
double converted = currentUnit.fromUnit(newValue);
|
double converted = currentUnit.fromUnit(newValue);
|
||||||
|
|
||||||
log.user("SpinnerModel setValue called for " + DoubleModel.this.toString() + " newValue=" + newValue +
|
log.user("SpinnerModel setValue called for " + DoubleModel.this.toString() + " newValue=" + newValue +
|
||||||
" converted=" + converted);
|
" converted=" + converted);
|
||||||
DoubleModel.this.setValue(converted);
|
DoubleModel.this.setValue(converted);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getNextValue() {
|
public Object getNextValue() {
|
||||||
double d = currentUnit.toUnit(DoubleModel.this.getValue());
|
double d = currentUnit.toUnit(DoubleModel.this.getValue());
|
||||||
@ -93,7 +106,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
d = max;
|
d = max;
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getPreviousValue() {
|
public Object getPreviousValue() {
|
||||||
double d = currentUnit.toUnit(DoubleModel.this.getValue());
|
double d = currentUnit.toUnit(DoubleModel.this.getValue());
|
||||||
@ -105,35 +118,33 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
d = min;
|
d = min;
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
/* FIXME - put min & max back
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Comparable<Double> getMinimum() {
|
public Comparable<Double> getMinimum() {
|
||||||
return currentUnit.toUnit(minValue);
|
return currentUnit.toUnit(minValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Comparable<Double> getMaximum() {
|
public Comparable<Double> getMaximum() {
|
||||||
return currentUnit.toUnit(maxValue);
|
return currentUnit.toUnit(maxValue);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addChangeListener(ChangeListener l) {
|
public void addChangeListener(ChangeListener l) {
|
||||||
DoubleModel.this.addChangeListener(l);
|
DoubleModel.this.addChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeChangeListener(ChangeListener l) {
|
public void removeChangeListener(ChangeListener l) {
|
||||||
DoubleModel.this.removeChangeListener(l);
|
DoubleModel.this.removeChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
DoubleModel.this.invalidate();
|
DoubleModel.this.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new SpinnerModel with the same base as the DoubleModel.
|
* Returns a new SpinnerModel with the same base as the DoubleModel.
|
||||||
* The values given to the JSpinner are in the currently selected units.
|
* The values given to the JSpinner are in the currently selected units.
|
||||||
@ -143,50 +154,46 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public SpinnerModel getSpinnerModel() {
|
public SpinnerModel getSpinnerModel() {
|
||||||
return new ValueSpinnerModel();
|
return new ValueSpinnerModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////// JSlider model ////////////
|
//////////// JSlider model ////////////
|
||||||
|
|
||||||
private class ValueSliderModel implements BoundedRangeModel, StateChangeListener, Invalidatable {
|
private class ValueSliderModel implements BoundedRangeModel, StateChangeListener, Invalidatable {
|
||||||
private static final int MAX = 1000;
|
private static final int MAX = 1000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use linear scale value = linear1 * x + linear0 when x < linearPosition
|
* Use linear scale value = linear1 * x + linear0 when x < linearPosition
|
||||||
* Use quadratic scale value = quad2 * x^2 + quad1 * x + quad0 otherwise
|
* Use quadratic scale value = quad2 * x^2 + quad1 * x + quad0 otherwise
|
||||||
*/
|
*/
|
||||||
private final boolean islinear;
|
private final boolean islinear;
|
||||||
|
|
||||||
// Linear in range x <= linearPosition
|
// Linear in range x <= linearPosition
|
||||||
private final double linearPosition;
|
private final double linearPosition;
|
||||||
|
|
||||||
// May be changing DoubleModels when using linear model
|
// May be changing DoubleModels when using linear model
|
||||||
private final DoubleModel min, mid, max;
|
private final DoubleModel min, mid, max;
|
||||||
|
|
||||||
// Linear multiplier and constant
|
// Linear multiplier and constant
|
||||||
//private final double linear1;
|
//private final double linear1;
|
||||||
//private final double linear0;
|
//private final double linear0;
|
||||||
|
|
||||||
// Non-linear multiplier, exponent and constant
|
// Non-linear multiplier, exponent and constant
|
||||||
private double quad2, quad1, quad0;
|
private double quad2, quad1, quad0;
|
||||||
|
|
||||||
public ValueSliderModel(DoubleModel min, DoubleModel max) {
|
public ValueSliderModel(DoubleModel min, DoubleModel max) {
|
||||||
this.islinear = true;
|
this.islinear = true;
|
||||||
linearPosition = 1.0;
|
linearPosition = 1.0;
|
||||||
|
|
||||||
this.min = min;
|
this.min = min;
|
||||||
this.mid = max; // Never use exponential scale
|
this.mid = max; // Never use exponential scale
|
||||||
this.max = max;
|
this.max = max;
|
||||||
|
|
||||||
min.addChangeListener(this);
|
min.addChangeListener(this);
|
||||||
max.addChangeListener(this);
|
max.addChangeListener(this);
|
||||||
|
|
||||||
quad2 = quad1 = quad0 = 0; // Not used
|
quad2 = quad1 = quad0 = 0; // Not used
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a linear model from min to max.
|
* Generate a linear model from min to max.
|
||||||
@ -194,18 +201,18 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public ValueSliderModel(double min, double max) {
|
public ValueSliderModel(double min, double max) {
|
||||||
this.islinear = true;
|
this.islinear = true;
|
||||||
linearPosition = 1.0;
|
linearPosition = 1.0;
|
||||||
|
|
||||||
this.min = new DoubleModel(min);
|
this.min = new DoubleModel(min);
|
||||||
this.mid = new DoubleModel(max); // Never use exponential scale
|
this.mid = new DoubleModel(max); // Never use exponential scale
|
||||||
this.max = new DoubleModel(max);
|
this.max = new DoubleModel(max);
|
||||||
|
|
||||||
quad2 = quad1 = quad0 = 0; // Not used
|
quad2 = quad1 = quad0 = 0; // Not used
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueSliderModel(double min, double mid, double max) {
|
public ValueSliderModel(double min, double mid, double max) {
|
||||||
this(min, 0.5, mid, max);
|
this(min, 0.5, mid, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueSliderModel(double min, double mid, DoubleModel max) {
|
public ValueSliderModel(double min, double mid, DoubleModel max) {
|
||||||
this(min, 0.5, mid, max);
|
this(min, 0.5, mid, max);
|
||||||
}
|
}
|
||||||
@ -226,22 +233,22 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
this.max = max;
|
this.max = max;
|
||||||
|
|
||||||
this.islinear = false;
|
this.islinear = false;
|
||||||
|
|
||||||
max.addChangeListener(this);
|
max.addChangeListener(this);
|
||||||
|
|
||||||
linearPosition = pos;
|
linearPosition = pos;
|
||||||
//linear0 = min;
|
//linear0 = min;
|
||||||
//linear1 = (mid-min)/pos;
|
//linear1 = (mid-min)/pos;
|
||||||
|
|
||||||
if (!(min < mid && mid <= max.getValue() && 0 < pos && pos < 1)) {
|
if (!(min < mid && mid <= max.getValue() && 0 < pos && pos < 1)) {
|
||||||
throw new IllegalArgumentException("Bad arguments for ValueSliderModel " +
|
throw new IllegalArgumentException("Bad arguments for ValueSliderModel " +
|
||||||
"min=" + min + " mid=" + mid + " max=" + max + " pos=" + pos);
|
"min=" + min + " mid=" + mid + " max=" + max + " pos=" + pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateExponentialParameters();
|
updateExponentialParameters();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateExponentialParameters() {
|
private void updateExponentialParameters() {
|
||||||
double pos = this.linearPosition;
|
double pos = this.linearPosition;
|
||||||
double minValue = this.min.getValue();
|
double minValue = this.min.getValue();
|
||||||
@ -258,11 +265,11 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
quad1 = (delta + 2 * (midValue - maxValue) * pos - delta * pos * pos) / pow2(pos - 1);
|
quad1 = (delta + 2 * (midValue - maxValue) * pos - delta * pos * pos) / pow2(pos - 1);
|
||||||
quad0 = (midValue - (2 * midValue + delta) * pos + (maxValue + delta) * pos * pos) / pow2(pos - 1);
|
quad0 = (midValue - (2 * midValue + delta) * pos + (maxValue + delta) * pos * pos) / pow2(pos - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double pow2(double x) {
|
private double pow2(double x) {
|
||||||
return x * x;
|
return x * x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getValue() {
|
public int getValue() {
|
||||||
double value = DoubleModel.this.getValue();
|
double value = DoubleModel.this.getValue();
|
||||||
@ -270,13 +277,13 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
return 0;
|
return 0;
|
||||||
if (value >= max.getValue())
|
if (value >= max.getValue())
|
||||||
return MAX;
|
return MAX;
|
||||||
|
|
||||||
double x;
|
double x;
|
||||||
if (value <= mid.getValue()) {
|
if (value <= mid.getValue()) {
|
||||||
// Use linear scale
|
// Use linear scale
|
||||||
//linear0 = min;
|
//linear0 = min;
|
||||||
//linear1 = (mid-min)/pos;
|
//linear1 = (mid-min)/pos;
|
||||||
|
|
||||||
x = (value - min.getValue()) * linearPosition / (mid.getValue() - min.getValue());
|
x = (value - min.getValue()) * linearPosition / (mid.getValue() - min.getValue());
|
||||||
} else {
|
} else {
|
||||||
// Use quadratic scale
|
// Use quadratic scale
|
||||||
@ -286,8 +293,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
}
|
}
|
||||||
return (int) (x * MAX);
|
return (int) (x * MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValue(int newValue) {
|
public void setValue(int newValue) {
|
||||||
if (firing > 0) {
|
if (firing > 0) {
|
||||||
@ -296,91 +303,91 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
" value=" + newValue + ", currently firing events");
|
" value=" + newValue + ", currently firing events");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double x = (double) newValue / MAX;
|
double x = (double) newValue / MAX;
|
||||||
double scaledValue;
|
double scaledValue;
|
||||||
|
|
||||||
if (x <= linearPosition) {
|
if (x <= linearPosition) {
|
||||||
// Use linear scale
|
// Use linear scale
|
||||||
//linear0 = min;
|
//linear0 = min;
|
||||||
//linear1 = (mid-min)/pos;
|
//linear1 = (mid-min)/pos;
|
||||||
|
|
||||||
scaledValue = (mid.getValue() - min.getValue()) / linearPosition * x + min.getValue();
|
scaledValue = (mid.getValue() - min.getValue()) / linearPosition * x + min.getValue();
|
||||||
} else {
|
} else {
|
||||||
// Use quadratic scale
|
// Use quadratic scale
|
||||||
scaledValue = quad2 * x * x + quad1 * x + quad0;
|
scaledValue = quad2 * x * x + quad1 * x + quad0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double converted = currentUnit.fromUnit(currentUnit.round(currentUnit.toUnit(scaledValue)));
|
double converted = currentUnit.fromUnit(currentUnit.round(currentUnit.toUnit(scaledValue)));
|
||||||
log.user("SliderModel setValue called for " + DoubleModel.this.toString() + " newValue=" + newValue +
|
log.user("SliderModel setValue called for " + DoubleModel.this.toString() + " newValue=" + newValue +
|
||||||
" scaledValue=" + scaledValue + " converted=" + converted);
|
" scaledValue=" + scaledValue + " converted=" + converted);
|
||||||
DoubleModel.this.setValue(converted);
|
DoubleModel.this.setValue(converted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Static get-methods
|
// Static get-methods
|
||||||
private boolean isAdjusting;
|
private boolean isAdjusting;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getExtent() {
|
public int getExtent() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMaximum() {
|
public int getMaximum() {
|
||||||
return MAX;
|
return MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMinimum() {
|
public int getMinimum() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean getValueIsAdjusting() {
|
public boolean getValueIsAdjusting() {
|
||||||
return isAdjusting;
|
return isAdjusting;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore set-values
|
// Ignore set-values
|
||||||
@Override
|
@Override
|
||||||
public void setExtent(int newExtent) {
|
public void setExtent(int newExtent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMaximum(int newMaximum) {
|
public void setMaximum(int newMaximum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMinimum(int newMinimum) {
|
public void setMinimum(int newMinimum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValueIsAdjusting(boolean b) {
|
public void setValueIsAdjusting(boolean b) {
|
||||||
isAdjusting = b;
|
isAdjusting = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting) {
|
public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting) {
|
||||||
setValueIsAdjusting(adjusting);
|
setValueIsAdjusting(adjusting);
|
||||||
setValue(value);
|
setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass change listeners to the underlying model
|
// Pass change listeners to the underlying model
|
||||||
@Override
|
@Override
|
||||||
public void addChangeListener(ChangeListener l) {
|
public void addChangeListener(ChangeListener l) {
|
||||||
DoubleModel.this.addChangeListener(l);
|
DoubleModel.this.addChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeChangeListener(ChangeListener l) {
|
public void removeChangeListener(ChangeListener l) {
|
||||||
DoubleModel.this.removeChangeListener(l);
|
DoubleModel.this.removeChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
DoubleModel.this.invalidate();
|
DoubleModel.this.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged(EventObject e) {
|
public void stateChanged(EventObject e) {
|
||||||
// Min or max range has changed.
|
// Min or max range has changed.
|
||||||
@ -394,48 +401,48 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
fireStateChanged();
|
fireStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public BoundedRangeModel getSliderModel(DoubleModel min, DoubleModel max) {
|
public BoundedRangeModel getSliderModel(DoubleModel min, DoubleModel max) {
|
||||||
return new ValueSliderModel(min, max);
|
return new ValueSliderModel(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundedRangeModel getSliderModel(double min, double max) {
|
public BoundedRangeModel getSliderModel(double min, double max) {
|
||||||
return new ValueSliderModel(min, max);
|
return new ValueSliderModel(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundedRangeModel getSliderModel(double min, double mid, double max) {
|
public BoundedRangeModel getSliderModel(double min, double mid, double max) {
|
||||||
return new ValueSliderModel(min, mid, max);
|
return new ValueSliderModel(min, mid, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundedRangeModel getSliderModel(double min, double mid, DoubleModel max) {
|
public BoundedRangeModel getSliderModel(double min, double mid, DoubleModel max) {
|
||||||
return new ValueSliderModel(min, mid, max);
|
return new ValueSliderModel(min, mid, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundedRangeModel getSliderModel(double min, double pos, double mid, double max) {
|
public BoundedRangeModel getSliderModel(double min, double pos, double mid, double max) {
|
||||||
return new ValueSliderModel(min, pos, mid, max);
|
return new ValueSliderModel(min, pos, mid, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////// Action model ////////////
|
//////////// Action model ////////////
|
||||||
|
|
||||||
private class AutomaticActionModel extends AbstractAction implements StateChangeListener, Invalidatable {
|
private class AutomaticActionModel extends AbstractAction implements StateChangeListener, Invalidatable {
|
||||||
private boolean oldValue = false;
|
private boolean oldValue = false;
|
||||||
|
|
||||||
public AutomaticActionModel() {
|
public AutomaticActionModel() {
|
||||||
oldValue = isAutomatic();
|
oldValue = isAutomatic();
|
||||||
addChangeListener(this);
|
addChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return isAutomaticAvailable();
|
return isAutomaticAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getValue(String key) {
|
public Object getValue(String key) {
|
||||||
if (key.equals(Action.SELECTED_KEY)) {
|
if (key.equals(Action.SELECTED_KEY)) {
|
||||||
@ -444,7 +451,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
}
|
}
|
||||||
return super.getValue(key);
|
return super.getValue(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putValue(String key, Object value) {
|
public void putValue(String key, Object value) {
|
||||||
if (firing > 0) {
|
if (firing > 0) {
|
||||||
@ -463,24 +470,24 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
super.putValue(key, value);
|
super.putValue(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement a wrapper to the ChangeListeners
|
// Implement a wrapper to the ChangeListeners
|
||||||
ArrayList<PropertyChangeListener> propertyChangeListeners =
|
ArrayList<PropertyChangeListener> propertyChangeListeners =
|
||||||
new ArrayList<PropertyChangeListener>();
|
new ArrayList<PropertyChangeListener>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||||
propertyChangeListeners.add(listener);
|
propertyChangeListeners.add(listener);
|
||||||
DoubleModel.this.addChangeListener(this);
|
DoubleModel.this.addChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||||
propertyChangeListeners.remove(listener);
|
propertyChangeListeners.remove(listener);
|
||||||
if (propertyChangeListeners.isEmpty())
|
if (propertyChangeListeners.isEmpty())
|
||||||
DoubleModel.this.removeChangeListener(this);
|
DoubleModel.this.removeChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the value has changed, generate an event to the listeners
|
// If the value has changed, generate an event to the listeners
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged(EventObject e) {
|
public void stateChanged(EventObject e) {
|
||||||
@ -495,18 +502,18 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
((PropertyChangeListener) l[i]).propertyChange(event);
|
((PropertyChangeListener) l[i]).propertyChange(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
// Setting performed in putValue
|
// Setting performed in putValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
DoubleModel.this.invalidate();
|
DoubleModel.this.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new Action corresponding to the changes of the automatic setting
|
* Returns a new Action corresponding to the changes of the automatic setting
|
||||||
* property of the value model. This may be used directly with e.g. check buttons.
|
* property of the value model. This may be used directly with e.g. check buttons.
|
||||||
@ -516,13 +523,13 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public Action getAutomaticAction() {
|
public Action getAutomaticAction() {
|
||||||
return new AutomaticActionModel();
|
return new AutomaticActionModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////// Main model /////////////
|
//////////// Main model /////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The main model handles all values in SI units, i.e. no conversion is made within the model.
|
* The main model handles all values in SI units, i.e. no conversion is made within the model.
|
||||||
*/
|
*/
|
||||||
@ -530,34 +537,34 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
private final ChangeSource source;
|
private final ChangeSource source;
|
||||||
private final String valueName;
|
private final String valueName;
|
||||||
private final double multiplier;
|
private final double multiplier;
|
||||||
|
|
||||||
private final Method getMethod;
|
private final Method getMethod;
|
||||||
private final Method setMethod;
|
private final Method setMethod;
|
||||||
|
|
||||||
private final Method getAutoMethod;
|
private final Method getAutoMethod;
|
||||||
private final Method setAutoMethod;
|
private final Method setAutoMethod;
|
||||||
|
|
||||||
private final ArrayList<EventListener> listeners = new ArrayList<EventListener>();
|
private final ArrayList<EventListener> listeners = new ArrayList<EventListener>();
|
||||||
|
|
||||||
private final UnitGroup units;
|
private final UnitGroup units;
|
||||||
private Unit currentUnit;
|
private Unit currentUnit;
|
||||||
|
|
||||||
private final double minValue;
|
private final double minValue;
|
||||||
private double maxValue;
|
private double maxValue;
|
||||||
|
|
||||||
private String toString = null;
|
private String toString = null;
|
||||||
|
|
||||||
|
|
||||||
private int firing = 0; // >0 when model itself is sending events
|
private int firing = 0; // >0 when model itself is sending events
|
||||||
|
|
||||||
|
|
||||||
// Used to differentiate changes in valueName and other changes in the component:
|
// Used to differentiate changes in valueName and other changes in the component:
|
||||||
private double lastValue = 0;
|
private double lastValue = 0;
|
||||||
private boolean lastAutomatic = false;
|
private boolean lastAutomatic = false;
|
||||||
|
|
||||||
private Invalidator invalidator = new Invalidator(this);
|
private Invalidator invalidator = new Invalidator(this);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a DoubleModel that contains an internal double value.
|
* Generate a DoubleModel that contains an internal double value.
|
||||||
*
|
*
|
||||||
@ -566,7 +573,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public DoubleModel(double value) {
|
public DoubleModel(double value) {
|
||||||
this(value, UnitGroup.UNITS_NONE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
this(value, UnitGroup.UNITS_NONE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a DoubleModel that contains an internal double value.
|
* Generate a DoubleModel that contains an internal double value.
|
||||||
*
|
*
|
||||||
@ -576,7 +583,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public DoubleModel(double value, UnitGroup unit) {
|
public DoubleModel(double value, UnitGroup unit) {
|
||||||
this(value, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
this(value, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a DoubleModel that contains an internal double value.
|
* Generate a DoubleModel that contains an internal double value.
|
||||||
*
|
*
|
||||||
@ -587,7 +594,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public DoubleModel(double value, UnitGroup unit, double min) {
|
public DoubleModel(double value, UnitGroup unit, double min) {
|
||||||
this(value, unit, min, Double.POSITIVE_INFINITY);
|
this(value, unit, min, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a DoubleModel that contains an internal double value.
|
* Generate a DoubleModel that contains an internal double value.
|
||||||
*
|
*
|
||||||
@ -600,18 +607,18 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
this.lastValue = value;
|
this.lastValue = value;
|
||||||
this.minValue = min;
|
this.minValue = min;
|
||||||
this.maxValue = max;
|
this.maxValue = max;
|
||||||
|
|
||||||
source = null;
|
source = null;
|
||||||
valueName = "Constant value";
|
valueName = "Constant value";
|
||||||
multiplier = 1;
|
multiplier = 1;
|
||||||
|
|
||||||
getMethod = setMethod = null;
|
getMethod = setMethod = null;
|
||||||
getAutoMethod = setAutoMethod = null;
|
getAutoMethod = setAutoMethod = null;
|
||||||
units = unit;
|
units = unit;
|
||||||
currentUnit = units.getDefaultUnit();
|
currentUnit = units.getDefaultUnit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new DoubleModel that changes the values of the specified component.
|
* Generates a new DoubleModel that changes the values of the specified component.
|
||||||
* The double value is read and written using the methods "get"/"set" + valueName.
|
* The double value is read and written using the methods "get"/"set" + valueName.
|
||||||
@ -627,37 +634,37 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
this.source = source;
|
this.source = source;
|
||||||
this.valueName = valueName;
|
this.valueName = valueName;
|
||||||
this.multiplier = multiplier;
|
this.multiplier = multiplier;
|
||||||
|
|
||||||
this.units = unit;
|
this.units = unit;
|
||||||
currentUnit = units.getDefaultUnit();
|
currentUnit = units.getDefaultUnit();
|
||||||
|
|
||||||
this.minValue = min;
|
this.minValue = min;
|
||||||
this.maxValue = max;
|
this.maxValue = max;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
getMethod = source.getClass().getMethod("get" + valueName);
|
getMethod = source.getClass().getMethod("get" + valueName);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
throw new IllegalArgumentException("get method for value '" + valueName +
|
throw new IllegalArgumentException("get method for value '" + valueName +
|
||||||
"' not present in class " + source.getClass().getCanonicalName());
|
"' not present in class " + source.getClass().getCanonicalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
Method s = null;
|
Method s = null;
|
||||||
try {
|
try {
|
||||||
s = source.getClass().getMethod("set" + valueName, double.class);
|
s = source.getClass().getMethod("set" + valueName, double.class);
|
||||||
} catch (NoSuchMethodException e1) {
|
} catch (NoSuchMethodException e1) {
|
||||||
} // Ignore
|
} // Ignore
|
||||||
setMethod = s;
|
setMethod = s;
|
||||||
|
|
||||||
// Automatic selection methods
|
// Automatic selection methods
|
||||||
|
|
||||||
Method set = null, get = null;
|
Method set = null, get = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
get = source.getClass().getMethod("is" + valueName + "Automatic");
|
get = source.getClass().getMethod("is" + valueName + "Automatic");
|
||||||
set = source.getClass().getMethod("set" + valueName + "Automatic", boolean.class);
|
set = source.getClass().getMethod("set" + valueName + "Automatic", boolean.class);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
} // ignore
|
} // ignore
|
||||||
|
|
||||||
if (set != null && get != null) {
|
if (set != null && get != null) {
|
||||||
getAutoMethod = get;
|
getAutoMethod = get;
|
||||||
setAutoMethod = set;
|
setAutoMethod = set;
|
||||||
@ -665,45 +672,45 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
getAutoMethod = null;
|
getAutoMethod = null;
|
||||||
setAutoMethod = null;
|
setAutoMethod = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName, double multiplier, UnitGroup unit,
|
public DoubleModel(ChangeSource source, String valueName, double multiplier, UnitGroup unit,
|
||||||
double min) {
|
double min) {
|
||||||
this(source, valueName, multiplier, unit, min, Double.POSITIVE_INFINITY);
|
this(source, valueName, multiplier, unit, min, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName, double multiplier, UnitGroup unit) {
|
public DoubleModel(ChangeSource source, String valueName, double multiplier, UnitGroup unit) {
|
||||||
this(source, valueName, multiplier, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
this(source, valueName, multiplier, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName, UnitGroup unit,
|
public DoubleModel(ChangeSource source, String valueName, UnitGroup unit,
|
||||||
double min, double max) {
|
double min, double max) {
|
||||||
this(source, valueName, 1.0, unit, min, max);
|
this(source, valueName, 1.0, unit, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName, UnitGroup unit, double min) {
|
public DoubleModel(ChangeSource source, String valueName, UnitGroup unit, double min) {
|
||||||
this(source, valueName, 1.0, unit, min, Double.POSITIVE_INFINITY);
|
this(source, valueName, 1.0, unit, min, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName, UnitGroup unit) {
|
public DoubleModel(ChangeSource source, String valueName, UnitGroup unit) {
|
||||||
this(source, valueName, 1.0, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
this(source, valueName, 1.0, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName) {
|
public DoubleModel(ChangeSource source, String valueName) {
|
||||||
this(source, valueName, 1.0, UnitGroup.UNITS_NONE,
|
this(source, valueName, 1.0, UnitGroup.UNITS_NONE,
|
||||||
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName, double min) {
|
public DoubleModel(ChangeSource source, String valueName, double min) {
|
||||||
this(source, valueName, 1.0, UnitGroup.UNITS_NONE, min, Double.POSITIVE_INFINITY);
|
this(source, valueName, 1.0, UnitGroup.UNITS_NONE, min, Double.POSITIVE_INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleModel(ChangeSource source, String valueName, double min, double max) {
|
public DoubleModel(ChangeSource source, String valueName, double min, double max) {
|
||||||
this(source, valueName, 1.0, UnitGroup.UNITS_NONE, min, max);
|
this(source, valueName, 1.0, UnitGroup.UNITS_NONE, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the variable (in SI units).
|
* Returns the value of the variable (in SI units).
|
||||||
@ -711,7 +718,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public double getValue() {
|
public double getValue() {
|
||||||
if (getMethod == null) // Constant value
|
if (getMethod == null) // Constant value
|
||||||
return lastValue;
|
return lastValue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (Double) getMethod.invoke(source) * multiplier;
|
return (Double) getMethod.invoke(source) * multiplier;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@ -722,14 +729,14 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
throw Reflection.handleWrappedException(e);
|
throw Reflection.handleWrappedException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value of the variable.
|
* Sets the value of the variable.
|
||||||
* @param v New value for parameter in SI units.
|
* @param v New value for parameter in SI units.
|
||||||
*/
|
*/
|
||||||
public void setValue(double v) {
|
public void setValue(double v) {
|
||||||
checkState(true);
|
checkState(true);
|
||||||
|
|
||||||
log.debug("Setting value " + v + " for " + this);
|
log.debug("Setting value " + v + " for " + this);
|
||||||
if (setMethod == null) {
|
if (setMethod == null) {
|
||||||
if (getMethod != null) {
|
if (getMethod != null) {
|
||||||
@ -740,7 +747,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
fireStateChanged();
|
fireStateChanged();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setMethod.invoke(source, v / multiplier);
|
setMethod.invoke(source, v / multiplier);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@ -751,14 +758,14 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
throw Reflection.handleWrappedException(e);
|
throw Reflection.handleWrappedException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether setting the value automatically is available.
|
* Returns whether setting the value automatically is available.
|
||||||
*/
|
*/
|
||||||
public boolean isAutomaticAvailable() {
|
public boolean isAutomaticAvailable() {
|
||||||
return (getAutoMethod != null) && (setAutoMethod != null);
|
return (getAutoMethod != null) && (setAutoMethod != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the value is currently being set automatically.
|
* Returns whether the value is currently being set automatically.
|
||||||
* Returns false if automatic setting is not available at all.
|
* Returns false if automatic setting is not available at all.
|
||||||
@ -766,7 +773,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public boolean isAutomatic() {
|
public boolean isAutomatic() {
|
||||||
if (getAutoMethod == null)
|
if (getAutoMethod == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (Boolean) getAutoMethod.invoke(source);
|
return (Boolean) getAutoMethod.invoke(source);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@ -777,20 +784,20 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
throw Reflection.handleWrappedException(e);
|
throw Reflection.handleWrappedException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether the value should be set automatically. Simply fires a
|
* Sets whether the value should be set automatically. Simply fires a
|
||||||
* state change event if automatic setting is not available.
|
* state change event if automatic setting is not available.
|
||||||
*/
|
*/
|
||||||
public void setAutomatic(boolean auto) {
|
public void setAutomatic(boolean auto) {
|
||||||
checkState(true);
|
checkState(true);
|
||||||
|
|
||||||
if (setAutoMethod == null) {
|
if (setAutoMethod == null) {
|
||||||
log.debug("Setting automatic to " + auto + " for " + this + ", automatic not available");
|
log.debug("Setting automatic to " + auto + " for " + this + ", automatic not available");
|
||||||
fireStateChanged(); // in case something is out-of-sync
|
fireStateChanged(); // in case something is out-of-sync
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Setting automatic to " + auto + " for " + this);
|
log.debug("Setting automatic to " + auto + " for " + this);
|
||||||
lastAutomatic = auto;
|
lastAutomatic = auto;
|
||||||
try {
|
try {
|
||||||
@ -803,8 +810,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
throw Reflection.handleWrappedException(e);
|
throw Reflection.handleWrappedException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current Unit. At the beginning it is the default unit of the UnitGroup.
|
* Returns the current Unit. At the beginning it is the default unit of the UnitGroup.
|
||||||
* @return The most recently set unit.
|
* @return The most recently set unit.
|
||||||
@ -812,7 +819,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public Unit getCurrentUnit() {
|
public Unit getCurrentUnit() {
|
||||||
return currentUnit;
|
return currentUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current Unit. The unit must be one of those included in the UnitGroup.
|
* Sets the current Unit. The unit must be one of those included in the UnitGroup.
|
||||||
* @param u The unit to set active.
|
* @param u The unit to set active.
|
||||||
@ -825,8 +832,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
currentUnit = u;
|
currentUnit = u;
|
||||||
fireStateChanged();
|
fireStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the UnitGroup associated with the parameter value.
|
* Returns the UnitGroup associated with the parameter value.
|
||||||
*
|
*
|
||||||
@ -835,8 +842,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public UnitGroup getUnitGroup() {
|
public UnitGroup getUnitGroup() {
|
||||||
return units;
|
return units;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener to the model. Adds the model as a listener to the value source if this
|
* Add a listener to the model. Adds the model as a listener to the value source if this
|
||||||
@ -846,7 +853,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
@Override
|
@Override
|
||||||
public void addChangeListener(EventListener l) {
|
public void addChangeListener(EventListener l) {
|
||||||
checkState(true);
|
checkState(true);
|
||||||
|
|
||||||
if (listeners.isEmpty()) {
|
if (listeners.isEmpty()) {
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
source.addChangeListener(this);
|
source.addChangeListener(this);
|
||||||
@ -854,11 +861,11 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
lastAutomatic = isAutomatic();
|
lastAutomatic = isAutomatic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.add(l);
|
listeners.add(l);
|
||||||
log.verbose(this + " adding listener (total " + listeners.size() + "): " + l);
|
log.verbose(this + " adding listener (total " + listeners.size() + "): " + l);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a listener from the model. Removes the model from being a listener to the Component
|
* Remove a listener from the model. Removes the model from being a listener to the Component
|
||||||
* if this was the last listener of the model.
|
* if this was the last listener of the model.
|
||||||
@ -867,15 +874,15 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
@Override
|
@Override
|
||||||
public void removeChangeListener(EventListener l) {
|
public void removeChangeListener(EventListener l) {
|
||||||
checkState(false);
|
checkState(false);
|
||||||
|
|
||||||
listeners.remove(l);
|
listeners.remove(l);
|
||||||
if (listeners.isEmpty() && source != null) {
|
if (listeners.isEmpty() && source != null) {
|
||||||
source.removeChangeListener(this);
|
source.removeChangeListener(this);
|
||||||
}
|
}
|
||||||
log.verbose(this + " removing listener (total " + listeners.size() + "): " + l);
|
log.verbose(this + " removing listener (total " + listeners.size() + "): " + l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidates this model by removing all listeners and removing this from
|
* Invalidates this model by removing all listeners and removing this from
|
||||||
* listening to the source. After invalidation no listeners can be added to this
|
* listening to the source. After invalidation no listeners can be added to this
|
||||||
@ -885,7 +892,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
log.verbose("Invalidating " + this);
|
log.verbose("Invalidating " + this);
|
||||||
invalidator.invalidate();
|
invalidator.invalidate();
|
||||||
|
|
||||||
if (!listeners.isEmpty()) {
|
if (!listeners.isEmpty()) {
|
||||||
log.warn("Invalidating " + this + " while still having listeners " + listeners);
|
log.warn("Invalidating " + this + " while still having listeners " + listeners);
|
||||||
}
|
}
|
||||||
@ -895,13 +902,13 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
}
|
}
|
||||||
MemoryManagement.collectable(this);
|
MemoryManagement.collectable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void checkState(boolean error) {
|
private void checkState(boolean error) {
|
||||||
invalidator.check(error);
|
invalidator.check(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
super.finalize();
|
super.finalize();
|
||||||
@ -909,14 +916,14 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
log.warn(this + " being garbage-collected while having listeners " + listeners);
|
log.warn(this + " being garbage-collected while having listeners " + listeners);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fire a ChangeEvent to all listeners.
|
* Fire a ChangeEvent to all listeners.
|
||||||
*/
|
*/
|
||||||
protected void fireStateChanged() {
|
protected void fireStateChanged() {
|
||||||
checkState(true);
|
checkState(true);
|
||||||
|
|
||||||
EventObject event = new EventObject(this);
|
EventObject event = new EventObject(this);
|
||||||
ChangeEvent cevent = new ChangeEvent(this);
|
ChangeEvent cevent = new ChangeEvent(this);
|
||||||
firing++;
|
firing++;
|
||||||
@ -931,7 +938,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
}
|
}
|
||||||
firing--;
|
firing--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the component changes. Checks whether the modeled value has changed, and if
|
* Called when the component changes. Checks whether the modeled value has changed, and if
|
||||||
* it has, updates lastValue and generates ChangeEvents for all listeners of the model.
|
* it has, updates lastValue and generates ChangeEvents for all listeners of the model.
|
||||||
@ -939,7 +946,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
@Override
|
@Override
|
||||||
public void stateChanged(EventObject e) {
|
public void stateChanged(EventObject e) {
|
||||||
checkState(true);
|
checkState(true);
|
||||||
|
|
||||||
double v = getValue();
|
double v = getValue();
|
||||||
boolean b = isAutomatic();
|
boolean b = isAutomatic();
|
||||||
if (lastValue == v && lastAutomatic == b)
|
if (lastValue == v && lastAutomatic == b)
|
||||||
@ -948,8 +955,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
|
|||||||
lastAutomatic = b;
|
lastAutomatic = b;
|
||||||
fireStateChanged();
|
fireStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Explain the DoubleModel as a String.
|
* Explain the DoubleModel as a String.
|
||||||
*/
|
*/
|
||||||
|
@ -3,19 +3,11 @@
|
|||||||
*/
|
*/
|
||||||
package net.sf.openrocket.gui.print.visitor;
|
package net.sf.openrocket.gui.print.visitor;
|
||||||
|
|
||||||
import com.itextpdf.text.Chunk;
|
import java.util.List;
|
||||||
import com.itextpdf.text.Document;
|
import java.util.Set;
|
||||||
import com.itextpdf.text.DocumentException;
|
|
||||||
import com.itextpdf.text.Element;
|
import javax.swing.ImageIcon;
|
||||||
import com.itextpdf.text.Font;
|
|
||||||
import com.itextpdf.text.Image;
|
|
||||||
import com.itextpdf.text.Paragraph;
|
|
||||||
import com.itextpdf.text.Phrase;
|
|
||||||
import com.itextpdf.text.Rectangle;
|
|
||||||
import com.itextpdf.text.pdf.PdfPCell;
|
|
||||||
import com.itextpdf.text.pdf.PdfPTable;
|
|
||||||
import com.itextpdf.text.pdf.PdfWriter;
|
|
||||||
import com.itextpdf.text.pdf.draw.VerticalPositionMark;
|
|
||||||
import net.sf.openrocket.gui.main.ComponentIcons;
|
import net.sf.openrocket.gui.main.ComponentIcons;
|
||||||
import net.sf.openrocket.gui.print.ITextHelper;
|
import net.sf.openrocket.gui.print.ITextHelper;
|
||||||
import net.sf.openrocket.gui.print.PrintUtilities;
|
import net.sf.openrocket.gui.print.PrintUtilities;
|
||||||
@ -45,13 +37,20 @@ import net.sf.openrocket.rocketcomponent.Transition;
|
|||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.unit.Unit;
|
import net.sf.openrocket.unit.Unit;
|
||||||
import net.sf.openrocket.unit.UnitGroup;
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
import net.sf.openrocket.util.Coordinate;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import com.itextpdf.text.Chunk;
|
||||||
import java.text.NumberFormat;
|
import com.itextpdf.text.Document;
|
||||||
import java.util.Collection;
|
import com.itextpdf.text.DocumentException;
|
||||||
import java.util.List;
|
import com.itextpdf.text.Element;
|
||||||
import java.util.Set;
|
import com.itextpdf.text.Font;
|
||||||
|
import com.itextpdf.text.Image;
|
||||||
|
import com.itextpdf.text.Paragraph;
|
||||||
|
import com.itextpdf.text.Phrase;
|
||||||
|
import com.itextpdf.text.Rectangle;
|
||||||
|
import com.itextpdf.text.pdf.PdfPCell;
|
||||||
|
import com.itextpdf.text.pdf.PdfPTable;
|
||||||
|
import com.itextpdf.text.pdf.PdfWriter;
|
||||||
|
import com.itextpdf.text.pdf.draw.VerticalPositionMark;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A visitor strategy for creating documentation about parts details.
|
* A visitor strategy for creating documentation about parts details.
|
||||||
@ -539,8 +538,6 @@ public class PartsDetailVisitorStrategy {
|
|||||||
Image img = null;
|
Image img = null;
|
||||||
java.awt.Image awtImage = new PrintableFinSet(theFinSet).createImage();
|
java.awt.Image awtImage = new PrintableFinSet(theFinSet).createImage();
|
||||||
|
|
||||||
Collection<Coordinate> x = theFinSet.getComponentBounds();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
img = Image.getInstance(writer, awtImage, 0.25f);
|
img = Image.getInstance(writer, awtImage, 0.25f);
|
||||||
}
|
}
|
||||||
@ -714,7 +711,7 @@ public class PartsDetailVisitorStrategy {
|
|||||||
*/
|
*/
|
||||||
protected String toLength (double length) {
|
protected String toLength (double length) {
|
||||||
final Unit defaultUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
|
final Unit defaultUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
|
||||||
return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(length)) + defaultUnit.toString();
|
return defaultUnit.toStringUnit(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -726,7 +723,7 @@ public class PartsDetailVisitorStrategy {
|
|||||||
*/
|
*/
|
||||||
protected String toMass (double mass) {
|
protected String toMass (double mass) {
|
||||||
final Unit defaultUnit = UnitGroup.UNITS_MASS.getDefaultUnit();
|
final Unit defaultUnit = UnitGroup.UNITS_MASS.getDefaultUnit();
|
||||||
return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(mass)) + defaultUnit.toString();
|
return defaultUnit.toStringUnit(mass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
188
core/src/net/sf/openrocket/unit/FractionalUnit.java
Normal file
188
core/src/net/sf/openrocket/unit/FractionalUnit.java
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package net.sf.openrocket.unit;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class FractionalUnit extends Unit {
|
||||||
|
|
||||||
|
// This is the base of the fractions. ie, 16d for 1/16ths.
|
||||||
|
private final int fractionBase;
|
||||||
|
// This is 1d/fractionBase;
|
||||||
|
private final double fractionValue;
|
||||||
|
|
||||||
|
// This is the value used when incrementing/decrementing.
|
||||||
|
private final double incrementValue;
|
||||||
|
|
||||||
|
// If the actual value differs from the decimal representation by more than this,
|
||||||
|
// we display as decimals.
|
||||||
|
private final double epsilon;
|
||||||
|
|
||||||
|
private final String unitLabel;
|
||||||
|
|
||||||
|
public FractionalUnit(double multiplier, String unit, String unitLabel, int fractionBase, double incrementValue) {
|
||||||
|
this( multiplier, unit, unitLabel, fractionBase, incrementValue, 0.1d/fractionBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FractionalUnit(double multiplier, String unit, String unitLabel, int fractionBase, double incrementValue, double epsilon) {
|
||||||
|
super(multiplier, unit);
|
||||||
|
this.unitLabel = unitLabel;
|
||||||
|
this.fractionBase = fractionBase;
|
||||||
|
this.fractionValue = 1.0d/fractionBase;
|
||||||
|
this.incrementValue = incrementValue;
|
||||||
|
this.epsilon = epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double round(double value) {
|
||||||
|
return roundTo( value, fractionValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
private double roundTo( double value, double fraction ) {
|
||||||
|
double remainder = Math.IEEEremainder( value, fraction );
|
||||||
|
return value - remainder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getNextValue(double value) {
|
||||||
|
double rounded = roundTo(value, incrementValue);
|
||||||
|
if ( rounded <= value + epsilon) {
|
||||||
|
rounded += incrementValue;
|
||||||
|
}
|
||||||
|
return rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getPreviousValue(double value) {
|
||||||
|
double rounded = roundTo(value, incrementValue);
|
||||||
|
if ( rounded >= value - epsilon ) {
|
||||||
|
rounded -= incrementValue;
|
||||||
|
}
|
||||||
|
return rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tick[] getTicks(double start, double end, double minor, double major) {
|
||||||
|
// Convert values
|
||||||
|
start = toUnit(start);
|
||||||
|
end = toUnit(end);
|
||||||
|
minor = toUnit(minor);
|
||||||
|
major = toUnit(major);
|
||||||
|
|
||||||
|
if (minor <= 0 || major <= 0 || major < minor) {
|
||||||
|
throw new IllegalArgumentException("getTicks called with minor="+minor+" major="+major);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Tick> ticks = new ArrayList<Tick>();
|
||||||
|
|
||||||
|
int mod2,mod3,mod4; // Moduli for minor-notable, major-nonnotable, major-notable
|
||||||
|
double minstep;
|
||||||
|
|
||||||
|
// Find the smallest possible step size
|
||||||
|
double one=1;
|
||||||
|
while (one > minor)
|
||||||
|
one /= 2;
|
||||||
|
while (one < minor)
|
||||||
|
one *= 2;
|
||||||
|
minstep = one;
|
||||||
|
mod2 = 16;
|
||||||
|
|
||||||
|
// Find step size for major ticks
|
||||||
|
one = 1;
|
||||||
|
while (one > major)
|
||||||
|
one /= 10;
|
||||||
|
while (one < major)
|
||||||
|
one *= 10;
|
||||||
|
if (one/2 >= major) {
|
||||||
|
// major step is round-five, major-notable is next round-ten
|
||||||
|
double majorstep = one/2;
|
||||||
|
mod3 = (int)Math.round(majorstep/minstep);
|
||||||
|
mod4 = mod3*2;
|
||||||
|
} else {
|
||||||
|
// major step is round-ten, major-notable is next round-ten
|
||||||
|
mod3 = (int)Math.round(one/minstep);
|
||||||
|
mod4 = mod3*10;
|
||||||
|
}
|
||||||
|
// Check for clashes between minor-notable and major-nonnotable
|
||||||
|
if (mod3 == mod2) {
|
||||||
|
if (mod2==2)
|
||||||
|
mod2 = 1; // Every minor tick is notable
|
||||||
|
else
|
||||||
|
mod2 = 5; // Every fifth minor tick is notable
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate starting position
|
||||||
|
int pos = (int)Math.ceil(start/minstep);
|
||||||
|
// System.out.println("mod2="+mod2+" mod3="+mod3+" mod4="+mod4);
|
||||||
|
while (pos*minstep <= end) {
|
||||||
|
double unitValue = pos*minstep;
|
||||||
|
double value = fromUnit(unitValue);
|
||||||
|
|
||||||
|
if (pos%mod4 == 0)
|
||||||
|
ticks.add(new Tick(value,unitValue,true,true));
|
||||||
|
else if (pos%mod3 == 0)
|
||||||
|
ticks.add(new Tick(value,unitValue,true,false));
|
||||||
|
else if (pos%mod2 == 0)
|
||||||
|
ticks.add(new Tick(value,unitValue,false,true));
|
||||||
|
else
|
||||||
|
ticks.add(new Tick(value,unitValue,false,false));
|
||||||
|
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ticks.toArray(new Tick[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(double value) {
|
||||||
|
|
||||||
|
double correctVal = toUnit(value);
|
||||||
|
double val = round(correctVal);
|
||||||
|
|
||||||
|
|
||||||
|
if ( Math.abs( val - correctVal ) > epsilon ) {
|
||||||
|
NumberFormat decFormat = new DecimalFormat("#.###");
|
||||||
|
return decFormat.format(correctVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberFormat intFormat = new DecimalFormat("#");
|
||||||
|
double sign = Math.signum(val);
|
||||||
|
|
||||||
|
double posValue = sign * val;
|
||||||
|
|
||||||
|
double intPart = Math.floor(posValue);
|
||||||
|
|
||||||
|
double frac = Math.rint((posValue - intPart)/fractionValue);
|
||||||
|
double fracBase = fractionBase;
|
||||||
|
|
||||||
|
// Reduce fraction.
|
||||||
|
while ( frac > 0 && fracBase > 2 && frac % 2 == 0 ) {
|
||||||
|
frac /= 2.0;
|
||||||
|
fracBase /= 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
posValue *= sign;
|
||||||
|
|
||||||
|
if ( frac == 0.0 ) {
|
||||||
|
return intFormat.format(posValue);
|
||||||
|
} else if (intPart == 0.0 ){
|
||||||
|
return intFormat.format(sign*frac) + "/" + intFormat.format(fracBase);
|
||||||
|
} else {
|
||||||
|
return intFormat.format(sign*intPart) + " " + intFormat.format(frac) + "/" + intFormat.format(fracBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toStringUnit(double value) {
|
||||||
|
if (Double.isNaN(value))
|
||||||
|
return "N/A";
|
||||||
|
|
||||||
|
String s = toString(value);
|
||||||
|
s += " " + unitLabel;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -84,6 +84,7 @@ public class UnitGroup {
|
|||||||
UNITS_LENGTH.addUnit(new GeneralUnit(0.01, "cm"));
|
UNITS_LENGTH.addUnit(new GeneralUnit(0.01, "cm"));
|
||||||
UNITS_LENGTH.addUnit(new GeneralUnit(1, "m"));
|
UNITS_LENGTH.addUnit(new GeneralUnit(1, "m"));
|
||||||
UNITS_LENGTH.addUnit(new GeneralUnit(0.0254, "in"));
|
UNITS_LENGTH.addUnit(new GeneralUnit(0.0254, "in"));
|
||||||
|
UNITS_LENGTH.addUnit(new FractionalUnit(0.0254, "in/64", "in", 64, 1d/16d));
|
||||||
UNITS_LENGTH.addUnit(new GeneralUnit(0.3048, "ft"));
|
UNITS_LENGTH.addUnit(new GeneralUnit(0.3048, "ft"));
|
||||||
UNITS_LENGTH.setDefaultUnit(1);
|
UNITS_LENGTH.setDefaultUnit(1);
|
||||||
|
|
||||||
|
53
core/src/net/sf/openrocket/util/FractionUtil.java
Normal file
53
core/src/net/sf/openrocket/util/FractionUtil.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package net.sf.openrocket.util;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public abstract class FractionUtil {
|
||||||
|
|
||||||
|
|
||||||
|
private final static Pattern fractionPattern = Pattern.compile("(-?\\d+)/(\\d+)");
|
||||||
|
private final static Pattern mixedFractionPattern = Pattern.compile("(-?\\d+)\\s+(\\d+)/(\\d+)");
|
||||||
|
/**
|
||||||
|
* Parse a double from a string supporting fraction formats.
|
||||||
|
*
|
||||||
|
* Will parse fractions specified with '/'. Mixed numbers separated by ' ' (space).
|
||||||
|
* If no fraction is found in the input string, it is parsed with Double.parseDouble()
|
||||||
|
* which my throw the runtime exception java.lang.NumberFormatException.
|
||||||
|
*
|
||||||
|
* Valid input may look like:
|
||||||
|
*
|
||||||
|
* "1/4" = 0.25d
|
||||||
|
* "-1/4" = -0.25d
|
||||||
|
* "1 1/4" = 1.25d
|
||||||
|
* "-1 1/4" = 1.25d
|
||||||
|
* "1.25" = 1.25d
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Double parseFraction( String str ) {
|
||||||
|
|
||||||
|
if ( str == null ) {
|
||||||
|
throw new java.lang.NumberFormatException("null String");
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher m1 = mixedFractionPattern.matcher(str);
|
||||||
|
if ( m1.find() ) {
|
||||||
|
double wholepart = Double.parseDouble(m1.group(1));
|
||||||
|
double num = Double.parseDouble(m1.group(2));
|
||||||
|
double den = Double.parseDouble(m1.group(3));
|
||||||
|
return wholepart + Math.copySign(num,wholepart) / den;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher m2 = fractionPattern.matcher(str);
|
||||||
|
if( m2.find() ) {
|
||||||
|
double num = Double.parseDouble(m2.group(1));
|
||||||
|
double den = Double.parseDouble(m2.group(2));
|
||||||
|
return num / den;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Double.parseDouble(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
206
core/test/net/sf/openrocket/unit/FractionalUnitTest.java
Normal file
206
core/test/net/sf/openrocket/unit/FractionalUnitTest.java
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package net.sf.openrocket.unit;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class FractionalUnitTest {
|
||||||
|
|
||||||
|
private final static Unit testUnit = new FractionalUnit(1, "unit", "unit", 4, 0.5);
|
||||||
|
private final static Unit testUnitApprox = new FractionalUnit(1, "unit", "unit", 16, 0.5, 0.02);
|
||||||
|
|
||||||
|
private final static Unit inchUnit = new FractionalUnit(0.0254, "in/64", "in", 64, 1d/16d);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRound() {
|
||||||
|
|
||||||
|
assertEquals(-1d, testUnit.round(-1.125), 0.0); // rounds to -1 since mod is even
|
||||||
|
assertEquals(-1d, testUnit.round(-1.0), 0.0);
|
||||||
|
assertEquals(-1d, testUnit.round(-.875), 0.0); // rounds to -1 since mod is even
|
||||||
|
|
||||||
|
assertEquals(-0.75d, testUnit.round(-.874), 0.0);
|
||||||
|
assertEquals(-0.75d, testUnit.round(-.75), 0.0);
|
||||||
|
assertEquals(-0.75d, testUnit.round(-.626), 0.0);
|
||||||
|
|
||||||
|
assertEquals(-0.5d, testUnit.round(-.625), 0.0); // rounds to -.5 since mod is even
|
||||||
|
assertEquals(-0.5d, testUnit.round(-.5), 0.0);
|
||||||
|
assertEquals(-0.5d, testUnit.round(-.375), 0.0); // rounds to -.5 since mod is even
|
||||||
|
|
||||||
|
assertEquals(-0.25d, testUnit.round(-.374), 0.0);
|
||||||
|
assertEquals(-0.25d, testUnit.round(-.25), 0.0);
|
||||||
|
assertEquals(-0.25d, testUnit.round(-.126), 0.0);
|
||||||
|
|
||||||
|
assertEquals(0d, testUnit.round(-.125), 0.0);
|
||||||
|
assertEquals(0d, testUnit.round(0), 0.0);
|
||||||
|
assertEquals(0d, testUnit.round(.125), 0.0);
|
||||||
|
|
||||||
|
assertEquals(0.25d, testUnit.round(.126), 0.0);
|
||||||
|
assertEquals(0.25d, testUnit.round(.25), 0.0);
|
||||||
|
assertEquals(0.25d, testUnit.round(.374), 0.0);
|
||||||
|
|
||||||
|
assertEquals(0.5d, testUnit.round(.375), 0.0); // rounds to .5 since mod is even
|
||||||
|
assertEquals(0.5d, testUnit.round(.5), 0.0);
|
||||||
|
assertEquals(0.5d, testUnit.round(.625), 0.0); // rounds to .5 since mod is even
|
||||||
|
|
||||||
|
assertEquals(0.75d, testUnit.round(.626), 0.0);
|
||||||
|
assertEquals(0.75d, testUnit.round(.75), 0.0);
|
||||||
|
assertEquals(0.75d, testUnit.round(.874), 0.0);
|
||||||
|
|
||||||
|
assertEquals(1d, testUnit.round(.875), 0.0); // rounds to 1 since mod is even
|
||||||
|
assertEquals(1d, testUnit.round(1.0), 0.0);
|
||||||
|
assertEquals(1d, testUnit.round(1.125), 0.0); // rounds to 1 since mod is even
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncrement() {
|
||||||
|
|
||||||
|
assertEquals( -1d, testUnit.getNextValue(-1.2), 0.0);
|
||||||
|
assertEquals( -1d, testUnit.getNextValue(-1.4), 0.0);
|
||||||
|
|
||||||
|
assertEquals( -0.5d, testUnit.getNextValue(-0.7), 0.0);
|
||||||
|
assertEquals( -0.5d, testUnit.getNextValue(-0.9), 0.0);
|
||||||
|
assertEquals( -0.5d, testUnit.getNextValue(-1.0), 0.0);
|
||||||
|
|
||||||
|
assertEquals( 0.0d, testUnit.getNextValue(-0.05), 0.0 );
|
||||||
|
assertEquals( 0.0d, testUnit.getNextValue(-0.062), 0.0 );
|
||||||
|
assertEquals( 0.0d, testUnit.getNextValue(-0.07), 0.0 );
|
||||||
|
assertEquals( 0.0d, testUnit.getNextValue(-0.11), 0.0 );
|
||||||
|
|
||||||
|
assertEquals( 0.5d, testUnit.getNextValue(0), 0.0 );
|
||||||
|
assertEquals( 0.5d, testUnit.getNextValue(0.01), 0.0 );
|
||||||
|
assertEquals( 0.5d, testUnit.getNextValue(0.062), 0.0 );
|
||||||
|
assertEquals( 0.5d, testUnit.getNextValue(0.0625), 0.0);
|
||||||
|
|
||||||
|
assertEquals( 1d, testUnit.getNextValue(0.51), 0.0);
|
||||||
|
assertEquals( 1d, testUnit.getNextValue(0.7), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecrement() {
|
||||||
|
|
||||||
|
assertEquals( -1.5d, testUnit.getPreviousValue(-1.2), 0.0);
|
||||||
|
assertEquals( -1.5d, testUnit.getPreviousValue(-1.4), 0.0);
|
||||||
|
assertEquals( -1.5d, testUnit.getPreviousValue(-1.0), 0.0);
|
||||||
|
|
||||||
|
assertEquals( -1d, testUnit.getPreviousValue(-0.7), 0.0);
|
||||||
|
assertEquals( -1d, testUnit.getPreviousValue(-0.9), 0.0);
|
||||||
|
|
||||||
|
assertEquals( -0.5d, testUnit.getPreviousValue(-0.01), 0.0 );
|
||||||
|
assertEquals( -0.5d, testUnit.getPreviousValue(-0.05), 0.0 );
|
||||||
|
assertEquals( -0.5d, testUnit.getPreviousValue(-0.062), 0.0 );
|
||||||
|
assertEquals( -0.5d, testUnit.getPreviousValue(-0.07), 0.0 );
|
||||||
|
assertEquals( -0.5d, testUnit.getPreviousValue(0), 0.0 );
|
||||||
|
|
||||||
|
assertEquals( 0.0d, testUnit.getPreviousValue(0.49), 0.0 );
|
||||||
|
assertEquals( 0.0d, testUnit.getPreviousValue(0.262), 0.0 );
|
||||||
|
assertEquals( 0.0d, testUnit.getPreviousValue(0.51), 0.0);
|
||||||
|
|
||||||
|
assertEquals( 0.5d, testUnit.getPreviousValue(0.7), 0.0);
|
||||||
|
|
||||||
|
assertEquals( 1.0d, testUnit.getPreviousValue(1.2), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToStringDefaultPrecision() {
|
||||||
|
|
||||||
|
// default epsilon is 0.025
|
||||||
|
assertEquals("-1.2", testUnit.toString(-1.2));
|
||||||
|
assertEquals("-1 1/4", testUnit.toString(-1.225));
|
||||||
|
assertEquals("-1 1/4", testUnit.toString(-1.227));
|
||||||
|
assertEquals("-1 1/4", testUnit.toString(-1.25));
|
||||||
|
assertEquals("-1 1/4", testUnit.toString(-1.25));
|
||||||
|
assertEquals("-1 1/4", testUnit.toString(-1.275));
|
||||||
|
assertEquals("-1.3", testUnit.toString(-1.3));
|
||||||
|
|
||||||
|
assertEquals("-0.2", testUnit.toString(-.2));
|
||||||
|
assertEquals("-1/4", testUnit.toString(-.225));
|
||||||
|
assertEquals("-1/4", testUnit.toString(-.25));
|
||||||
|
assertEquals("-1/4", testUnit.toString(-.274));
|
||||||
|
//assertEquals("-1/4", testUnit.toString(-.275)); // this has roundoff error which pushes it over epsilon
|
||||||
|
assertEquals("-0.3", testUnit.toString(-.3));
|
||||||
|
|
||||||
|
assertEquals("-0.1", testUnit.toString(-.1));
|
||||||
|
assertEquals("0", testUnit.toString(-0.024));
|
||||||
|
assertEquals("0", testUnit.toString(0));
|
||||||
|
assertEquals("0", testUnit.toString(.024));
|
||||||
|
assertEquals("0.1", testUnit.toString(.1));
|
||||||
|
|
||||||
|
assertEquals("0.2", testUnit.toString(.2));
|
||||||
|
assertEquals("1/4", testUnit.toString(.225));
|
||||||
|
assertEquals("1/4", testUnit.toString(.25));
|
||||||
|
assertEquals("1/4", testUnit.toString(.274));
|
||||||
|
assertEquals("0.3", testUnit.toString(.3));
|
||||||
|
|
||||||
|
assertEquals("1.2", testUnit.toString(1.2));
|
||||||
|
assertEquals("1 1/4", testUnit.toString(1.225));
|
||||||
|
assertEquals("1 1/4", testUnit.toString(1.25));
|
||||||
|
assertEquals("1 1/4", testUnit.toString(1.275));
|
||||||
|
assertEquals("1.3", testUnit.toString(1.3));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToStringWithPrecision() {
|
||||||
|
|
||||||
|
// epsilon is .02
|
||||||
|
assertEquals("-1 3/16", testUnitApprox.toString(-1.2));
|
||||||
|
assertEquals("-1.225", testUnitApprox.toString(-1.225));
|
||||||
|
assertEquals("-1 1/4", testUnitApprox.toString(-1.25));
|
||||||
|
assertEquals("-1.275", testUnitApprox.toString(-1.275));
|
||||||
|
assertEquals("-1 5/16", testUnitApprox.toString(-1.3));
|
||||||
|
|
||||||
|
assertEquals("-3/16", testUnitApprox.toString(-.2));
|
||||||
|
assertEquals("-0.225", testUnitApprox.toString(-.225));
|
||||||
|
assertEquals("-1/4", testUnitApprox.toString(-.25));
|
||||||
|
assertEquals("-0.275", testUnitApprox.toString(-.275));
|
||||||
|
assertEquals("-5/16", testUnitApprox.toString(-.3));
|
||||||
|
|
||||||
|
assertEquals("-0.1", testUnitApprox.toString(-.1));
|
||||||
|
assertEquals("-0.024", testUnitApprox.toString(-0.024));
|
||||||
|
assertEquals("0", testUnitApprox.toString(0));
|
||||||
|
assertEquals("0.024", testUnitApprox.toString(.024));
|
||||||
|
assertEquals("0.1", testUnitApprox.toString(.1));
|
||||||
|
|
||||||
|
assertEquals("3/16", testUnitApprox.toString(.2));
|
||||||
|
assertEquals("0.225", testUnitApprox.toString(.225));
|
||||||
|
assertEquals("1/4", testUnitApprox.toString(.25));
|
||||||
|
assertEquals("0.275", testUnitApprox.toString(.275));
|
||||||
|
assertEquals("5/16", testUnitApprox.toString(.3));
|
||||||
|
|
||||||
|
assertEquals("1 3/16", testUnitApprox.toString(1.2));
|
||||||
|
assertEquals("1.225", testUnitApprox.toString(1.225));
|
||||||
|
assertEquals("1 1/4", testUnitApprox.toString(1.25));
|
||||||
|
assertEquals("1.275", testUnitApprox.toString(1.275));
|
||||||
|
assertEquals("1 5/16", testUnitApprox.toString(1.3));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInchToString() {
|
||||||
|
|
||||||
|
// Just some random test points.
|
||||||
|
assertEquals( "1/64", inchUnit.toString( 1d/64d*0.0254));
|
||||||
|
|
||||||
|
assertEquals( "-5/64", inchUnit.toString( -5d/64d*0.0254));
|
||||||
|
|
||||||
|
assertEquals( "4 1/2", inchUnit.toString( 9d/2d*0.0254));
|
||||||
|
|
||||||
|
assertEquals( "0.002", inchUnit.toString( 0.002*0.0254));
|
||||||
|
|
||||||
|
// default body tube length:
|
||||||
|
double length = 8d * 0.025;
|
||||||
|
|
||||||
|
assertEquals( "7 7/8", inchUnit.toString( length) );
|
||||||
|
|
||||||
|
// had problems with roundoff in decrement.
|
||||||
|
|
||||||
|
double v = inchUnit.toUnit(length);
|
||||||
|
for ( int i = 0; i< 15; i++ ) {
|
||||||
|
assertTrue( v > inchUnit.getPreviousValue(v) );
|
||||||
|
v = inchUnit.getPreviousValue(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
58
core/test/net/sf/openrocket/util/FractionUtilTest.java
Normal file
58
core/test/net/sf/openrocket/util/FractionUtilTest.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package net.sf.openrocket.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class FractionUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseFractions() {
|
||||||
|
|
||||||
|
// zeros
|
||||||
|
assertEquals( 0.0d, FractionUtil.parseFraction("0"), 0.0);
|
||||||
|
assertEquals( 0.0d, FractionUtil.parseFraction("-0"), 0.0);
|
||||||
|
assertEquals( 0.0d, FractionUtil.parseFraction("0/2"), 0.0);
|
||||||
|
assertEquals( 0.0d, FractionUtil.parseFraction("-0/2"), 0.0);
|
||||||
|
assertEquals( 0.0d, FractionUtil.parseFraction("0 0/4"), 0.0);
|
||||||
|
assertEquals( 0.0d, FractionUtil.parseFraction("0 -0/4"), 0.0);
|
||||||
|
|
||||||
|
// Simple fraction.
|
||||||
|
assertEquals( 0.25, FractionUtil.parseFraction("1/4"),0.0);
|
||||||
|
|
||||||
|
// ignores leading & trailing spaces
|
||||||
|
assertEquals( 0.25, FractionUtil.parseFraction(" 1/4 "),0.0);
|
||||||
|
|
||||||
|
// non reduced fraction
|
||||||
|
assertEquals( 0.25, FractionUtil.parseFraction("2/8"),0.0);
|
||||||
|
|
||||||
|
// negative number
|
||||||
|
assertEquals( -0.25, FractionUtil.parseFraction("-1/4"),0.0);
|
||||||
|
|
||||||
|
// improper fraction
|
||||||
|
assertEquals( 1.75, FractionUtil.parseFraction("7/4"),0.0);
|
||||||
|
|
||||||
|
// negative improper fraction
|
||||||
|
assertEquals( -1.75, FractionUtil.parseFraction("-7/4"),0.0);
|
||||||
|
|
||||||
|
// two digit numerator & denominators
|
||||||
|
assertEquals( 11d/16d, FractionUtil.parseFraction("11/16)"),0.0);
|
||||||
|
assertEquals( -11d/16d, FractionUtil.parseFraction("-11/16)"),0.0);
|
||||||
|
|
||||||
|
// Mixed fractions
|
||||||
|
assertEquals( 1.25d, FractionUtil.parseFraction("1 1/4"),0.0);
|
||||||
|
assertEquals( -1.25d, FractionUtil.parseFraction("-1 1/4"),0.0);
|
||||||
|
|
||||||
|
// extra spaces
|
||||||
|
assertEquals( 1.25d, FractionUtil.parseFraction(" 1 1/4"),0.0);
|
||||||
|
assertEquals( 1.25d, FractionUtil.parseFraction("1 1/4"),0.0);
|
||||||
|
assertEquals( 1.25d, FractionUtil.parseFraction("1 1/4 "),0.0);
|
||||||
|
|
||||||
|
assertEquals( 2.75d, FractionUtil.parseFraction("2 3/4"),0.0);
|
||||||
|
assertEquals( 15.75d, FractionUtil.parseFraction("15 3/4"),0.0);
|
||||||
|
assertEquals( 2.75d, FractionUtil.parseFraction("1 7/4"),0.0);
|
||||||
|
|
||||||
|
assertEquals( 69d/64d, FractionUtil.parseFraction("1 5/64"),0.0);
|
||||||
|
assertEquals( -69d/64d, FractionUtil.parseFraction("-1 5/64"),0.0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user