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:
Kevin Ruland 2012-05-10 02:48:07 +00:00
parent 7b093f79f8
commit bd680c7e14
8 changed files with 705 additions and 195 deletions

View File

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

View File

@ -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;
@ -57,11 +58,11 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
* 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
@ -72,7 +73,19 @@ 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);
@ -105,8 +118,7 @@ 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);
@ -116,8 +128,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
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);
@ -144,10 +155,6 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
return new ValueSpinnerModel(); return new ValueSpinnerModel();
} }
//////////// JSlider model //////////// //////////// JSlider model ////////////
private class ValueSliderModel implements BoundedRangeModel, StateChangeListener, Invalidatable { private class ValueSliderModel implements BoundedRangeModel, StateChangeListener, Invalidatable {

View File

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

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

View File

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

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

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

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