This is something that, while useful now, will be infinitely more useful once we start talking about scene editors and the such.
Includes the ability to set the general scale of all the included units as well as converting between all of them. If one artist sets their working units to centimeters when everyone else is working in inches, this will bring some relief in translating the objects
For now, it should be helpful in wrangling assets and being able to relate "meters to meters" rather than trying to think in "meters in terms of Vector3f"
Currently implemented units of measure:
Millimeter
Centimeter
Decimeter
Meter
Kilometer
Inch
Foot
Yard
Mile
Nautical Mile
Here's abstract class of a unit of Length:
package com.jme.scene.measure;
/**
* <p><code>LengthUnit</code> forms the base class for implementing
* scaled measure in a jME scenegraph.</p>
*
* <p><code>LengthUnit</code> can be used in the instantiation
* of a <code>Vector3f</code> as such:<p>
* <code>
* Vector3f myScaledVector = new Vector3f(<br>
* new Foot(2).convertToFloat(),<br>
* new Foot(1).convertToFloat(),<br>
* new Inch(3).convertToFloat());<br>
* </code>
* @author <a href="mailto:skye.book@gmail.com">Skye Book
*
*/
abstract public class LengthUnit {
public enum Units{
MILLIMETER, DECIMETER, CENTIMETER, METER, KILOMETER,
INCH, FOOT, YARD, MILE, NAUTICAL_MILE
}
protected float numberOfUnits;
/**
* <code>metersPerFoot</code> is the general multiplier for
* the scale of the unit system. The default is 1 meter for
* each whole number.
*/
protected float metersPerFloat = 1;
/**
* Constructor creates a <code>LengthUnit</code> of a specified length
* and unit, allowing you to convert easily between popular measures of
* distance.
*/
public LengthUnit(float length) {
this.numberOfUnits = length;
}
/**
* Converts the current <code>LengthUnit</code> into another form.
* (e.g: Meters -> Feet)
* @param targetUnit The measurement being converted into
* @return The LengthUnit created in the form of the targetUnit
*/
public abstract LengthUnit convert(Units targetUnit);
/**
* Converts a LengthUnit into a float that is useful in defining
* spaces in the scenegraph.
* @return A scaled float which can be used to construct
* a <code>Vector3f</code>.
*/
public abstract float convertToFloat();
/**
* @return the metersPerFloat
*/
public float getMetersPerFloat() {
return metersPerFloat;
}
/**
* Sets the general scale in terms of what is actually contained
* in a Vector3f. For example, setting this to 5 would mean that
* 15 meters in all directions could be reproduced with
* <code>Vector3f(3,3,3)</code>
*
* @param metersPerFloat The general multiplier for
* the scale of the unit system. The default is 1 meter for
* each whole number.
*/
public void setMetersPerFloat(float metersPerFloat) {
this.metersPerFloat = metersPerFloat;
}
public float getNumberOfUnits() {
return numberOfUnits;
}
public void setNumberOfUnits(float numberOfUnits) {
this.numberOfUnits = numberOfUnits;
}
}
And an example of one of the units of measure (they're all the same, just the calculations change... one should suffice)
package com.jme.scene.measure;
/**
* @author <a href="mailto:skye.book@gmail.com">Skye Book
*
*/
public class Meter extends LengthUnit {
/**
* Constructor creates a specified number of Meters,
* allowing you to convert easily between popular measures of
* distance.
*
*@param length The number of meters.
*/
public Meter(float length) {
super(length);
// TODO Auto-generated constructor stub
}
/* (non-Javadoc)
* @see com.jme.scene.measure.LengthUnit#convert(com.jme.scene.measure.LengthUnit.Units)
*/
@Override
public LengthUnit convert(Units targetUnit) {
if(targetUnit.equals(Units.MILLIMETER))
{
return new Millimeter(numberOfUnits*1000f);
}
else if(targetUnit.equals(Units.CENTIMETER))
{
return new Centimeter(numberOfUnits*100f);
}
else if(targetUnit.equals(Units.DECIMETER))
{
return new Decimeter(numberOfUnits*10f);
}
else if(targetUnit.equals(Units.METER))
{
return this;
}
else if(targetUnit.equals(Units.KILOMETER))
{
return new Kilometer(numberOfUnits*0.001f);
}
else if(targetUnit.equals(Units.INCH))
{
return new Inch(numberOfUnits*39.3700787f);
}
else if(targetUnit.equals(Units.FOOT))
{
return new Foot(numberOfUnits*3.2808399f);
}
else if(targetUnit.equals(Units.YARD))
{
return new Yard(numberOfUnits*1.0936133f);
}
else if(targetUnit.equals(Units.MILE))
{
return new Mile(numberOfUnits*0.000621371192f);
}
else if(targetUnit.equals(Units.NAUTICAL_MILE))
{
return new NauticalMile(numberOfUnits*0.000539956803f);
}
else
return null;
}
/**
* Copyright 2008-2009 Brooklyn eXperimental Media Center
* Betaville Project by Brooklyn eXperimental Media Center at NYU-Poly
* http://bxmc.poly.edu
*/
package com.jme.scene.measure;
import com.jme.math.Vector3f;
/**
* <p><code>LengthUnit</code> forms the base class for implementing
* scaled measure in a jME scenegraph.</p>
*
* <p><code>LengthUnit</code> can be used in the instantiation
* of a <code>Vector3f</code> as such:<p>
* <code>
* Vector3f myScaledVector = new Vector3f(<br>
* new Foot(2).convertToFloat(),<br>
* new Foot(1).convertToFloat(),<br>
* new Inch(3).convertToFloat());<br>
* </code>
* @author <a href="mailto:skye.book@gmail.com">Skye Book
*
*/
abstract public class LengthUnit {
public enum Units{
MILLIMETER, DECIMETER, CENTIMETER, METER, KILOMETER,
INCH, FOOT, YARD, MILE, NAUTICAL_MILE
}
protected float numberOfUnits;
/**
* <code>metersPerFoot</code> is the general multiplier for
* the scale of the unit system. The default is 1 meter for
* each whole number.
*/
protected float metersPerFloat = 1;
/**
* Constructor creates a <code>LengthUnit</code> of a specified length
* and unit, allowing you to convert easily between popular measures of
* distance.
*/
public LengthUnit(float length) {
this.numberOfUnits = length;
}
/**
* Converts the current <code>LengthUnit</code> into another form.
* (e.g: Meters -> Feet)
* @param targetUnit The measurement being converted into
* @return The LengthUnit created in the form of the targetUnit
*/
public abstract LengthUnit convert(Units targetUnit);
/**
*
* @param vectorToConvert A <code>Vector3f</code> to be scaled to
* another unit of measure.
* @param startUnit The unit being converted from.
* @param targetUnit The unit being converted to.
* @return A Vector3f with values in the converted unit of measure.
*/
public static Vector3f convertVectorUnits(Vector3f vectorToConvert, Units startUnit, Units targetUnit)
{
/* instantiate blank units. All enumeration values are
* present here, so there's no reason why you should
* get a converted Inch of length 0 returned..
*/
LengthUnit xUnit = new Inch(0);
LengthUnit yUnit = new Inch(0);
LengthUnit zUnit = new Inch(0);
if(startUnit.equals(Units.MILLIMETER))
{
xUnit = new Millimeter(vectorToConvert.x);
yUnit = new Millimeter(vectorToConvert.y);
zUnit = new Millimeter(vectorToConvert.z);
}
else if(startUnit.equals(Units.CENTIMETER))
{
xUnit = new Centimeter(vectorToConvert.x);
yUnit = new Centimeter(vectorToConvert.y);
zUnit = new Centimeter(vectorToConvert.z);
}
else if(startUnit.equals(Units.DECIMETER))
{
xUnit = new Decimeter(vectorToConvert.x);
yUnit = new Decimeter(vectorToConvert.y);
zUnit = new Decimeter(vectorToConvert.z);
}
else if(startUnit.equals(Units.METER))
{
xUnit = new Meter(vectorToConvert.x);
yUnit = new Meter(vectorToConvert.y);
zUnit = new Meter(vectorToConvert.z);
}
else if(startUnit.equals(Units.KILOMETER))
{
xUnit = new Kilometer(vectorToConvert.x);
yUnit = new Kilometer(vectorToConvert.y);
zUnit = new Kilometer(vectorToConvert.z);
}
else if(startUnit.equals(Units.INCH))
{
xUnit = new Inch(vectorToConvert.x);
yUnit = new Inch(vectorToConvert.y);
zUnit = new Inch(vectorToConvert.z);
}
else if(startUnit.equals(Units.FOOT))
{
xUnit = new Foot(vectorToConvert.x);
yUnit = new Foot(vectorToConvert.y);
zUnit = new Foot(vectorToConvert.z);
}
else if(startUnit.equals(Units.YARD))
{
xUnit = new Yard(vectorToConvert.x);
yUnit = new Yard(vectorToConvert.y);
zUnit = new Yard(vectorToConvert.z);
}
else if(startUnit.equals(Units.MILE))
{
xUnit = new Mile(vectorToConvert.x);
yUnit = new Mile(vectorToConvert.y);
zUnit = new Mile(vectorToConvert.z);
}
else if(startUnit.equals(Units.NAUTICAL_MILE))
{
xUnit = new NauticalMile(vectorToConvert.x);
yUnit = new NauticalMile(vectorToConvert.y);
zUnit = new NauticalMile(vectorToConvert.z);
}
else
return vectorToConvert;
return new Vector3f(xUnit.convert(targetUnit).convertToFloat(),
yUnit.convert(targetUnit).convertToFloat(),
zUnit.convert(targetUnit).convertToFloat());
}
/**
* Converts a LengthUnit into a float that is useful in defining
* spaces in the scenegraph.
* @return A scaled float which can be used to construct
* a <code>Vector3f</code>.
*/
public abstract float convertToFloat();
/**
* @return the metersPerFloat
*/
public float getMetersPerFloat() {
return metersPerFloat;
}
/**
* Sets the general scale in terms of what is actually contained
* in a Vector3f. For example, setting this to 5 would mean that
* 15 meters in all directions could be reproduced with
* <code>Vector3f(3,3,3)</code>
*
* @param metersPerFloat The general multiplier for
* the scale of the unit system. The default is 1 meter for
* each whole number.
*/
public void setMetersPerFloat(float metersPerFloat) {
this.metersPerFloat = metersPerFloat;
}
public float getNumberOfUnits() {
return numberOfUnits;
}
public void setNumberOfUnits(float numberOfUnits) {
this.numberOfUnits = numberOfUnits;
}
}
For each convertion you then first convert to Meter (the standard unit), than from Meter to the Unit you want.
:D
What do you think?
It's better extendible I think.
The problem with this is that we're doubling the number of calculations needed to convert a unit of measure.. As far as "better" extendible, your method does make it slightly simpler to create new measure types, though it's adding work for the JVM to process. I'm not so sure that formulating a few extra multipliers when writing the code isn't worth the small time investment when it comes to performance later on ;)
The problem with this is that we're doubling the number of calculations needed to convert a unit of measure.. As far as "better" extendible, your method does make it slightly simpler to create new measure types, though it's adding work for the JVM to process. I'm not so sure that formulating a few extra multipliers when writing the code isn't worth the small time investment when it comes to performance later on Wink
That mustn't be true, because it will get rid of all the checks :D
All it requires is one float multiplikation, but will save about ~12/2 = 6 checks..
You'd still need to check for which unit you want to convert to, so which checks would be eliminated? (Now you've got me curious about how much those checks slow it down lol)
So, since I've more or less paused my cell-and-portal implementation, because I want to wait till the SceneManager's from Ogli comes out
I've implemented the LengthUnit's in "my Way":
The result's:
New classes can be made damn fast!
But performance is about 2 times better with the "old" implementation:
( The problem is not in multiplying twice but it's in the cloneing of the instances )
// The test first generates 10 million NauticalMile's and then convert them twice (through the old and new Way) to Foot
Iterations: 10000000
Test results: (all times in millies)
Time used for Generation: 2117
Time used for Converting(newWay): 1700
Time used for Converting(oldWay): 960
So if anyone interested in an easy to use framework for building own unit's ( not only LengthUnit ) and can accept the worse performance, please just say a "yes, i want" :wink:
Of course the old method's for converting can still be used. (But it will throw an UnsupportedOperationException if you try to convert to or from a "new" LengthUnit like LightYear or AstronomicalUnit)