1 package net.sf.openrocket.unit;
3 import java.text.DecimalFormat;
4 import java.text.NumberFormat;
5 import java.util.ArrayList;
7 import net.sf.openrocket.util.Chars;
9 public class FractionalUnit extends Unit {
11 private final static char FRACTION = Chars.FRACTION;
13 private final static String[] NUMERATOR = {
26 private final static String[] DENOMINATOR = {
39 // This is the base of the fractions. ie, 16d for 1/16ths.
40 private final int fractionBase;
41 // This is 1d/fractionBase;
42 private final double fractionValue;
44 // This is the value used when incrementing/decrementing.
45 private final double incrementValue;
47 // If the actual value differs from the decimal representation by more than this,
48 // we display as decimals.
49 private final double epsilon;
51 private final String unitLabel;
53 public FractionalUnit(double multiplier, String unit, String unitLabel, int fractionBase, double incrementValue) {
54 this(multiplier, unit, unitLabel, fractionBase, incrementValue, 0.1d / fractionBase);
57 public FractionalUnit(double multiplier, String unit, String unitLabel, int fractionBase, double incrementValue, double epsilon) {
58 super(multiplier, unit);
59 this.unitLabel = unitLabel;
60 this.fractionBase = fractionBase;
61 this.fractionValue = 1.0d / fractionBase;
62 this.incrementValue = incrementValue;
63 this.epsilon = epsilon;
67 public double round(double value) {
68 return roundTo(value, fractionValue);
71 private double roundTo(double value, double fraction) {
72 double remainder = Math.IEEEremainder(value, fraction);
73 return value - remainder;
77 public double getNextValue(double value) {
78 double rounded = roundTo(value, incrementValue);
79 if (rounded <= value + epsilon) {
80 rounded += incrementValue;
86 public double getPreviousValue(double value) {
87 double rounded = roundTo(value, incrementValue);
88 if (rounded >= value - epsilon) {
89 rounded -= incrementValue;
95 public Tick[] getTicks(double start, double end, double minor, double major) {
97 start = toUnit(start);
99 minor = toUnit(minor);
100 major = toUnit(major);
102 if (minor <= 0 || major <= 0 || major < minor) {
103 throw new IllegalArgumentException("getTicks called with minor=" + minor + " major=" + major);
106 ArrayList<Tick> ticks = new ArrayList<Tick>();
108 int mod2, mod3, mod4; // Moduli for minor-notable, major-nonnotable, major-notable
111 // Find the smallest possible step size
120 // Find step size for major ticks
126 if (one / 2 >= major) {
127 // major step is round-five, major-notable is next round-ten
128 double majorstep = one / 2;
129 mod3 = (int) Math.round(majorstep / minstep);
132 // major step is round-ten, major-notable is next round-ten
133 mod3 = (int) Math.round(one / minstep);
136 // Check for clashes between minor-notable and major-nonnotable
139 mod2 = 1; // Every minor tick is notable
141 mod2 = 5; // Every fifth minor tick is notable
145 // Calculate starting position
146 int pos = (int) Math.ceil(start / minstep);
147 // System.out.println("mod2="+mod2+" mod3="+mod3+" mod4="+mod4);
148 while (pos * minstep <= end) {
149 double unitValue = pos * minstep;
150 double value = fromUnit(unitValue);
153 ticks.add(new Tick(value, unitValue, true, true));
154 else if (pos % mod3 == 0)
155 ticks.add(new Tick(value, unitValue, true, false));
156 else if (pos % mod2 == 0)
157 ticks.add(new Tick(value, unitValue, false, true));
159 ticks.add(new Tick(value, unitValue, false, false));
164 return ticks.toArray(new Tick[0]);
169 public String toString(double value) {
171 double correctVal = toUnit(value);
172 double val = round(correctVal);
175 if (Math.abs(val - correctVal) > epsilon) {
176 NumberFormat decFormat = new DecimalFormat("#.###");
177 return decFormat.format(correctVal);
180 NumberFormat intFormat = new DecimalFormat("#");
181 double sign = Math.signum(val);
183 double posValue = sign * val;
185 double intPart = Math.floor(posValue);
187 double frac = Math.rint((posValue - intPart) / fractionValue);
188 double fracBase = fractionBase;
191 while (frac > 0 && fracBase > 2 && frac % 2 == 0) {
199 return intFormat.format(posValue);
200 } else if (intPart == 0.0) {
201 return (sign < 0 ? "-" : "") + numeratorString(Double.valueOf(frac).intValue())
202 + FRACTION + denominatorString(Double.valueOf(fracBase).intValue());
204 return intFormat.format(sign * intPart) + " " + numeratorString(Double.valueOf(frac).intValue())
205 + FRACTION + denominatorString(Double.valueOf(fracBase).intValue());
210 private String numeratorString(int value) {
217 rep = NUMERATOR[value % 10] + rep;
223 private String denominatorString(int value) {
229 rep = DENOMINATOR[value % 10] + rep;
236 public String toStringUnit(double value) {
237 if (Double.isNaN(value))
240 String s = toString(value);
241 s += " " + unitLabel;