1 /** 2 * Contains fast approximations of some {@link java.lang.Math} operations. 3 * <p> 4 * By default, {@link java.lang.Math} methods will be used by all other DOML classes. In order to use the approximations in this class, start the JVM with the parameter <code>-DDOML.fastmath</code>. 5 * <p> 6 * There are two algorithms for approximating sin/cos: 7 * <ol> 8 * <li>arithmetic <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361815/view.html#msg361815">polynomial approximation</a> contributed by roquendm 9 * <li>theagentd's <a href="http://www.java-gaming.org/topics/extremely-fast-sine-cosine/36469/msg/346213/view.html#msg346213">linear interpolation</a> variant of Riven's algorithm from 10 * <a href="http://www.java-gaming.org/topics/extremely-fast-sine-cosine/36469/view.html">http://www.java-gaming.org/</a> 11 * </ol> 12 * By default, the first algorithm is being used. In order to use the second one, start the JVM with <code>-DDOML.sinLookup</code>. The lookup table bit length of the second algorithm can also be adjusted 13 * for improved accuracy via <code>-DDOML.sinLookup.bits=<n></code>, where <n> is the number of bits of the lookup table. 14 * 15 * @author Kai Burjack 16 */ 17 module doml.math; 18 19 import doml.options; 20 21 import std.traits: Select, isFloatingPoint, isIntegral; 22 23 import std.math: 24 math_pi = PI, 25 math_sin = sin, 26 math_rint = rint, 27 math_floor = floor, 28 math_cos = cos, 29 math_acos = acos, 30 math_sqrt = sqrt, 31 math_atan2 = atan2, 32 math_tan = tan, 33 math_asin = asin, 34 math_abs = abs, 35 math_ceil = ceil, 36 math_round = round, 37 math_exp = exp, 38 math_fma = fma, 39 math_pow = pow, 40 math_isInfinite = isInfinity; 41 42 // These aren't math library but oh well 43 import std.algorithm: 44 math_min = min, 45 math_max = max; 46 47 // I'm not indent matching this 48 import std.random: 49 math_random = Random, 50 math_unpredictableSeed = unpredictableSeed, 51 math_uniform = uniform; 52 53 import doml.rounding_mode; 54 55 // All the java comments are now wrong oh nooo 56 57 58 /* 59 * The MIT License 60 * 61 * Copyright (c) 2015-2021 DOML 62 +*%#$%# Translated by jordan4ibanez 63 * 64 * Permission is hereby granted, free of charge, to any person obtaining a copy 65 * of this software and associated documentation files (the "Software"), to deal 66 * in the Software without restriction, including without limitation the rights 67 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 68 * copies of the Software, and to permit persons to whom the Software is 69 * furnished to do so, subject to the following conditions: 70 * 71 * The above copyright notice and this permission notice shall be included in 72 * all copies or substantial portions of the Software. 73 * 74 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 75 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 76 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 77 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 78 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 79 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 80 * THE SOFTWARE. 81 */ 82 83 /* 84 * The following implementation of an approximation of sine and cosine was 85 * thankfully donated by Riven from http://java-gaming.org/. 86 * 87 * The code for linear interpolation was gratefully donated by theagentd 88 * from the same site. 89 */ 90 91 immutable double PI = math_pi; 92 immutable double PI2 = PI * 2.0; 93 immutable double PIHalf = PI * 0.5; 94 immutable double PI_4 = PI * 0.25; 95 immutable double PI_INV = 1.0 / PI; 96 97 98 // Credit: https://forum.dlang.org/post/bug-5900-3@http.d.puremagic.com%2Fissues%2F 99 /// Converts from degrees to radians. 100 @safe pure nothrow Select!(isFloatingPoint!T || isIntegral!T, T, double) 101 radians(T)(in T x) { 102 return x * (PI / 180); 103 } 104 105 /// Converts from radians to degrees. 106 @safe pure nothrow Select!(isFloatingPoint!T || isIntegral!T, T, double) 107 degrees(T)(in T x) { 108 return x / (PI / 180); 109 } 110 111 @safe pure nothrow Select!(isFloatingPoint!T || isIntegral!T, T, double) 112 math_signum(T)(in T x) { 113 return (T(0) < x) - (x < T(0)); 114 } 115 116 // Thanks to adr, a Java function in D 117 double longbits(long a) { 118 return *cast(double*) &a; 119 } 120 int doubleToRawIntBits(double a) { 121 return *cast(int*) &a; 122 } 123 long doubleToLongBits(double a) { 124 return *cast(long*) &a; 125 } 126 127 bool isInfinite(double value) { 128 return math_isInfinite(value); 129 } 130 131 132 private const double c1 = longbits(-4_628_199_217_061_079_772L); 133 private const double c2 = longbits(4_575_957_461_383_582_011L); 134 private const double c3 = longbits(-4_671_919_876_300_759_001L); 135 private const double c4 = longbits(4_523_617_214_285_661_942L); 136 private const double c5 = longbits(-4_730_215_272_828_025_532L); 137 private const double c6 = longbits(4_460_272_573_143_870_633L); 138 private const double c7 = longbits(-4_797_767_418_267_846_529L); 139 140 /** 141 * @author theagentd 142 */ 143 double sin_theagentd_arith(double x){ 144 double xi = math_floor((x + PI_4) * PI_INV); 145 double x_ = x - xi * PI; 146 double sign = (cast(int)xi & 1) * -2 + 1; 147 double x2 = x_ * x_; 148 double sin = x_; 149 double tx = x_ * x2; 150 sin += tx * c1; tx *= x2; 151 sin += tx * c2; tx *= x2; 152 sin += tx * c3; tx *= x2; 153 sin += tx * c4; tx *= x2; 154 sin += tx * c5; tx *= x2; 155 sin += tx * c6; tx *= x2; 156 sin += tx * c7; 157 return sign * sin; 158 } 159 160 /** 161 * Reference: <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361718/view.html#msg361718">http://www.java-gaming.org/</a> 162 */ 163 double sin_roquen_arith(double x) { 164 double xi = math_floor((x + PI_4) * PI_INV); 165 double x_ = x - xi * PI; 166 double sign = (cast(int)xi & 1) * -2 + 1; 167 double x2 = x_ * x_; 168 169 // code from sin_theagentd_arith: 170 // double sin = x_; 171 // double tx = x_ * x2; 172 // sin += tx * c1; tx *= x2; 173 // sin += tx * c2; tx *= x2; 174 // sin += tx * c3; tx *= x2; 175 // sin += tx * c4; tx *= x2; 176 // sin += tx * c5; tx *= x2; 177 // sin += tx * c6; tx *= x2; 178 // sin += tx * c7; 179 // return sign * sin; 180 181 double sin; 182 x_ = sign*x_; 183 sin = c7; 184 sin = sin*x2 + c6; 185 sin = sin*x2 + c5; 186 sin = sin*x2 + c4; 187 sin = sin*x2 + c3; 188 sin = sin*x2 + c2; 189 sin = sin*x2 + c1; 190 return x_ + x_*x2*sin; 191 } 192 193 private const double s5 = longbits(4_523_227_044_276_562_163L); 194 private const double s4 = longbits(-4_671_934_770_969_572_232L); 195 private const double s3 = longbits(4_575_957_211_482_072_852L); 196 private const double s2 = longbits(-4_628_199_223_918_090_387L); 197 private const double s1 = longbits(4_607_182_418_589_157_889L); 198 199 /** 200 * Reference: <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361815/view.html#msg361815">http://www.java-gaming.org/</a> 201 */ 202 double sin_roquen_9(double v) { 203 double i = math_rint(v*PI_INV); 204 double x = v - i * PI; 205 double qs = 1-2*(cast(int)i & 1); 206 double x2 = x*x; 207 double r; 208 x = qs*x; 209 r = s5; 210 r = r*x2 + s4; 211 r = r*x2 + s3; 212 r = r*x2 + s2; 213 r = r*x2 + s1; 214 return x*r; 215 } 216 217 private const double k1 = longbits(-4_628_199_217_061_079_959L); 218 private const double k2 = longbits(4_575_957_461_383_549_981L); 219 private const double k3 = longbits(-4_671_919_876_307_284_301L); 220 private const double k4 = longbits(4_523_617_213_632_129_738L); 221 private const double k5 = longbits(-4_730_215_344_060_517_252L); 222 private const double k6 = longbits(4_460_268_259_291_226_124L); 223 private const double k7 = longbits(-4_798_040_743_777_455_072L); 224 225 /** 226 * Reference: <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361815/view.html#msg361815">http://www.java-gaming.org/</a> 227 */ 228 double sin_roquen_newk(double v) { 229 double i = math_rint(v*PI_INV); 230 double x = v - i * PI; 231 double qs = 1-2*(cast(int)i & 1); 232 double x2 = x*x; 233 double r; 234 x = qs*x; 235 r = k7; 236 r = r*x2 + k6; 237 r = r*x2 + k5; 238 r = r*x2 + k4; 239 r = r*x2 + k3; 240 r = r*x2 + k2; 241 r = r*x2 + k1; 242 return x + x*x2*r; 243 } 244 245 double pow(double a, double b) { 246 return math_pow(a, b); 247 } 248 249 double sin(double rad) { 250 return math_sin(rad); 251 } 252 253 double cos(double rad) { 254 return math_cos(rad); 255 } 256 257 double cosFromSin(double sin, double angle) { 258 //if (Options.FASTMATH){ 259 // return math_sin(angle + PIHalf); 260 // } 261 // sin(x)^2 + cos(x)^2 = 1 262 double cos = math_sqrt(1.0 - sin * sin); 263 double a = angle + PIHalf; 264 double b = a - cast(int)(a / PI2) * PI2; 265 if (b < 0.0) 266 b = PI2 + b; 267 if (b >= PI) 268 return -cos; 269 return cos; 270 } 271 272 /* Other math functions not yet approximated */ 273 274 double sqrt(double r) { 275 return math_sqrt(r); 276 } 277 278 double invsqrt(double r) { 279 return 1.0 / math_sqrt(r); 280 } 281 282 double tan(double r) { 283 return math_tan(r); 284 } 285 286 double acos(double r) { 287 return math_acos(r); 288 } 289 290 double safeAcos(double v) { 291 if (v < -1.0) 292 return PI; 293 else if (v > +1.0) 294 return 0.0; 295 else 296 return math_acos(v); 297 } 298 299 300 double atan2(double y, double x) { 301 return math_atan2(y, x); 302 } 303 304 double asin(double r) { 305 return math_asin(r); 306 } 307 308 double abs(double r) { 309 return math_abs(r); 310 } 311 312 // Taken from runtime 313 bool equals(double a, double b, double delta) { 314 return doubleToLongBits(a) == doubleToLongBits(b) || abs(a - b) <= delta; 315 } 316 317 bool absEqualsOne(double r) { 318 return (doubleToLongBits(r) & 0x7FFFFFFFFFFFFFFFL) == 0x3FF0000000000000L; 319 } 320 321 int abs(int r) { 322 return math_abs(r); 323 } 324 325 int max(int x, int y) { 326 return math_max(x, y); 327 } 328 329 int min(int x, int y) { 330 return math_min(x, y); 331 } 332 333 double min(double a, double b) { 334 return a < b ? a : b; 335 } 336 337 double max(double a, double b) { 338 return a > b ? a : b; 339 } 340 341 ulong max(ulong a, ulong b) { 342 return a > b ? a : b; 343 } 344 345 double clamp(double a, double b, double val) { 346 return max(a,math_min(b,val)); 347 } 348 int clamp(int a, int b, int val) { 349 return max(a, math_min(b, val)); 350 } 351 352 double toRadians(double angles) { 353 return radians(angles); 354 } 355 356 double toDegrees(double angles) { 357 return degrees(angles); 358 } 359 360 double floor(double v) { 361 return math_floor(v); 362 } 363 364 double ceil(double v) { 365 return math_ceil(v); 366 } 367 368 long round(double v) { 369 return cast(long) math_round(v); 370 } 371 372 double exp(double a) { 373 return math_exp(a); 374 } 375 376 bool isFinite(double d) { 377 return math_abs(d) <= double.max; 378 } 379 380 double fma(double a, double b, double c) { 381 return math_fma(a, b, c); 382 } 383 384 int roundUsing(double v, int mode) { 385 switch (mode) { 386 case RoundingMode.TRUNCATE: 387 return cast(int) v; 388 case RoundingMode.CEILING: 389 return cast(int) math_ceil(v); 390 case RoundingMode.FLOOR: 391 return cast(int) math_floor(v); 392 case RoundingMode.HALF_DOWN: 393 return roundHalfDown(v); 394 case RoundingMode.HALF_UP: 395 return roundHalfUp(v); 396 case RoundingMode.HALF_EVEN: 397 return roundHalfEven(v); 398 default: { 399 return 0; 400 } 401 } 402 } 403 404 double lerp(double a, double b, double t) { 405 return math_fma(b - a, t, a); 406 } 407 408 double biLerp(double q00, double q10, double q01, double q11, double tx, double ty) { 409 double lerpX1 = lerp(q00, q10, tx); 410 double lerpX2 = lerp(q01, q11, tx); 411 return lerp(lerpX1, lerpX2, ty); 412 } 413 414 double triLerp(double q000, double q100, double q010, double q110, double q001, double q101, double q011, double q111, 415 double tx, double ty, double tz) { 416 double x00 = lerp(q000, q100, tx); 417 double x10 = lerp(q010, q110, tx); 418 double x01 = lerp(q001, q101, tx); 419 double x11 = lerp(q011, q111, tx); 420 double y0 = lerp(x00, x10, ty); 421 double y1 = lerp(x01, x11, ty); 422 return lerp(y0, y1, tz); 423 } 424 425 int roundHalfEven(double v) { 426 return cast(int) math_rint(v); 427 } 428 int roundHalfDown(double v) { 429 return (v > 0) ? cast(int) math_ceil(v - 0.5) : cast(int) math_floor(v + 0.5); 430 } 431 int roundHalfUp(double v) { 432 return (v > 0) ? cast(int) math_floor(v + 0.5) : cast(int) math_ceil(v - 0.5); 433 } 434 435 double random() { 436 math_random random = math_random(math_unpredictableSeed()); 437 return math_uniform(0.0, 1.0, random); 438 } 439 440 double safeAsin(double r) { 441 return r <= -1.0 ? -PIHalf : r >= 1.0 ? PIHalf : math_asin(r); 442 } 443 444 double signum(double v) { 445 return math_signum(v); 446 } 447 int signum(int v) { 448 int r; 449 r = math_signum(v); 450 r = (v >> 31) | (-v >>> 31); 451 return r; 452 } 453 int signum(long v) { 454 int r; 455 r = cast(int)math_signum(v); 456 r = cast(int) ((v >> 63) | (-v >>> 63)); 457 return r; 458 } 459