/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.math;
import java.util.Random;
/**
* FastMath
provides 'fast' math approximations and float equivalents of Math
* functions. These are all used as static values and functions.
*
* @author Various
* @version $Id: FastMath.java,v 1.45 2007/08/26 08:44:20 irrisor Exp $
*/
final public class FastMath {
private FastMath() {
}
/** A "close to zero" double epsilon value for use*/
public static final double DBL_EPSILON = 2.220446049250313E-16d;
/** A "close to zero" float epsilon value for use*/
public static final float FLT_EPSILON = 1.1920928955078125E-7f;
/** A "close to zero" float epsilon value for use*/
public static final float ZERO_TOLERANCE = 0.0001f;
public static final float ONE_THIRD = 1f / 3f;
/** The value PI as a float. (180 degrees) */
public static final float PI = (float) Math.PI;
/** The value 2PI as a float. (360 degrees) */
public static final float TWO_PI = 2.0f * PI;
/** The value PI/2 as a float. (90 degrees) */
public static final float HALF_PI = 0.5f * PI;
/** The value PI/4 as a float. (45 degrees) */
public static final float QUARTER_PI = 0.25f * PI;
/** The value 1/PI as a float. */
public static final float INV_PI = 1.0f / PI;
/** The value 1/(2PI) as a float. */
public static final float INV_TWO_PI = 1.0f / TWO_PI;
/** A value to multiply a degree value by, to convert it to radians. */
public static final float DEG_TO_RAD = PI / 180.0f;
/** A value to multiply a radian value by, to convert it to degrees. */
public static final float RAD_TO_DEG = 180.0f / PI;
/** A precreated random object for random numbers. */
public static final Random rand = new Random(System.currentTimeMillis());
/**
* Returns true if the number is a power of 2 (2,4,8,16...)
*
* A good implementation found on the Java boards. note: a number is a power
* of two if and only if it is the smallest number with that number of
* significant bits. Therefore, if you subtract 1, you know that the new
* number will have fewer bits, so ANDing the original number with anything
* less than it will give 0.
*
* @param number
* The number to test.
* @return True if it is a power of two.
*/
public static boolean isPowerOfTwo(int number) {
return (number > 0) && (number & (number - 1)) == 0;
}
public static int nearestPowerOfTwo(int number) {
return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2)));
}
/**
* Linear interpolation from startValue to endValue by the given percent.
* Basically: ((1 - percent) * startValue) + (percent * endValue)
*
* @param scale
* scale value to use. if 1, use endValue, if 0, use startValue.
* @param startValue
* Begining value. 0% of f
* @param endValue
* ending value. 100% of f
* @return The interpolated value between startValue and endValue.
*/
public static float interpolateLinear(float scale, float startValue, float endValue) {
if (startValue == endValue) {
return startValue;
}
if (scale <= 0f) {
return startValue;
}
if (scale >= 1f) {
return endValue;
}
return ((1f - scale) * startValue) + (scale * endValue);
}
/**
* Linear interpolation from startValue to endValue by the given percent.
* Basically: ((1 - percent) * startValue) + (percent * endValue)
*
* @param scale
* scale value to use. if 1, use endValue, if 0, use startValue.
* @param startValue
* Begining value. 0% of f
* @param endValue
* ending value. 100% of f
* @param store a vector3f to store the result
* @return The interpolated value between startValue and endValue.
*/
public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) {
if (store == null) {
store = new Vector3f();
}
store.x = interpolateLinear(scale, startValue.x, endValue.x);
store.y = interpolateLinear(scale, startValue.y, endValue.y);
store.z = interpolateLinear(scale, startValue.z, endValue.z);
return store;
}
/**
* Linear interpolation from startValue to endValue by the given percent.
* Basically: ((1 - percent) * startValue) + (percent * endValue)
*
* @param scale
* scale value to use. if 1, use endValue, if 0, use startValue.
* @param startValue
* Begining value. 0% of f
* @param endValue
* ending value. 100% of f
* @return The interpolated value between startValue and endValue.
*/
public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue) {
return interpolateLinear(scale, startValue, endValue, null);
}
/**
* Linear extrapolation from startValue to endValue by the given scale.
* if scale is between 0 and 1 this method returns the same result as interpolateLinear
* if the scale is over 1 the value is linearly extrapolated.
* Note that the end value is the value for a scale of 1.
* @param scale the scale for extrapolation
* @param startValue the starting value (scale = 0)
* @param endValue the end value (scale = 1)
* @return an extrapolation for the given parameters
*/
public static float extrapolateLinear(float scale, float startValue, float endValue) {
// if (scale <= 0f) {
// return startValue;
// }
return ((1f - scale) * startValue) + (scale * endValue);
}
/**
* Linear extrapolation from startValue to endValue by the given scale.
* if scale is between 0 and 1 this method returns the same result as interpolateLinear
* if the scale is over 1 the value is linearly extrapolated.
* Note that the end value is the value for a scale of 1.
* @param scale the scale for extrapolation
* @param startValue the starting value (scale = 0)
* @param endValue the end value (scale = 1)
* @param store an initialized vector to store the return value
* @return an extrapolation for the given parameters
*/
public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) {
if (store == null) {
store = new Vector3f();
}
// if (scale <= 1f) {
// return interpolateLinear(scale, startValue, endValue, store);
// }
store.x = extrapolateLinear(scale, startValue.x, endValue.x);
store.y = extrapolateLinear(scale, startValue.y, endValue.y);
store.z = extrapolateLinear(scale, startValue.z, endValue.z);
return store;
}
/**
* Linear extrapolation from startValue to endValue by the given scale.
* if scale is between 0 and 1 this method returns the same result as interpolateLinear
* if the scale is over 1 the value is linearly extrapolated.
* Note that the end value is the value for a scale of 1.
* @param scale the scale for extrapolation
* @param startValue the starting value (scale = 0)
* @param endValue the end value (scale = 1)
* @return an extrapolation for the given parameters
*/
public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue) {
return extrapolateLinear(scale, startValue, endValue, null);
}
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
* here is the interpolation matrix
* m = [ 0.0 1.0 0.0 0.0 ]
* [-T 0.0 T 0.0 ]
* [ 2T T-3 3-2T -T ]
* [-T 2-T T-2 T ]
* where T is the curve tension
* the result is a value between p1 and p2, t=0 for p1, t=1 for p2
* @param u value from 0 to 1
* @param T The tension of the curve
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @return catmull-Rom interpolation
*/
public static float interpolateCatmullRom(float u, float T, float p0, float p1, float p2, float p3) {
float c1, c2, c3, c4;
c1 = p1;
c2 = -1.0f * T * p0 + T * p2;
c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3;
c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3;
return (float) (((c4 * u + c3) * u + c2) * u + c1);
}
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
* here is the interpolation matrix
* m = [ 0.0 1.0 0.0 0.0 ]
* [-T 0.0 T 0.0 ]
* [ 2T T-3 3-2T -T ]
* [-T 2-T T-2 T ]
* where T is the tension of the curve
* the result is a value between p1 and p2, t=0 for p1, t=1 for p2
* @param u value from 0 to 1
* @param T The tension of the curve
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @param store a Vector3f to store the result
* @return catmull-Rom interpolation
*/
public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) {
if (store == null) {
store = new Vector3f();
}
store.x = interpolateCatmullRom(u, T, p0.x, p1.x, p2.x, p3.x);
store.y = interpolateCatmullRom(u, T, p0.y, p1.y, p2.y, p3.y);
store.z = interpolateCatmullRom(u, T, p0.z, p1.z, p2.z, p3.z);
return store;
}
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
* here is the interpolation matrix
* m = [ 0.0 1.0 0.0 0.0 ]
* [-T 0.0 T 0.0 ]
* [ 2T T-3 3-2T -T ]
* [-T 2-T T-2 T ]
* where T is the tension of the curve
* the result is a value between p1 and p2, t=0 for p1, t=1 for p2
* @param u value from 0 to 1
* @param T The tension of the curve
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @return catmull-Rom interpolation
*/
public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
return interpolateCatmullRom(u, T, p0, p1, p2, p3, null);
}
/**Interpolate a spline between at least 4 control points following the Bezier equation.
* here is the interpolation matrix
* m = [ -1.0 3.0 -3.0 1.0 ]
* [ 3.0 -6.0 3.0 0.0 ]
* [ -3.0 3.0 0.0 0.0 ]
* [ 1.0 0.0 0.0 0.0 ]
* where T is the curve tension
* the result is a value between p1 and p3, t=0 for p1, t=1 for p3
* @param u value from 0 to 1
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @return Bezier interpolation
*/
public static float interpolateBezier(float u, float p0, float p1, float p2, float p3) {
float oneMinusU = 1.0f - u;
float oneMinusU2 = oneMinusU * oneMinusU;
float u2 = u * u;
return p0 * oneMinusU2 * oneMinusU
+ 3.0f * p1 * u * oneMinusU2
+ 3.0f * p2 * u2 * oneMinusU
+ p3 * u2 * u;
}
/**Interpolate a spline between at least 4 control points following the Bezier equation.
* here is the interpolation matrix
* m = [ -1.0 3.0 -3.0 1.0 ]
* [ 3.0 -6.0 3.0 0.0 ]
* [ -3.0 3.0 0.0 0.0 ]
* [ 1.0 0.0 0.0 0.0 ]
* where T is the tension of the curve
* the result is a value between p1 and p3, t=0 for p1, t=1 for p3
* @param u value from 0 to 1
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @param store a Vector3f to store the result
* @return Bezier interpolation
*/
public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) {
if (store == null) {
store = new Vector3f();
}
store.x = interpolateBezier(u, p0.x, p1.x, p2.x, p3.x);
store.y = interpolateBezier(u, p0.y, p1.y, p2.y, p3.y);
store.z = interpolateBezier(u, p0.z, p1.z, p2.z, p3.z);
return store;
}
/**Interpolate a spline between at least 4 control points following the Bezier equation.
* here is the interpolation matrix
* m = [ -1.0 3.0 -3.0 1.0 ]
* [ 3.0 -6.0 3.0 0.0 ]
* [ -3.0 3.0 0.0 0.0 ]
* [ 1.0 0.0 0.0 0.0 ]
* where T is the tension of the curve
* the result is a value between p1 and p3, t=0 for p1, t=1 for p3
* @param u value from 0 to 1
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @return Bezier interpolation
*/
public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
return interpolateBezier(u, p0, p1, p2, p3, null);
}
/**
* Compute the lenght on a catmull rom spline between control point 1 and 2
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @param startRange the starting range on the segment (use 0)
* @param endRange the end range on the segment (use 1)
* @param curveTension the curve tension
* @return the length of the segment
*/
public static float getCatmullRomP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, float startRange, float endRange, float curveTension) {
float epsilon = 0.001f;
float middleValue = (startRange + endRange) * 0.5f;
Vector3f start = p1.clone();
if (startRange != 0) {
FastMath.interpolateCatmullRom(startRange, curveTension, p0, p1, p2, p3, start);
}
Vector3f end = p2.clone();
if (endRange != 1) {
FastMath.interpolateCatmullRom(endRange, curveTension, p0, p1, p2, p3, end);
}
Vector3f middle = FastMath.interpolateCatmullRom(middleValue, curveTension, p0, p1, p2, p3);
float l = end.subtract(start).length();
float l1 = middle.subtract(start).length();
float l2 = end.subtract(middle).length();
float len = l1 + l2;
if (l + epsilon < len) {
l1 = getCatmullRomP1toP2Length(p0, p1, p2, p3, startRange, middleValue, curveTension);
l2 = getCatmullRomP1toP2Length(p0, p1, p2, p3, middleValue, endRange, curveTension);
}
l = l1 + l2;
return l;
}
/**
* Compute the lenght on a bezier spline between control point 1 and 2
* @param p0 control point 0
* @param p1 control point 1
* @param p2 control point 2
* @param p3 control point 3
* @return the length of the segment
*/
public static float getBezierP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
float delta = 0.02f, t = 0.0f, result = 0.0f;
Vector3f v1 = p0.clone(), v2 = new Vector3f();
while (t <= 1.0f) {
FastMath.interpolateBezier(t, p0, p1, p2, p3, v2);
result += v1.subtractLocal(v2).length();
v1.set(v2);
t += delta;
}
return result;
}
/**
* Returns the arc cosine of an angle given in radians.
* Special cases:
*
Source:
* http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
broken link
*
* @param half The half floating point value as a short.
* @return floating point value of the half.
*/
public static float convertHalfToFloat(short half) {
switch ((int) half) {
case 0x0000:
return 0f;
case 0x8000:
return -0f;
case 0x7c00:
return Float.POSITIVE_INFINITY;
case 0xfc00:
return Float.NEGATIVE_INFINITY;
// TODO: Support for NaN?
default:
return Float.intBitsToFloat(((half & 0x8000) << 16)
| (((half & 0x7c00) + 0x1C000) << 13)
| ((half & 0x03FF) << 13));
}
}
public static short convertFloatToHalf(float flt) {
if (Float.isNaN(flt)) {
throw new UnsupportedOperationException("NaN to half conversion not supported!");
} else if (flt == Float.POSITIVE_INFINITY) {
return (short) 0x7c00;
} else if (flt == Float.NEGATIVE_INFINITY) {
return (short) 0xfc00;
} else if (flt == 0f) {
return (short) 0x0000;
} else if (flt == -0f) {
return (short) 0x8000;
} else if (flt > 65504f) {
// max value supported by half float
return 0x7bff;
} else if (flt < -65504f) {
return (short) (0x7bff | 0x8000);
} else if (flt > 0f && flt < 5.96046E-8f) {
return 0x0001;
} else if (flt < 0f && flt > -5.96046E-8f) {
return (short) 0x8001;
}
int f = Float.floatToIntBits(flt);
return (short) (((f >> 16) & 0x8000)
| ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
| ((f >> 13) & 0x03ff));
}
}