/** * */ package cz.cuni.mff.peckam.java.origamist.math; import java.text.MessageFormat; import java.util.ResourceBundle; import cz.cuni.mff.peckam.java.origamist.configuration.Configuration; import cz.cuni.mff.peckam.java.origamist.services.ServiceLocator; import cz.cuni.mff.peckam.java.origamist.services.interfaces.ConfigurationManager; /** * A unit of angles. * * @author Martin Pecka */ public enum AngleUnit { /** Degrees. */ DEGREE { @Override public double convertTo(double value, AngleUnit targetUnit) { if (targetUnit == this) return value; if (targetUnit == RAD) return value * degToRad; return RAD.convertTo(convertTo(value, RAD), targetUnit); } @Override public String formatValue(double value) { format.applyPattern(messages.getString("deg.unitString")); return format.format(new Object[] { value }); } @Override public String getUnit() { return messages.getString("deg.unit"); } @Override public double getMaxValue() { return 360; } @Override public String toString() { return messages.getString("deg.name"); } }, /** Radians. They are used as the conversion inter-unit. */ RAD { @Override public double convertTo(double value, AngleUnit targetUnit) { if (targetUnit == this) return value; switch (targetUnit) { case DEGREE: return value * radToDeg; case GRAD: return value * radToGrad; default: return value; } } @Override public String formatValue(double value) { double timesPi = value / Math.PI; format.applyPattern(messages.getString("rad.unitString")); return format.format(new Object[] { timesPi + "π" }); } @Override public String getUnit() { return messages.getString("rad.unit"); } @Override public double getMaxValue() { return 2d * Math.PI; } @Override public String toString() { return messages.getString("rad.name"); } @Override public Double parseValue(String text) throws NumberFormatException { String txt = text.toLowerCase().replaceAll("π", "pi"); if (!txt.contains("pi")) return super.parseValue(txt); // allow strings like "2*PI", "pi*2", "PI/2", "2pi" if (txt.matches("pi\\s*/")) return Math.PI / Double.parseDouble(txt.replaceAll("\\s*pi\\s*/\\s*", "")); return Double.parseDouble(txt.replaceAll("\\s*\\*?\\s*pi\\s*\\*?\\s*", "")) * Math.PI; } @Override public String getNiceValue(double value) { double timesPi = value / Math.PI; return timesPi + "π"; } }, /** Grads. */ GRAD { @Override public double convertTo(double value, AngleUnit targetUnit) { if (targetUnit == this) return value; if (targetUnit == RAD) return value * gradToRad; return RAD.convertTo(convertTo(value, RAD), targetUnit); } @Override public String formatValue(double value) { format.applyPattern(messages.getString("grad.unitString")); return format.format(new Object[] { value }); } @Override public String getUnit() { return messages.getString("grad.unit"); } @Override public double getMaxValue() { return 400; } @Override public String toString() { return messages.getString("grad.name"); } }; protected static final double degToRad = Math.PI / 180d; protected static final double radToDeg = 1d / degToRad; protected static final double gradToRad = Math.PI / 200d; protected static final double radToGrad = 1d / gradToRad; /** The resource bundle this class can use. */ protected ResourceBundle messages; /** The format object to format strings with. */ protected MessageFormat format; { ServiceLocator .get(ConfigurationManager.class) .get() .addAndRunResourceBundleListener( new Configuration.ResourceBundleLocaleListener(AngleUnit.this.getDeclaringClass().getName()) { @Override public void bundleChanged() { messages = this.bundle; format = new MessageFormat("", getProperty()); } }); } /** * Convert the given angle in <code>this</code> units to the target unit. * * @param value The angle to convert. * @param targetUnit The unit to convert to. * @return The converted angle. */ public abstract double convertTo(double value, AngleUnit targetUnit); /** * @return The value that represents a whole circle. */ public abstract double getMaxValue(); /** * Return a corresponding base angle (angle in interval 0 to whole circle). * * @param value The value to be normalized. * @return The normalized value. */ public double normalize(double value) { final double max = getMaxValue(); if (value < 0 || value > max) return value - (Math.floor(value / max) * max); else return value; } /** * @return The full name of this unit. */ @Override public abstract String toString(); /** * @return The string representing this unit's mark. */ public abstract String getUnit(); /** * Return the string representing the given value in <code>this</code> unit. * * @param value The angle to format. * @return The string representing the given angle. */ public abstract String formatValue(double value); /** * Return the string representing the given value in <code>this</code> unit. * * @param value The angle to format. * @param unit The unit of the given angle. * @return The string representing the given angle. */ public String formatValue(double value, AngleUnit unit) { return formatValue(unit.convertTo(value, this)); } /** * Parse an angle from the given string. * * @param text The text to be parsed. * @return The parsed value. * * @throws NumberFormatException If the string cannot be parsed as a value of this unit. */ public Double parseValue(String text) throws NumberFormatException { return Double.parseDouble(text); } /** * Return a user-friendly string representation for the given value. The returned string has to be parseable by * {@link #parseValue(String)}. * * @param value The value to get nice form of. * @return The nice form of the given value. */ public String getNiceValue(double value) { return Double.toString(value); } }