1 package net.sf.openrocket.unit;
3 import java.text.DecimalFormat;
4 import java.text.NumberFormat;
5 import java.util.ArrayList;
7 public class FractionalUnit extends Unit {
9 // This is the base of the fractions. ie, 16d for 1/16ths.
10 private final int fractionBase;
11 // This is 1d/fractionBase;
12 private final double fractionValue;
14 // This is the value used when incrementing/decrementing.
15 private final double incrementValue;
17 // If the actual value differs from the decimal representation by more than this,
18 // we display as decimals.
19 private final double epsilon;
21 private final String unitLabel;
23 public FractionalUnit(double multiplier, String unit, String unitLabel, int fractionBase, double incrementValue) {
24 this( multiplier, unit, unitLabel, fractionBase, incrementValue, 0.1d/fractionBase);
27 public FractionalUnit(double multiplier, String unit, String unitLabel, int fractionBase, double incrementValue, double epsilon) {
28 super(multiplier, unit);
29 this.unitLabel = unitLabel;
30 this.fractionBase = fractionBase;
31 this.fractionValue = 1.0d/fractionBase;
32 this.incrementValue = incrementValue;
33 this.epsilon = epsilon;
37 public double round(double value) {
38 return roundTo( value, fractionValue );
41 private double roundTo( double value, double fraction ) {
42 double remainder = Math.IEEEremainder( value, fraction );
43 return value - remainder;
47 public double getNextValue(double value) {
48 double rounded = roundTo(value, incrementValue);
49 if ( rounded <= value + epsilon) {
50 rounded += incrementValue;
56 public double getPreviousValue(double value) {
57 double rounded = roundTo(value, incrementValue);
58 if ( rounded >= value - epsilon ) {
59 rounded -= incrementValue;
65 public Tick[] getTicks(double start, double end, double minor, double major) {
67 start = toUnit(start);
69 minor = toUnit(minor);
70 major = toUnit(major);
72 if (minor <= 0 || major <= 0 || major < minor) {
73 throw new IllegalArgumentException("getTicks called with minor="+minor+" major="+major);
76 ArrayList<Tick> ticks = new ArrayList<Tick>();
78 int mod2,mod3,mod4; // Moduli for minor-notable, major-nonnotable, major-notable
81 // Find the smallest possible step size
90 // Find step size for major ticks
97 // major step is round-five, major-notable is next round-ten
98 double majorstep = one/2;
99 mod3 = (int)Math.round(majorstep/minstep);
102 // major step is round-ten, major-notable is next round-ten
103 mod3 = (int)Math.round(one/minstep);
106 // Check for clashes between minor-notable and major-nonnotable
109 mod2 = 1; // Every minor tick is notable
111 mod2 = 5; // Every fifth minor tick is notable
115 // Calculate starting position
116 int pos = (int)Math.ceil(start/minstep);
117 // System.out.println("mod2="+mod2+" mod3="+mod3+" mod4="+mod4);
118 while (pos*minstep <= end) {
119 double unitValue = pos*minstep;
120 double value = fromUnit(unitValue);
123 ticks.add(new Tick(value,unitValue,true,true));
124 else if (pos%mod3 == 0)
125 ticks.add(new Tick(value,unitValue,true,false));
126 else if (pos%mod2 == 0)
127 ticks.add(new Tick(value,unitValue,false,true));
129 ticks.add(new Tick(value,unitValue,false,false));
134 return ticks.toArray(new Tick[0]);
139 public String toString(double value) {
141 double correctVal = toUnit(value);
142 double val = round(correctVal);
145 if ( Math.abs( val - correctVal ) > epsilon ) {
146 NumberFormat decFormat = new DecimalFormat("#.###");
147 return decFormat.format(correctVal);
150 NumberFormat intFormat = new DecimalFormat("#");
151 double sign = Math.signum(val);
153 double posValue = sign * val;
155 double intPart = Math.floor(posValue);
157 double frac = Math.rint((posValue - intPart)/fractionValue);
158 double fracBase = fractionBase;
161 while ( frac > 0 && fracBase > 2 && frac % 2 == 0 ) {
169 return intFormat.format(posValue);
170 } else if (intPart == 0.0 ){
171 return intFormat.format(sign*frac) + "/" + intFormat.format(fracBase);
173 return intFormat.format(sign*intPart) + " " + intFormat.format(frac) + "/" + intFormat.format(fracBase);
179 public String toStringUnit(double value) {
180 if (Double.isNaN(value))
183 String s = toString(value);
184 s += " " + unitLabel;