/*
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* OpenPnP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/
package org.openpnp.model;
import java.util.Locale;
import org.simpleframework.xml.Attribute;
public class Length {
public enum Field {
X, Y, Z
}
@Attribute
private double value;
@Attribute
private LengthUnit units;
public Length() {
}
public Length(double value, LengthUnit units) {
this.value = value;
this.units = units;
}
public Length add(Length length) {
length = length.convertToUnits(units);
return new Length(value + length.getValue(), units);
}
public Length subtract(Length length) {
length = length.convertToUnits(units);
return new Length(value - length.getValue(), units);
}
public Length multiply(Length length) {
length = length.convertToUnits(units);
return new Length(value * length.getValue(), units);
}
public Length add(double d) {
return new Length(value + d, units);
}
public Length subtract(double d) {
return new Length(value - d, units);
}
public Length multiply(double d) {
return new Length(value * d, units);
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
public LengthUnit getUnits() {
return units;
}
public void setUnits(LengthUnit units) {
this.units = units;
}
public Length convertToUnits(LengthUnit units) {
if (this.units == units) {
return this;
}
// First convert the current value to millimeters, which we use as a base unit.
double mm = 0;
if (this.units == LengthUnit.Millimeters) {
mm = value;
}
else if (this.units == LengthUnit.Centimeters) {
mm = value * 10;
}
else if (this.units == LengthUnit.Meters) {
mm = value * 1000;
}
else if (this.units == LengthUnit.Inches) {
mm = value * 25.4;
}
else if (this.units == LengthUnit.Feet) {
mm = value * 25.4 * 12;
}
else if (this.units == LengthUnit.Mils) {
mm = value / 1000 * 25.4;
}
else if (this.units == LengthUnit.Microns) {
mm = value / 1000.0;
}
else {
throw new Error("convertLength() unrecognized units " + this.units);
}
// Then convert the calculated millimeter value to the requested unit.
if (units == LengthUnit.Millimeters) {
return new Length(mm, units);
}
else if (units == LengthUnit.Centimeters) {
return new Length(mm / 10, units);
}
else if (units == LengthUnit.Meters) {
return new Length(mm / 1000, units);
}
else if (units == LengthUnit.Inches) {
return new Length(mm * (1 / 25.4), units);
}
else if (units == LengthUnit.Feet) {
return new Length(mm * (1 / 25.4) * 12, units);
}
else if (units == LengthUnit.Mils) {
return new Length(mm * (1 / 25.4 * 1000), units);
}
else if (units == LengthUnit.Microns) {
return new Length(mm * 1000, units);
}
else {
throw new Error("convertLength() unrecognized units " + units);
}
}
public static double convertToUnits(double value, LengthUnit fromUnits, LengthUnit toUnits) {
return new Length(value, fromUnits).convertToUnits(toUnits).getValue();
}
public static Length parse(String s) {
return parse(s, false);
}
/**
* Takes a value in the format of a double followed by any number of spaces followed by the
* shortName of a LengthUnit value and returns the value as a Length object. Returns null if the
* value could not be parsed.
*/
public static Length parse(String s, boolean requireUnits) {
if (s == null) {
return null;
}
s = s.trim();
Length length = new Length(0, null);
// find the index of the first character that is not a -, . or digit.
int startOfUnits = -1;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch != '-' && ch != '.' && !Character.isDigit(ch)) {
startOfUnits = i;
break;
}
}
String valueString = null;
if (startOfUnits != -1) {
valueString = s.substring(0, startOfUnits);
String unitsString = s.substring(startOfUnits);
unitsString = unitsString.trim();
for (LengthUnit lengthUnit : LengthUnit.values()) {
if (lengthUnit.getShortName().equalsIgnoreCase(unitsString)) {
length.setUnits(lengthUnit);
break;
}
}
}
else {
valueString = s;
}
if (requireUnits && length.getUnits() == null) {
return null;
}
try {
double value = Double.parseDouble(valueString);
length.setValue(value);
}
catch (Exception e) {
return null;
}
return length;
}
@Override
public String toString() {
return String.format(Locale.US, "%2.3f%s", value, units.getShortName());
}
/**
* Performs the same function as toString() but allows the caller to specify the format String
* that is used. The format String should contain %f and %s in that order for value and
* units.getShortName().
*
* @param fmt
* @return
*/
public String toString(String fmt) {
if (fmt == null) {
return toString();
}
return String.format(Locale.US, fmt, value, units.getShortName());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((units == null) ? 0 : units.hashCode());
long temp;
temp = Double.doubleToLongBits(value);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Length other = (Length) obj;
if (units != other.units)
return false;
if (Double.doubleToLongBits(value) != Double.doubleToLongBits(other.value))
return false;
return true;
}
public static Location setLocationField(Configuration configuration, Location location,
Length length, Field field, boolean defaultToOldUnits) {
Length oldLength = null;
if (field == Field.X) {
oldLength = location.getLengthX();
}
else if (field == Field.Y) {
oldLength = location.getLengthY();
}
else if (field == Field.Z) {
oldLength = location.getLengthZ();
}
if (length.getUnits() == null) {
if (defaultToOldUnits) {
length.setUnits(oldLength.getUnits());
}
if (length.getUnits() == null) {
length.setUnits(configuration.getSystemUnits());
}
}
if (location.getUnits() == null) {
throw new Error("This can't happen!");
}
else {
location = location.convertToUnits(length.getUnits());
}
if (field == Field.X) {
location = location.derive(length.getValue(), null, null, null);
}
else if (field == Field.Y) {
location = location.derive(null, length.getValue(), null, null);
}
else if (field == Field.Z) {
location = location.derive(null, null, length.getValue(), null);
}
return location;
}
/**
* Sets the specified field on the passed Location object. Enforces application specific unit
* conversion. If the new Length value does not have units set, this method will set the units
* of the Length to the system default units. If the Location itself does not have units set,
* the Location's units are set to the Length's units. Finally, if the Location's units have
* changed, the entire Location is converted to the new units and the new object is returned.
*
* @param configuration
* @param location
* @param length
* @param field
* @return
*/
public static Location setLocationField(Configuration configuration, Location location,
Length length, Field field) {
return setLocationField(configuration, location, length, field, false);
}
}