1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.math; 33 34 import java.util.Random; 35 36 /** 37 * <code>FastMath</code> provides 'fast' math approximations and float equivalents of Math 38 * functions. These are all used as static values and functions. 39 * 40 * @author Various 41 * @version $Id: FastMath.java,v 1.45 2007/08/26 08:44:20 irrisor Exp $ 42 */ 43 final public class FastMath { 44 FastMath()45 private FastMath() { 46 } 47 /** A "close to zero" double epsilon value for use*/ 48 public static final double DBL_EPSILON = 2.220446049250313E-16d; 49 /** A "close to zero" float epsilon value for use*/ 50 public static final float FLT_EPSILON = 1.1920928955078125E-7f; 51 /** A "close to zero" float epsilon value for use*/ 52 public static final float ZERO_TOLERANCE = 0.0001f; 53 public static final float ONE_THIRD = 1f / 3f; 54 /** The value PI as a float. (180 degrees) */ 55 public static final float PI = (float) Math.PI; 56 /** The value 2PI as a float. (360 degrees) */ 57 public static final float TWO_PI = 2.0f * PI; 58 /** The value PI/2 as a float. (90 degrees) */ 59 public static final float HALF_PI = 0.5f * PI; 60 /** The value PI/4 as a float. (45 degrees) */ 61 public static final float QUARTER_PI = 0.25f * PI; 62 /** The value 1/PI as a float. */ 63 public static final float INV_PI = 1.0f / PI; 64 /** The value 1/(2PI) as a float. */ 65 public static final float INV_TWO_PI = 1.0f / TWO_PI; 66 /** A value to multiply a degree value by, to convert it to radians. */ 67 public static final float DEG_TO_RAD = PI / 180.0f; 68 /** A value to multiply a radian value by, to convert it to degrees. */ 69 public static final float RAD_TO_DEG = 180.0f / PI; 70 /** A precreated random object for random numbers. */ 71 public static final Random rand = new Random(System.currentTimeMillis()); 72 73 /** 74 * Returns true if the number is a power of 2 (2,4,8,16...) 75 * 76 * A good implementation found on the Java boards. note: a number is a power 77 * of two if and only if it is the smallest number with that number of 78 * significant bits. Therefore, if you subtract 1, you know that the new 79 * number will have fewer bits, so ANDing the original number with anything 80 * less than it will give 0. 81 * 82 * @param number 83 * The number to test. 84 * @return True if it is a power of two. 85 */ isPowerOfTwo(int number)86 public static boolean isPowerOfTwo(int number) { 87 return (number > 0) && (number & (number - 1)) == 0; 88 } 89 nearestPowerOfTwo(int number)90 public static int nearestPowerOfTwo(int number) { 91 return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); 92 } 93 94 /** 95 * Linear interpolation from startValue to endValue by the given percent. 96 * Basically: ((1 - percent) * startValue) + (percent * endValue) 97 * 98 * @param scale 99 * scale value to use. if 1, use endValue, if 0, use startValue. 100 * @param startValue 101 * Begining value. 0% of f 102 * @param endValue 103 * ending value. 100% of f 104 * @return The interpolated value between startValue and endValue. 105 */ interpolateLinear(float scale, float startValue, float endValue)106 public static float interpolateLinear(float scale, float startValue, float endValue) { 107 if (startValue == endValue) { 108 return startValue; 109 } 110 if (scale <= 0f) { 111 return startValue; 112 } 113 if (scale >= 1f) { 114 return endValue; 115 } 116 return ((1f - scale) * startValue) + (scale * endValue); 117 } 118 119 /** 120 * Linear interpolation from startValue to endValue by the given percent. 121 * Basically: ((1 - percent) * startValue) + (percent * endValue) 122 * 123 * @param scale 124 * scale value to use. if 1, use endValue, if 0, use startValue. 125 * @param startValue 126 * Begining value. 0% of f 127 * @param endValue 128 * ending value. 100% of f 129 * @param store a vector3f to store the result 130 * @return The interpolated value between startValue and endValue. 131 */ interpolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store)132 public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) { 133 if (store == null) { 134 store = new Vector3f(); 135 } 136 store.x = interpolateLinear(scale, startValue.x, endValue.x); 137 store.y = interpolateLinear(scale, startValue.y, endValue.y); 138 store.z = interpolateLinear(scale, startValue.z, endValue.z); 139 return store; 140 } 141 142 /** 143 * Linear interpolation from startValue to endValue by the given percent. 144 * Basically: ((1 - percent) * startValue) + (percent * endValue) 145 * 146 * @param scale 147 * scale value to use. if 1, use endValue, if 0, use startValue. 148 * @param startValue 149 * Begining value. 0% of f 150 * @param endValue 151 * ending value. 100% of f 152 * @return The interpolated value between startValue and endValue. 153 */ interpolateLinear(float scale, Vector3f startValue, Vector3f endValue)154 public static Vector3f interpolateLinear(float scale, Vector3f startValue, Vector3f endValue) { 155 return interpolateLinear(scale, startValue, endValue, null); 156 } 157 158 /** 159 * Linear extrapolation from startValue to endValue by the given scale. 160 * if scale is between 0 and 1 this method returns the same result as interpolateLinear 161 * if the scale is over 1 the value is linearly extrapolated. 162 * Note that the end value is the value for a scale of 1. 163 * @param scale the scale for extrapolation 164 * @param startValue the starting value (scale = 0) 165 * @param endValue the end value (scale = 1) 166 * @return an extrapolation for the given parameters 167 */ extrapolateLinear(float scale, float startValue, float endValue)168 public static float extrapolateLinear(float scale, float startValue, float endValue) { 169 // if (scale <= 0f) { 170 // return startValue; 171 // } 172 return ((1f - scale) * startValue) + (scale * endValue); 173 } 174 175 /** 176 * Linear extrapolation from startValue to endValue by the given scale. 177 * if scale is between 0 and 1 this method returns the same result as interpolateLinear 178 * if the scale is over 1 the value is linearly extrapolated. 179 * Note that the end value is the value for a scale of 1. 180 * @param scale the scale for extrapolation 181 * @param startValue the starting value (scale = 0) 182 * @param endValue the end value (scale = 1) 183 * @param store an initialized vector to store the return value 184 * @return an extrapolation for the given parameters 185 */ extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store)186 public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue, Vector3f store) { 187 if (store == null) { 188 store = new Vector3f(); 189 } 190 // if (scale <= 1f) { 191 // return interpolateLinear(scale, startValue, endValue, store); 192 // } 193 store.x = extrapolateLinear(scale, startValue.x, endValue.x); 194 store.y = extrapolateLinear(scale, startValue.y, endValue.y); 195 store.z = extrapolateLinear(scale, startValue.z, endValue.z); 196 return store; 197 } 198 199 /** 200 * Linear extrapolation from startValue to endValue by the given scale. 201 * if scale is between 0 and 1 this method returns the same result as interpolateLinear 202 * if the scale is over 1 the value is linearly extrapolated. 203 * Note that the end value is the value for a scale of 1. 204 * @param scale the scale for extrapolation 205 * @param startValue the starting value (scale = 0) 206 * @param endValue the end value (scale = 1) 207 * @return an extrapolation for the given parameters 208 */ extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue)209 public static Vector3f extrapolateLinear(float scale, Vector3f startValue, Vector3f endValue) { 210 return extrapolateLinear(scale, startValue, endValue, null); 211 } 212 213 /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation. 214 * here is the interpolation matrix 215 * m = [ 0.0 1.0 0.0 0.0 ] 216 * [-T 0.0 T 0.0 ] 217 * [ 2T T-3 3-2T -T ] 218 * [-T 2-T T-2 T ] 219 * where T is the curve tension 220 * the result is a value between p1 and p2, t=0 for p1, t=1 for p2 221 * @param u value from 0 to 1 222 * @param T The tension of the curve 223 * @param p0 control point 0 224 * @param p1 control point 1 225 * @param p2 control point 2 226 * @param p3 control point 3 227 * @return catmull-Rom interpolation 228 */ interpolateCatmullRom(float u, float T, float p0, float p1, float p2, float p3)229 public static float interpolateCatmullRom(float u, float T, float p0, float p1, float p2, float p3) { 230 float c1, c2, c3, c4; 231 c1 = p1; 232 c2 = -1.0f * T * p0 + T * p2; 233 c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3; 234 c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3; 235 236 return (float) (((c4 * u + c3) * u + c2) * u + c1); 237 } 238 239 /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation. 240 * here is the interpolation matrix 241 * m = [ 0.0 1.0 0.0 0.0 ] 242 * [-T 0.0 T 0.0 ] 243 * [ 2T T-3 3-2T -T ] 244 * [-T 2-T T-2 T ] 245 * where T is the tension of the curve 246 * the result is a value between p1 and p2, t=0 for p1, t=1 for p2 247 * @param u value from 0 to 1 248 * @param T The tension of the curve 249 * @param p0 control point 0 250 * @param p1 control point 1 251 * @param p2 control point 2 252 * @param p3 control point 3 253 * @param store a Vector3f to store the result 254 * @return catmull-Rom interpolation 255 */ interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store)256 public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) { 257 if (store == null) { 258 store = new Vector3f(); 259 } 260 store.x = interpolateCatmullRom(u, T, p0.x, p1.x, p2.x, p3.x); 261 store.y = interpolateCatmullRom(u, T, p0.y, p1.y, p2.y, p3.y); 262 store.z = interpolateCatmullRom(u, T, p0.z, p1.z, p2.z, p3.z); 263 return store; 264 } 265 266 /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation. 267 * here is the interpolation matrix 268 * m = [ 0.0 1.0 0.0 0.0 ] 269 * [-T 0.0 T 0.0 ] 270 * [ 2T T-3 3-2T -T ] 271 * [-T 2-T T-2 T ] 272 * where T is the tension of the curve 273 * the result is a value between p1 and p2, t=0 for p1, t=1 for p2 274 * @param u value from 0 to 1 275 * @param T The tension of the curve 276 * @param p0 control point 0 277 * @param p1 control point 1 278 * @param p2 control point 2 279 * @param p3 control point 3 280 * @return catmull-Rom interpolation 281 */ interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3)282 public static Vector3f interpolateCatmullRom(float u, float T, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { 283 return interpolateCatmullRom(u, T, p0, p1, p2, p3, null); 284 } 285 286 /**Interpolate a spline between at least 4 control points following the Bezier equation. 287 * here is the interpolation matrix 288 * m = [ -1.0 3.0 -3.0 1.0 ] 289 * [ 3.0 -6.0 3.0 0.0 ] 290 * [ -3.0 3.0 0.0 0.0 ] 291 * [ 1.0 0.0 0.0 0.0 ] 292 * where T is the curve tension 293 * the result is a value between p1 and p3, t=0 for p1, t=1 for p3 294 * @param u value from 0 to 1 295 * @param p0 control point 0 296 * @param p1 control point 1 297 * @param p2 control point 2 298 * @param p3 control point 3 299 * @return Bezier interpolation 300 */ interpolateBezier(float u, float p0, float p1, float p2, float p3)301 public static float interpolateBezier(float u, float p0, float p1, float p2, float p3) { 302 float oneMinusU = 1.0f - u; 303 float oneMinusU2 = oneMinusU * oneMinusU; 304 float u2 = u * u; 305 return p0 * oneMinusU2 * oneMinusU 306 + 3.0f * p1 * u * oneMinusU2 307 + 3.0f * p2 * u2 * oneMinusU 308 + p3 * u2 * u; 309 } 310 311 /**Interpolate a spline between at least 4 control points following the Bezier equation. 312 * here is the interpolation matrix 313 * m = [ -1.0 3.0 -3.0 1.0 ] 314 * [ 3.0 -6.0 3.0 0.0 ] 315 * [ -3.0 3.0 0.0 0.0 ] 316 * [ 1.0 0.0 0.0 0.0 ] 317 * where T is the tension of the curve 318 * the result is a value between p1 and p3, t=0 for p1, t=1 for p3 319 * @param u value from 0 to 1 320 * @param p0 control point 0 321 * @param p1 control point 1 322 * @param p2 control point 2 323 * @param p3 control point 3 324 * @param store a Vector3f to store the result 325 * @return Bezier interpolation 326 */ interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store)327 public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, Vector3f store) { 328 if (store == null) { 329 store = new Vector3f(); 330 } 331 store.x = interpolateBezier(u, p0.x, p1.x, p2.x, p3.x); 332 store.y = interpolateBezier(u, p0.y, p1.y, p2.y, p3.y); 333 store.z = interpolateBezier(u, p0.z, p1.z, p2.z, p3.z); 334 return store; 335 } 336 337 /**Interpolate a spline between at least 4 control points following the Bezier equation. 338 * here is the interpolation matrix 339 * m = [ -1.0 3.0 -3.0 1.0 ] 340 * [ 3.0 -6.0 3.0 0.0 ] 341 * [ -3.0 3.0 0.0 0.0 ] 342 * [ 1.0 0.0 0.0 0.0 ] 343 * where T is the tension of the curve 344 * the result is a value between p1 and p3, t=0 for p1, t=1 for p3 345 * @param u value from 0 to 1 346 * @param p0 control point 0 347 * @param p1 control point 1 348 * @param p2 control point 2 349 * @param p3 control point 3 350 * @return Bezier interpolation 351 */ interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3)352 public static Vector3f interpolateBezier(float u, Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { 353 return interpolateBezier(u, p0, p1, p2, p3, null); 354 } 355 356 /** 357 * Compute the lenght on a catmull rom spline between control point 1 and 2 358 * @param p0 control point 0 359 * @param p1 control point 1 360 * @param p2 control point 2 361 * @param p3 control point 3 362 * @param startRange the starting range on the segment (use 0) 363 * @param endRange the end range on the segment (use 1) 364 * @param curveTension the curve tension 365 * @return the length of the segment 366 */ getCatmullRomP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, float startRange, float endRange, float curveTension)367 public static float getCatmullRomP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3, float startRange, float endRange, float curveTension) { 368 369 float epsilon = 0.001f; 370 float middleValue = (startRange + endRange) * 0.5f; 371 Vector3f start = p1.clone(); 372 if (startRange != 0) { 373 FastMath.interpolateCatmullRom(startRange, curveTension, p0, p1, p2, p3, start); 374 } 375 Vector3f end = p2.clone(); 376 if (endRange != 1) { 377 FastMath.interpolateCatmullRom(endRange, curveTension, p0, p1, p2, p3, end); 378 } 379 Vector3f middle = FastMath.interpolateCatmullRom(middleValue, curveTension, p0, p1, p2, p3); 380 float l = end.subtract(start).length(); 381 float l1 = middle.subtract(start).length(); 382 float l2 = end.subtract(middle).length(); 383 float len = l1 + l2; 384 if (l + epsilon < len) { 385 l1 = getCatmullRomP1toP2Length(p0, p1, p2, p3, startRange, middleValue, curveTension); 386 l2 = getCatmullRomP1toP2Length(p0, p1, p2, p3, middleValue, endRange, curveTension); 387 } 388 l = l1 + l2; 389 return l; 390 } 391 392 /** 393 * Compute the lenght on a bezier spline between control point 1 and 2 394 * @param p0 control point 0 395 * @param p1 control point 1 396 * @param p2 control point 2 397 * @param p3 control point 3 398 * @return the length of the segment 399 */ getBezierP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3)400 public static float getBezierP1toP2Length(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { 401 float delta = 0.02f, t = 0.0f, result = 0.0f; 402 Vector3f v1 = p0.clone(), v2 = new Vector3f(); 403 while (t <= 1.0f) { 404 FastMath.interpolateBezier(t, p0, p1, p2, p3, v2); 405 result += v1.subtractLocal(v2).length(); 406 v1.set(v2); 407 t += delta; 408 } 409 return result; 410 } 411 412 /** 413 * Returns the arc cosine of an angle given in radians.<br> 414 * Special cases: 415 * <ul><li>If fValue is smaller than -1, then the result is PI. 416 * <li>If the argument is greater than 1, then the result is 0.</ul> 417 * @param fValue The angle, in radians. 418 * @return fValue's acos 419 * @see java.lang.Math#acos(double) 420 */ acos(float fValue)421 public static float acos(float fValue) { 422 if (-1.0f < fValue) { 423 if (fValue < 1.0f) { 424 return (float) Math.acos(fValue); 425 } 426 427 return 0.0f; 428 } 429 430 return PI; 431 } 432 433 /** 434 * Returns the arc sine of an angle given in radians.<br> 435 * Special cases: 436 * <ul><li>If fValue is smaller than -1, then the result is -HALF_PI. 437 * <li>If the argument is greater than 1, then the result is HALF_PI.</ul> 438 * @param fValue The angle, in radians. 439 * @return fValue's asin 440 * @see java.lang.Math#asin(double) 441 */ asin(float fValue)442 public static float asin(float fValue) { 443 if (-1.0f < fValue) { 444 if (fValue < 1.0f) { 445 return (float) Math.asin(fValue); 446 } 447 448 return HALF_PI; 449 } 450 451 return -HALF_PI; 452 } 453 454 /** 455 * Returns the arc tangent of an angle given in radians.<br> 456 * @param fValue The angle, in radians. 457 * @return fValue's atan 458 * @see java.lang.Math#atan(double) 459 */ atan(float fValue)460 public static float atan(float fValue) { 461 return (float) Math.atan(fValue); 462 } 463 464 /** 465 * A direct call to Math.atan2. 466 * @param fY 467 * @param fX 468 * @return Math.atan2(fY,fX) 469 * @see java.lang.Math#atan2(double, double) 470 */ atan2(float fY, float fX)471 public static float atan2(float fY, float fX) { 472 return (float) Math.atan2(fY, fX); 473 } 474 475 /** 476 * Rounds a fValue up. A call to Math.ceil 477 * @param fValue The value. 478 * @return The fValue rounded up 479 * @see java.lang.Math#ceil(double) 480 */ ceil(float fValue)481 public static float ceil(float fValue) { 482 return (float) Math.ceil(fValue); 483 } 484 485 /** 486 * Fast Trig functions for x86. This forces the trig functiosn to stay 487 * within the safe area on the x86 processor (-45 degrees to +45 degrees) 488 * The results may be very slightly off from what the Math and StrictMath 489 * trig functions give due to rounding in the angle reduction but it will be 490 * very very close. 491 * 492 * note: code from wiki posting on java.net by jeffpk 493 */ reduceSinAngle(float radians)494 public static float reduceSinAngle(float radians) { 495 radians %= TWO_PI; // put us in -2PI to +2PI space 496 if (Math.abs(radians) > PI) { // put us in -PI to +PI space 497 radians = radians - (TWO_PI); 498 } 499 if (Math.abs(radians) > HALF_PI) {// put us in -PI/2 to +PI/2 space 500 radians = PI - radians; 501 } 502 503 return radians; 504 } 505 506 /** 507 * Returns sine of a value. 508 * 509 * note: code from wiki posting on java.net by jeffpk 510 * 511 * @param fValue 512 * The value to sine, in radians. 513 * @return The sine of fValue. 514 * @see java.lang.Math#sin(double) 515 */ sin2(float fValue)516 public static float sin2(float fValue) { 517 fValue = reduceSinAngle(fValue); // limits angle to between -PI/2 and +PI/2 518 if (Math.abs(fValue) <= Math.PI / 4) { 519 return (float) Math.sin(fValue); 520 } 521 522 return (float) Math.cos(Math.PI / 2 - fValue); 523 } 524 525 /** 526 * Returns cos of a value. 527 * 528 * @param fValue 529 * The value to cosine, in radians. 530 * @return The cosine of fValue. 531 * @see java.lang.Math#cos(double) 532 */ cos2(float fValue)533 public static float cos2(float fValue) { 534 return sin2(fValue + HALF_PI); 535 } 536 cos(float v)537 public static float cos(float v) { 538 return (float) Math.cos(v); 539 } 540 sin(float v)541 public static float sin(float v) { 542 return (float) Math.sin(v); 543 } 544 545 /** 546 * Returns E^fValue 547 * @param fValue Value to raise to a power. 548 * @return The value E^fValue 549 * @see java.lang.Math#exp(double) 550 */ exp(float fValue)551 public static float exp(float fValue) { 552 return (float) Math.exp(fValue); 553 } 554 555 /** 556 * Returns Absolute value of a float. 557 * @param fValue The value to abs. 558 * @return The abs of the value. 559 * @see java.lang.Math#abs(float) 560 */ abs(float fValue)561 public static float abs(float fValue) { 562 if (fValue < 0) { 563 return -fValue; 564 } 565 return fValue; 566 } 567 568 /** 569 * Returns a number rounded down. 570 * @param fValue The value to round 571 * @return The given number rounded down 572 * @see java.lang.Math#floor(double) 573 */ floor(float fValue)574 public static float floor(float fValue) { 575 return (float) Math.floor(fValue); 576 } 577 578 /** 579 * Returns 1/sqrt(fValue) 580 * @param fValue The value to process. 581 * @return 1/sqrt(fValue) 582 * @see java.lang.Math#sqrt(double) 583 */ invSqrt(float fValue)584 public static float invSqrt(float fValue) { 585 return (float) (1.0f / Math.sqrt(fValue)); 586 } 587 fastInvSqrt(float x)588 public static float fastInvSqrt(float x) { 589 float xhalf = 0.5f * x; 590 int i = Float.floatToIntBits(x); // get bits for floating value 591 i = 0x5f375a86 - (i >> 1); // gives initial guess y0 592 x = Float.intBitsToFloat(i); // convert bits back to float 593 x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy 594 return x; 595 } 596 597 /** 598 * Returns the log base E of a value. 599 * @param fValue The value to log. 600 * @return The log of fValue base E 601 * @see java.lang.Math#log(double) 602 */ log(float fValue)603 public static float log(float fValue) { 604 return (float) Math.log(fValue); 605 } 606 607 /** 608 * Returns the logarithm of value with given base, calculated as log(value)/log(base), 609 * so that pow(base, return)==value (contributed by vear) 610 * @param value The value to log. 611 * @param base Base of logarithm. 612 * @return The logarithm of value with given base 613 */ log(float value, float base)614 public static float log(float value, float base) { 615 return (float) (Math.log(value) / Math.log(base)); 616 } 617 618 /** 619 * Returns a number raised to an exponent power. fBase^fExponent 620 * @param fBase The base value (IE 2) 621 * @param fExponent The exponent value (IE 3) 622 * @return base raised to exponent (IE 8) 623 * @see java.lang.Math#pow(double, double) 624 */ pow(float fBase, float fExponent)625 public static float pow(float fBase, float fExponent) { 626 return (float) Math.pow(fBase, fExponent); 627 } 628 629 /** 630 * Returns the value squared. fValue ^ 2 631 * @param fValue The vaule to square. 632 * @return The square of the given value. 633 */ sqr(float fValue)634 public static float sqr(float fValue) { 635 return fValue * fValue; 636 } 637 638 /** 639 * Returns the square root of a given value. 640 * @param fValue The value to sqrt. 641 * @return The square root of the given value. 642 * @see java.lang.Math#sqrt(double) 643 */ sqrt(float fValue)644 public static float sqrt(float fValue) { 645 return (float) Math.sqrt(fValue); 646 } 647 648 /** 649 * Returns the tangent of a value. If USE_FAST_TRIG is enabled, an approximate value 650 * is returned. Otherwise, a direct value is used. 651 * @param fValue The value to tangent, in radians. 652 * @return The tangent of fValue. 653 * @see java.lang.Math#tan(double) 654 */ tan(float fValue)655 public static float tan(float fValue) { 656 return (float) Math.tan(fValue); 657 } 658 659 /** 660 * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise 661 * @param iValue The integer to examine. 662 * @return The integer's sign. 663 */ sign(int iValue)664 public static int sign(int iValue) { 665 if (iValue > 0) { 666 return 1; 667 } 668 if (iValue < 0) { 669 return -1; 670 } 671 return 0; 672 } 673 674 /** 675 * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise 676 * @param fValue The float to examine. 677 * @return The float's sign. 678 */ sign(float fValue)679 public static float sign(float fValue) { 680 return Math.signum(fValue); 681 } 682 683 /** 684 * Given 3 points in a 2d plane, this function computes if the points going from A-B-C 685 * are moving counter clock wise. 686 * @param p0 Point 0. 687 * @param p1 Point 1. 688 * @param p2 Point 2. 689 * @return 1 If they are CCW, -1 if they are not CCW, 0 if p2 is between p0 and p1. 690 */ counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2)691 public static int counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) { 692 float dx1, dx2, dy1, dy2; 693 dx1 = p1.x - p0.x; 694 dy1 = p1.y - p0.y; 695 dx2 = p2.x - p0.x; 696 dy2 = p2.y - p0.y; 697 if (dx1 * dy2 > dy1 * dx2) { 698 return 1; 699 } 700 if (dx1 * dy2 < dy1 * dx2) { 701 return -1; 702 } 703 if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0)) { 704 return -1; 705 } 706 if ((dx1 * dx1 + dy1 * dy1) < (dx2 * dx2 + dy2 * dy2)) { 707 return 1; 708 } 709 return 0; 710 } 711 712 /** 713 * Test if a point is inside a triangle. 1 if the point is on the ccw side, 714 * -1 if the point is on the cw side, and 0 if it is on neither. 715 * @param t0 First point of the triangle. 716 * @param t1 Second point of the triangle. 717 * @param t2 Third point of the triangle. 718 * @param p The point to test. 719 * @return Value 1 or -1 if inside triangle, 0 otherwise. 720 */ pointInsideTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p)721 public static int pointInsideTriangle(Vector2f t0, Vector2f t1, Vector2f t2, Vector2f p) { 722 int val1 = counterClockwise(t0, t1, p); 723 if (val1 == 0) { 724 return 1; 725 } 726 int val2 = counterClockwise(t1, t2, p); 727 if (val2 == 0) { 728 return 1; 729 } 730 if (val2 != val1) { 731 return 0; 732 } 733 int val3 = counterClockwise(t2, t0, p); 734 if (val3 == 0) { 735 return 1; 736 } 737 if (val3 != val1) { 738 return 0; 739 } 740 return val3; 741 } 742 743 /** 744 * A method that computes normal for a triangle defined by three vertices. 745 * @param v1 first vertex 746 * @param v2 second vertex 747 * @param v3 third vertex 748 * @return a normal for the face 749 */ computeNormal(Vector3f v1, Vector3f v2, Vector3f v3)750 public static Vector3f computeNormal(Vector3f v1, Vector3f v2, Vector3f v3) { 751 Vector3f a1 = v1.subtract(v2); 752 Vector3f a2 = v3.subtract(v2); 753 return a2.crossLocal(a1).normalizeLocal(); 754 } 755 756 /** 757 * Returns the determinant of a 4x4 matrix. 758 */ determinant(double m00, double m01, double m02, double m03, double m10, double m11, double m12, double m13, double m20, double m21, double m22, double m23, double m30, double m31, double m32, double m33)759 public static float determinant(double m00, double m01, double m02, 760 double m03, double m10, double m11, double m12, double m13, 761 double m20, double m21, double m22, double m23, double m30, 762 double m31, double m32, double m33) { 763 764 double det01 = m20 * m31 - m21 * m30; 765 double det02 = m20 * m32 - m22 * m30; 766 double det03 = m20 * m33 - m23 * m30; 767 double det12 = m21 * m32 - m22 * m31; 768 double det13 = m21 * m33 - m23 * m31; 769 double det23 = m22 * m33 - m23 * m32; 770 return (float) (m00 * (m11 * det23 - m12 * det13 + m13 * det12) - m01 771 * (m10 * det23 - m12 * det03 + m13 * det02) + m02 772 * (m10 * det13 - m11 * det03 + m13 * det01) - m03 773 * (m10 * det12 - m11 * det02 + m12 * det01)); 774 } 775 776 /** 777 * Returns a random float between 0 and 1. 778 * 779 * @return A random float between <tt>0.0f</tt> (inclusive) to 780 * <tt>1.0f</tt> (exclusive). 781 */ nextRandomFloat()782 public static float nextRandomFloat() { 783 return rand.nextFloat(); 784 } 785 786 /** 787 * Returns a random float between min and max. 788 * 789 * @return A random int between <tt>min</tt> (inclusive) to 790 * <tt>max</tt> (inclusive). 791 */ nextRandomInt(int min, int max)792 public static int nextRandomInt(int min, int max) { 793 return (int) (nextRandomFloat() * (max - min + 1)) + min; 794 } 795 nextRandomInt()796 public static int nextRandomInt() { 797 return rand.nextInt(); 798 } 799 800 /** 801 * Converts a point from Spherical coordinates to Cartesian (using positive 802 * Y as up) and stores the results in the store var. 803 */ sphericalToCartesian(Vector3f sphereCoords, Vector3f store)804 public static Vector3f sphericalToCartesian(Vector3f sphereCoords, 805 Vector3f store) { 806 store.y = sphereCoords.x * FastMath.sin(sphereCoords.z); 807 float a = sphereCoords.x * FastMath.cos(sphereCoords.z); 808 store.x = a * FastMath.cos(sphereCoords.y); 809 store.z = a * FastMath.sin(sphereCoords.y); 810 811 return store; 812 } 813 814 /** 815 * Converts a point from Cartesian coordinates (using positive Y as up) to 816 * Spherical and stores the results in the store var. (Radius, Azimuth, 817 * Polar) 818 */ cartesianToSpherical(Vector3f cartCoords, Vector3f store)819 public static Vector3f cartesianToSpherical(Vector3f cartCoords, 820 Vector3f store) { 821 float x = cartCoords.x; 822 if (x == 0) { 823 x = FastMath.FLT_EPSILON; 824 } 825 store.x = FastMath.sqrt((x * x) 826 + (cartCoords.y * cartCoords.y) 827 + (cartCoords.z * cartCoords.z)); 828 store.y = FastMath.atan(cartCoords.z / x); 829 if (x < 0) { 830 store.y += FastMath.PI; 831 } 832 store.z = FastMath.asin(cartCoords.y / store.x); 833 return store; 834 } 835 836 /** 837 * Converts a point from Spherical coordinates to Cartesian (using positive 838 * Z as up) and stores the results in the store var. 839 */ sphericalToCartesianZ(Vector3f sphereCoords, Vector3f store)840 public static Vector3f sphericalToCartesianZ(Vector3f sphereCoords, 841 Vector3f store) { 842 store.z = sphereCoords.x * FastMath.sin(sphereCoords.z); 843 float a = sphereCoords.x * FastMath.cos(sphereCoords.z); 844 store.x = a * FastMath.cos(sphereCoords.y); 845 store.y = a * FastMath.sin(sphereCoords.y); 846 847 return store; 848 } 849 850 /** 851 * Converts a point from Cartesian coordinates (using positive Z as up) to 852 * Spherical and stores the results in the store var. (Radius, Azimuth, 853 * Polar) 854 */ cartesianZToSpherical(Vector3f cartCoords, Vector3f store)855 public static Vector3f cartesianZToSpherical(Vector3f cartCoords, 856 Vector3f store) { 857 float x = cartCoords.x; 858 if (x == 0) { 859 x = FastMath.FLT_EPSILON; 860 } 861 store.x = FastMath.sqrt((x * x) 862 + (cartCoords.y * cartCoords.y) 863 + (cartCoords.z * cartCoords.z)); 864 store.z = FastMath.atan(cartCoords.z / x); 865 if (x < 0) { 866 store.z += FastMath.PI; 867 } 868 store.y = FastMath.asin(cartCoords.y / store.x); 869 return store; 870 } 871 872 /** 873 * Takes an value and expresses it in terms of min to max. 874 * 875 * @param val - 876 * the angle to normalize (in radians) 877 * @return the normalized angle (also in radians) 878 */ normalize(float val, float min, float max)879 public static float normalize(float val, float min, float max) { 880 if (Float.isInfinite(val) || Float.isNaN(val)) { 881 return 0f; 882 } 883 float range = max - min; 884 while (val > max) { 885 val -= range; 886 } 887 while (val < min) { 888 val += range; 889 } 890 return val; 891 } 892 893 /** 894 * @param x 895 * the value whose sign is to be adjusted. 896 * @param y 897 * the value whose sign is to be used. 898 * @return x with its sign changed to match the sign of y. 899 */ copysign(float x, float y)900 public static float copysign(float x, float y) { 901 if (y >= 0 && x <= -0) { 902 return -x; 903 } else if (y < 0 && x >= 0) { 904 return -x; 905 } else { 906 return x; 907 } 908 } 909 910 /** 911 * Take a float input and clamp it between min and max. 912 * 913 * @param input 914 * @param min 915 * @param max 916 * @return clamped input 917 */ clamp(float input, float min, float max)918 public static float clamp(float input, float min, float max) { 919 return (input < min) ? min : (input > max) ? max : input; 920 } 921 922 /** 923 * Clamps the given float to be between 0 and 1. 924 * 925 * @param input 926 * @return input clamped between 0 and 1. 927 */ saturate(float input)928 public static float saturate(float input) { 929 return clamp(input, 0f, 1f); 930 } 931 932 /** 933 * Converts a single precision (32 bit) floating point value 934 * into half precision (16 bit). 935 * 936 * <p>Source: <a href="http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf"> 937 * http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf</a><br><strong>broken link</strong> 938 * 939 * @param half The half floating point value as a short. 940 * @return floating point value of the half. 941 */ convertHalfToFloat(short half)942 public static float convertHalfToFloat(short half) { 943 switch ((int) half) { 944 case 0x0000: 945 return 0f; 946 case 0x8000: 947 return -0f; 948 case 0x7c00: 949 return Float.POSITIVE_INFINITY; 950 case 0xfc00: 951 return Float.NEGATIVE_INFINITY; 952 // TODO: Support for NaN? 953 default: 954 return Float.intBitsToFloat(((half & 0x8000) << 16) 955 | (((half & 0x7c00) + 0x1C000) << 13) 956 | ((half & 0x03FF) << 13)); 957 } 958 } 959 convertFloatToHalf(float flt)960 public static short convertFloatToHalf(float flt) { 961 if (Float.isNaN(flt)) { 962 throw new UnsupportedOperationException("NaN to half conversion not supported!"); 963 } else if (flt == Float.POSITIVE_INFINITY) { 964 return (short) 0x7c00; 965 } else if (flt == Float.NEGATIVE_INFINITY) { 966 return (short) 0xfc00; 967 } else if (flt == 0f) { 968 return (short) 0x0000; 969 } else if (flt == -0f) { 970 return (short) 0x8000; 971 } else if (flt > 65504f) { 972 // max value supported by half float 973 return 0x7bff; 974 } else if (flt < -65504f) { 975 return (short) (0x7bff | 0x8000); 976 } else if (flt > 0f && flt < 5.96046E-8f) { 977 return 0x0001; 978 } else if (flt < 0f && flt > -5.96046E-8f) { 979 return (short) 0x8001; 980 } 981 982 int f = Float.floatToIntBits(flt); 983 return (short) (((f >> 16) & 0x8000) 984 | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) 985 | ((f >> 13) & 0x03ff)); 986 } 987 } 988