diff --git a/src/main/java/pythagoras/f/AffineTransform.java b/src/main/java/pythagoras/f/AffineTransform.java
index 5357087..2b2223a 100644
--- a/src/main/java/pythagoras/f/AffineTransform.java
+++ b/src/main/java/pythagoras/f/AffineTransform.java
@@ -96,7 +96,7 @@ public class AffineTransform extends AbstractTransform
// compute the difference; if it's small enough, we're done
float d00 = n00 - o00, d10 = n10 - o10;
float d01 = n01 - o01, d11 = n11 - o11;
- if (d00*d00 + d10*d10 + d01*d01 + d11*d11 < FloatMath.EPSILON) {
+ if (d00*d00 + d10*d10 + d01*d01 + d11*d11 < MathUtil.EPSILON) {
break;
}
}
@@ -340,8 +340,8 @@ public class AffineTransform extends AbstractTransform
@Override
public String toString () {
- return "affine [" + FloatMath.toString(m00) + " " + FloatMath.toString(m01) + " " +
- FloatMath.toString(m10) + " " + FloatMath.toString(m11) + " " + translation() + "]";
+ return "affine [" + MathUtil.toString(m00) + " " + MathUtil.toString(m01) + " " +
+ MathUtil.toString(m10) + " " + MathUtil.toString(m11) + " " + translation() + "]";
}
// we don't publicize this because it might encourage someone to do something stupid like
diff --git a/src/main/java/pythagoras/f/FloatMath.java b/src/main/java/pythagoras/f/FloatMath.java
index 676dd43..acde20c 100644
--- a/src/main/java/pythagoras/f/FloatMath.java
+++ b/src/main/java/pythagoras/f/FloatMath.java
@@ -5,28 +5,17 @@
package pythagoras.f;
/**
- * Utility methods and constants for single-precision floating point math.
+ * Utility methods and constants for single-precision floating point math. Extends {@link MathUtil}
+ * with shim methods that call through to {@link Math} and convert the results to float.
*/
-public class FloatMath
+public class FloatMath extends MathUtil
{
/** The ratio of a circle's circumference to its diameter. */
public static final float PI = (float)Math.PI;
- /** The circle constant, Tau (τ) http://tauday.com/ */
- public static final float TAU = (float)(Math.PI * 2);
-
- /** Twice Pi. */
- public static final float TWO_PI = TAU;
-
- /** Pi times one half. */
- public static final float HALF_PI = (float)(Math.PI * 0.5);
-
/** The base value of the natural logarithm. */
public static final float E = (float)Math.E;
- /** A small number. */
- public static final float EPSILON = 0.00001f;
-
/**
* Computes and returns the sine of the given angle.
*
@@ -197,24 +186,6 @@ public class FloatMath
return (float)Math.floor(v);
}
- /**
- * A cheaper version of {@link Math#round} that doesn't handle the special cases.
- */
- public static int round (float v)
- {
- return (v < 0f) ? (int)(v - 0.5f) : (int)(v + 0.5f);
- }
-
- /**
- * Returns the floor of v as an integer without calling the relatively expensive
- * {@link Math#floor}.
- */
- public static int ifloor (float v)
- {
- int iv = (int)v;
- return (v < 0f) ? ((iv == v || iv == Integer.MIN_VALUE) ? iv : (iv - 1)) : iv;
- }
-
/**
* Returns the ceiling of v.
*
@@ -224,193 +195,4 @@ public class FloatMath
{
return (float)Math.ceil(v);
}
-
- /**
- * Returns the ceiling of v as an integer without calling the relatively expensive
- * {@link Math#ceil}.
- */
- public static int iceil (float v)
- {
- int iv = (int)v;
- return (v > 0f) ? ((iv == v || iv == Integer.MAX_VALUE) ? iv : (iv + 1)) : iv;
- }
-
- /**
- * Clamps a value to the range [lower, upper].
- */
- public static float clamp (float v, float lower, float upper)
- {
- return Math.min(Math.max(v, lower), upper);
- }
-
- /**
- * Rounds a value to the nearest multiple of a target.
- */
- public static float roundNearest (float v, float target)
- {
- target = Math.abs(target);
- if (v >= 0) {
- return target * floor((v + 0.5f * target) / target);
- } else {
- return target * ceil((v - 0.5f * target) / target);
- }
- }
-
- /**
- * Checks whether the value supplied is in [lower, upper].
- */
- public static boolean isWithin (float v, float lower, float upper)
- {
- return v >= lower && v <= upper;
- }
-
- /**
- * Returns a random value according to the normal distribution with the provided mean and
- * standard deviation.
- *
- * @param normal a normally distributed random value.
- * @param mean the desired mean.
- * @param stddev the desired standard deviation.
- */
- public static float normal (float normal, float mean, float stddev)
- {
- return stddev*normal + mean;
- }
-
- /**
- * Returns a random value according to the exponential distribution with the provided mean.
- *
- * @param random a uniformly distributed random value.
- * @param mean the desired mean.
- */
- public static float exponential (float random, float mean)
- {
- return -log(1f - random) * mean;
- }
-
- /**
- * Linearly interpolates between two angles, taking the shortest path around the circle.
- * This assumes that both angles are in [-pi, +pi].
- */
- public static float lerpa (float a1, float a2, float t)
- {
- float ma1 = mirrorAngle(a1), ma2 = mirrorAngle(a2);
- float d = Math.abs(a2 - a1), md = Math.abs(ma1 - ma2);
- return (d < md) ? lerp(a1, a2, t) : mirrorAngle(lerp(ma1, ma2, t));
- }
-
- /**
- * Linearly interpolates between v1 and v2 by the parameter t.
- */
- public static float lerp (float v1, float v2, float t)
- {
- return v1 + t*(v2 - v1);
- }
-
- /**
- * Determines whether two values are "close enough" to equal.
- */
- public static boolean epsilonEquals (float v1, float v2)
- {
- return Math.abs(v1 - v2) < EPSILON;
- }
-
- /**
- * Returns the (shortest) distance between two angles, assuming that both angles are in
- * [-pi, +pi].
- */
- public static float angularDistance (float a1, float a2)
- {
- float ma1 = mirrorAngle(a1), ma2 = mirrorAngle(a2);
- return Math.min(Math.abs(a1 - a2), Math.abs(ma1 - ma2));
- }
-
- /**
- * Returns the (shortest) difference between two angles, assuming that both angles are in
- * [-pi, +pi].
- */
- public static float angularDifference (float a1, float a2)
- {
- float ma1 = mirrorAngle(a1), ma2 = mirrorAngle(a2);
- float diff = a1 - a2, mdiff = ma2 - ma1;
- return (Math.abs(diff) < Math.abs(mdiff)) ? diff : mdiff;
- }
-
- /**
- * Returns an angle in the range [-pi, pi].
- */
- public static float normalizeAngle (float a)
- {
- while (a < -PI) {
- a += TWO_PI;
- }
- while (a > PI) {
- a -= TWO_PI;
- }
- return a;
- }
-
- /**
- * Returns an angle in the range [0, 2pi].
- */
- public static float normalizeAnglePositive (float a)
- {
- while (a < 0f) {
- a += TWO_PI;
- }
- while (a > TWO_PI) {
- a -= TWO_PI;
- }
- return a;
- }
-
- /**
- * Returns the mirror angle of the specified angle (assumed to be in [-pi, +pi]).
- */
- public static float mirrorAngle (float a)
- {
- return (a > 0f ? PI : -PI) - a;
- }
-
- /**
- * Sets the number of decimal places to show when formatting values. By default, they are
- * formatted to three decimal places.
- */
- public static void setToStringDecimalPlaces (int places) {
- if (places < 0) throw new IllegalArgumentException("Decimal places must be >= 0.");
- TO_STRING_DECIMAL_PLACES = places;
- }
-
- /**
- * Formats the supplied floating point value, truncated to the currently configured number of
- * decimal places. The value is also always preceded by a sign (e.g. +1.0 or -0.5).
- */
- public static String toString (float value)
- {
- StringBuilder buf = new StringBuilder();
- if (value >= 0) buf.append("+");
- else {
- buf.append("-");
- value = -value;
- }
- int ivalue = (int)value;
- buf.append(ivalue);
- if (TO_STRING_DECIMAL_PLACES > 0) {
- buf.append(".");
- for (int ii = 0; ii < TO_STRING_DECIMAL_PLACES; ii++) {
- value = (value - ivalue) * 10;
- ivalue = (int)value;
- buf.append(ivalue);
- }
- // trim trailing zeros
- for (int ii = 0; ii < TO_STRING_DECIMAL_PLACES-1; ii++) {
- if (buf.charAt(buf.length()-1) == '0') {
- buf.setLength(buf.length()-1);
- }
- }
- }
- return buf.toString();
- }
-
- protected static int TO_STRING_DECIMAL_PLACES = 3;
}
diff --git a/src/main/java/pythagoras/f/MathUtil.java b/src/main/java/pythagoras/f/MathUtil.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/src/main/java/pythagoras/f/MathUtil.java
@@ -0,0 +1,230 @@
+//
+// Pythagoras - a collection of geometry classes
+// http://github.com/samskivert/pythagoras
+
+package pythagoras.f;
+
+/**
+ * Math utility methods.
+ */
+public class MathUtil
+{
+ /** A small number. */
+ public static final float EPSILON = 0.00001f;
+
+ /** The circle constant, Tau (τ) http://tauday.com/ */
+ public static final float TAU = (float)(Math.PI * 2);
+
+ /** Twice Pi. */
+ public static final float TWO_PI = TAU;
+
+ /** Pi times one half. */
+ public static final float HALF_PI = (float)(Math.PI * 0.5);
+
+ /**
+ * A cheaper version of {@link Math#round} that doesn't handle the special cases.
+ */
+ public static int round (float v)
+ {
+ return (v < 0f) ? (int)(v - 0.5f) : (int)(v + 0.5f);
+ }
+
+ /**
+ * Returns the floor of v as an integer without calling the relatively expensive
+ * {@link Math#floor}.
+ */
+ public static int ifloor (float v)
+ {
+ int iv = (int)v;
+ return (v < 0f) ? ((iv == v || iv == Integer.MIN_VALUE) ? iv : (iv - 1)) : iv;
+ }
+
+ /**
+ * Returns the ceiling of v as an integer without calling the relatively expensive
+ * {@link Math#ceil}.
+ */
+ public static int iceil (float v)
+ {
+ int iv = (int)v;
+ return (v > 0f) ? ((iv == v || iv == Integer.MAX_VALUE) ? iv : (iv + 1)) : iv;
+ }
+
+ /**
+ * Clamps a value to the range [lower, upper].
+ */
+ public static float clamp (float v, float lower, float upper)
+ {
+ return Math.min(Math.max(v, lower), upper);
+ }
+
+ /**
+ * Rounds a value to the nearest multiple of a target.
+ */
+ public static float roundNearest (float v, float target)
+ {
+ target = Math.abs(target);
+ if (v >= 0) {
+ return target * FloatMath.floor((v + 0.5f * target) / target);
+ } else {
+ return target * FloatMath.ceil((v - 0.5f * target) / target);
+ }
+ }
+
+ /**
+ * Checks whether the value supplied is in [lower, upper].
+ */
+ public static boolean isWithin (float v, float lower, float upper)
+ {
+ return v >= lower && v <= upper;
+ }
+
+ /**
+ * Returns a random value according to the normal distribution with the provided mean and
+ * standard deviation.
+ *
+ * @param normal a normally distributed random value.
+ * @param mean the desired mean.
+ * @param stddev the desired standard deviation.
+ */
+ public static float normal (float normal, float mean, float stddev)
+ {
+ return stddev*normal + mean;
+ }
+
+ /**
+ * Returns a random value according to the exponential distribution with the provided mean.
+ *
+ * @param random a uniformly distributed random value.
+ * @param mean the desired mean.
+ */
+ public static float exponential (float random, float mean)
+ {
+ return -FloatMath.log(1f - random) * mean;
+ }
+
+ /**
+ * Linearly interpolates between two angles, taking the shortest path around the circle.
+ * This assumes that both angles are in [-pi, +pi].
+ */
+ public static float lerpa (float a1, float a2, float t)
+ {
+ float ma1 = mirrorAngle(a1), ma2 = mirrorAngle(a2);
+ float d = Math.abs(a2 - a1), md = Math.abs(ma1 - ma2);
+ return (d < md) ? lerp(a1, a2, t) : mirrorAngle(lerp(ma1, ma2, t));
+ }
+
+ /**
+ * Linearly interpolates between v1 and v2 by the parameter t.
+ */
+ public static float lerp (float v1, float v2, float t)
+ {
+ return v1 + t*(v2 - v1);
+ }
+
+ /**
+ * Determines whether two values are "close enough" to equal.
+ */
+ public static boolean epsilonEquals (float v1, float v2)
+ {
+ return Math.abs(v1 - v2) < EPSILON;
+ }
+
+ /**
+ * Returns the (shortest) distance between two angles, assuming that both angles are in
+ * [-pi, +pi].
+ */
+ public static float angularDistance (float a1, float a2)
+ {
+ float ma1 = mirrorAngle(a1), ma2 = mirrorAngle(a2);
+ return Math.min(Math.abs(a1 - a2), Math.abs(ma1 - ma2));
+ }
+
+ /**
+ * Returns the (shortest) difference between two angles, assuming that both angles are in
+ * [-pi, +pi].
+ */
+ public static float angularDifference (float a1, float a2)
+ {
+ float ma1 = mirrorAngle(a1), ma2 = mirrorAngle(a2);
+ float diff = a1 - a2, mdiff = ma2 - ma1;
+ return (Math.abs(diff) < Math.abs(mdiff)) ? diff : mdiff;
+ }
+
+ /**
+ * Returns an angle in the range [-pi, pi].
+ */
+ public static float normalizeAngle (float a)
+ {
+ while (a < -FloatMath.PI) {
+ a += TWO_PI;
+ }
+ while (a > FloatMath.PI) {
+ a -= TWO_PI;
+ }
+ return a;
+ }
+
+ /**
+ * Returns an angle in the range [0, 2pi].
+ */
+ public static float normalizeAnglePositive (float a)
+ {
+ while (a < 0f) {
+ a += TWO_PI;
+ }
+ while (a > TWO_PI) {
+ a -= TWO_PI;
+ }
+ return a;
+ }
+
+ /**
+ * Returns the mirror angle of the specified angle (assumed to be in [-pi, +pi]).
+ */
+ public static float mirrorAngle (float a)
+ {
+ return (a > 0f ? FloatMath.PI : -FloatMath.PI) - a;
+ }
+
+ /**
+ * Sets the number of decimal places to show when formatting values. By default, they are
+ * formatted to three decimal places.
+ */
+ public static void setToStringDecimalPlaces (int places) {
+ if (places < 0) throw new IllegalArgumentException("Decimal places must be >= 0.");
+ TO_STRING_DECIMAL_PLACES = places;
+ }
+
+ /**
+ * Formats the supplied floating point value, truncated to the currently configured number of
+ * decimal places. The value is also always preceded by a sign (e.g. +1.0 or -0.5).
+ */
+ public static String toString (float value)
+ {
+ StringBuilder buf = new StringBuilder();
+ if (value >= 0) buf.append("+");
+ else {
+ buf.append("-");
+ value = -value;
+ }
+ int ivalue = (int)value;
+ buf.append(ivalue);
+ if (TO_STRING_DECIMAL_PLACES > 0) {
+ buf.append(".");
+ for (int ii = 0; ii < TO_STRING_DECIMAL_PLACES; ii++) {
+ value = (value - ivalue) * 10;
+ ivalue = (int)value;
+ buf.append(ivalue);
+ }
+ // trim trailing zeros
+ for (int ii = 0; ii < TO_STRING_DECIMAL_PLACES-1; ii++) {
+ if (buf.charAt(buf.length()-1) == '0') {
+ buf.setLength(buf.length()-1);
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ protected static int TO_STRING_DECIMAL_PLACES = 3;
+}
diff --git a/src/main/java/pythagoras/f/NonUniformTransform.java b/src/main/java/pythagoras/f/NonUniformTransform.java
index 3ad2b58..c022507 100644
--- a/src/main/java/pythagoras/f/NonUniformTransform.java
+++ b/src/main/java/pythagoras/f/NonUniformTransform.java
@@ -165,7 +165,7 @@ public class NonUniformTransform extends AbstractTransform
float ntx = (otx*cosa - oty*sina) * scaleX + tx();
float nty = (otx*sina + oty*cosa) * scaleY + ty();
- float nrotation = FloatMath.normalizeAngle(rotation + other.rotation());
+ float nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
float nscaleX = scaleX * other.scaleX();
float nscaleY = scaleY * other.scaleY();
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
@@ -181,7 +181,7 @@ public class NonUniformTransform extends AbstractTransform
float sina = FloatMath.sin(other.rotation()), cosa = FloatMath.cos(other.rotation());
float ntx = (tx*cosa - ty*sina) * other.scaleX() + other.tx();
float nty = (tx*sina + ty*cosa) * other.scaleY() + other.ty();
- float nrotation = FloatMath.normalizeAngle(other.rotation() + rotation);
+ float nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
float nscaleX = other.scaleX() * scaleX;
float nscaleY = other.scaleY() * scaleY;
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
@@ -193,11 +193,11 @@ public class NonUniformTransform extends AbstractTransform
return other.lerp(this, -t); // TODO: is this correct?
}
- float ntx = FloatMath.lerpa(tx, other.tx(), t);
- float nty = FloatMath.lerpa(ty, other.ty(), t);
- float nrotation = FloatMath.lerpa(rotation, other.rotation(), t);
- float nscaleX = FloatMath.lerp(scaleX, other.scaleX(), t);
- float nscaleY = FloatMath.lerp(scaleY, other.scaleY(), t);
+ float ntx = MathUtil.lerpa(tx, other.tx(), t);
+ float nty = MathUtil.lerpa(ty, other.ty(), t);
+ float nrotation = MathUtil.lerpa(rotation, other.rotation(), t);
+ float nscaleX = MathUtil.lerp(scaleX, other.scaleX(), t);
+ float nscaleY = MathUtil.lerp(scaleY, other.scaleY(), t);
return new NonUniformTransform(nscaleX, nscaleY, nrotation, ntx, nty);
}
diff --git a/src/main/java/pythagoras/f/Points.java b/src/main/java/pythagoras/f/Points.java
index 6222d24..7ffe273 100644
--- a/src/main/java/pythagoras/f/Points.java
+++ b/src/main/java/pythagoras/f/Points.java
@@ -25,7 +25,7 @@ public class Points
* Returns the Euclidean distance between the specified two points.
*/
public static float distance (float x1, float y1, float x2, float y2) {
- return (float)Math.sqrt(distanceSq(x1, y1, x2, y2));
+ return FloatMath.sqrt(distanceSq(x1, y1, x2, y2));
}
/** Transforms a point as specified, storing the result in the point provided.
@@ -59,6 +59,6 @@ public class Points
* +x-y, -x-y, etc.
*/
public static String pointToString (float x, float y) {
- return FloatMath.toString(x) + FloatMath.toString(y);
+ return MathUtil.toString(x) + MathUtil.toString(y);
}
}
diff --git a/src/main/java/pythagoras/f/RigidTransform.java b/src/main/java/pythagoras/f/RigidTransform.java
index 25551ff..aabd26b 100644
--- a/src/main/java/pythagoras/f/RigidTransform.java
+++ b/src/main/java/pythagoras/f/RigidTransform.java
@@ -114,7 +114,7 @@ public class RigidTransform extends AbstractTransform
Vector nt = other.translation();
nt.rotateAndAdd(rotation, translation(), nt);
- float nrotation = FloatMath.normalizeAngle(rotation + other.rotation());
+ float nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
return new RigidTransform(nrotation, nt.x, nt.y);
}
@@ -126,7 +126,7 @@ public class RigidTransform extends AbstractTransform
Vector nt = translation();
nt.rotateAndAdd(other.rotation(), other.translation(), nt);
- float nrotation = FloatMath.normalizeAngle(other.rotation() + rotation);
+ float nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
return new RigidTransform(nrotation, nt.x, nt.y);
}
@@ -136,7 +136,7 @@ public class RigidTransform extends AbstractTransform
return other.lerp(this, -t); // TODO: is this correct?
}
Vector nt = translation().lerpLocal(other.translation(), t);
- return new RigidTransform(FloatMath.lerpa(rotation, other.rotation(), t), nt.x, nt.y);
+ return new RigidTransform(MathUtil.lerpa(rotation, other.rotation(), t), nt.x, nt.y);
}
@Override // from Transform
diff --git a/src/main/java/pythagoras/f/UniformTransform.java b/src/main/java/pythagoras/f/UniformTransform.java
index 2b46a6a..cd94244 100644
--- a/src/main/java/pythagoras/f/UniformTransform.java
+++ b/src/main/java/pythagoras/f/UniformTransform.java
@@ -136,7 +136,7 @@ public class UniformTransform extends AbstractTransform
Vector nt = other.translation();
nt.rotateScaleAndAdd(rotation, scale, translation(), nt);
- float nrotation = FloatMath.normalizeAngle(rotation + other.rotation());
+ float nrotation = MathUtil.normalizeAngle(rotation + other.rotation());
float nscale = scale * other.uniformScale();
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
}
@@ -150,7 +150,7 @@ public class UniformTransform extends AbstractTransform
Vector nt = translation();
nt.rotateScaleAndAdd(other.rotation(), other.uniformScale(),
other.translation(), nt);
- float nrotation = FloatMath.normalizeAngle(other.rotation() + rotation);
+ float nrotation = MathUtil.normalizeAngle(other.rotation() + rotation);
float nscale = other.uniformScale() * scale;
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
}
@@ -162,8 +162,8 @@ public class UniformTransform extends AbstractTransform
}
Vector nt = translation().lerpLocal(other.translation(), t);
- float nrotation = FloatMath.lerpa(rotation, other.rotation(), t);
- float nscale = FloatMath.lerp(scale, other.uniformScale(), t);
+ float nrotation = MathUtil.lerpa(rotation, other.rotation(), t);
+ float nscale = MathUtil.lerp(scale, other.uniformScale(), t);
return new UniformTransform(nscale, nrotation, nt.x, nt.y);
}
diff --git a/src/main/java/pythagoras/f/Vectors.java b/src/main/java/pythagoras/f/Vectors.java
index 9575995..9f7c4e4 100644
--- a/src/main/java/pythagoras/f/Vectors.java
+++ b/src/main/java/pythagoras/f/Vectors.java
@@ -74,6 +74,6 @@ public class Vectors
* +x-y, -x-y, etc.
*/
public static String vectorToString (float x, float y) {
- return FloatMath.toString(x) + FloatMath.toString(y);
+ return MathUtil.toString(x) + MathUtil.toString(y);
}
}