1 module gfm.math.funcs; 2 3 import std.math, 4 std.traits; 5 6 /// Returns: minimum of a and b. 7 T min(T)(T a, T b) pure nothrow 8 { 9 return a < b ? a : b; 10 } 11 12 /// Returns: maximum of a and b. 13 T max(T)(T a, T b) pure nothrow 14 { 15 return a > b ? a : b; 16 } 17 18 /// Convert from radians to degrees. 19 T degrees(T)(T x) pure if (!isIntegral!T) 20 { 21 return x * (180 / PI); 22 } 23 24 /// Convert from degrees to radians. 25 T radians(T)(T x) pure nothrow if (!isIntegral!T) 26 { 27 return x * (PI / 180); 28 } 29 30 /// Linear interpolation, akin to GLSL's mix. 31 S lerp(S, T)(S a, S b, T t) pure nothrow 32 { 33 return t * b + (1 - t) * a; 34 } 35 36 /// Clamp x in [min, max], akin to GLSL's clamp. 37 T clamp(T)(T x, T min, T max) pure nothrow 38 { 39 if (x < min) 40 return min; 41 else if (x > max) 42 return max; 43 else 44 return x; 45 } 46 47 /// Integer truncation. 48 long ltrunc(real x) nothrow // may be pure but trunc isn't pure 49 { 50 return cast(long)(trunc(x)); 51 } 52 53 /// Integer flooring. 54 long lfloor(real x) nothrow // may be pure but floor isn't pure 55 { 56 return cast(long)(floor(x)); 57 } 58 59 /// Returns: Fractional part of x. 60 T fract(T)(real x) nothrow 61 { 62 return x - lfloor(x); 63 } 64 65 /// Safe asin: input clamped to [-1, 1] 66 T safeAsin(T)(T x) pure nothrow 67 { 68 return asin(clamp!T(x, -1, 1)); 69 } 70 71 /// Safe acos: input clamped to [-1, 1] 72 T safeAcos(T)(T x) pure nothrow 73 { 74 return acos(clamp!T(x, -1, 1)); 75 } 76 77 /// Same as GLSL step function. 78 /// 0.0 is returned if x < edge, and 1.0 is returned otherwise. 79 T step(T)(T edge, T x) pure nothrow 80 { 81 return (x < edge) ? 0 : 1; 82 } 83 84 /// Same as GLSL smoothstep function. 85 /// See: http://en.wikipedia.org/wiki/Smoothstep 86 T smoothStep(T)(T a, T b, T t) pure nothrow 87 { 88 if (t <= a) 89 return 0; 90 else if (t >= b) 91 return 1; 92 else 93 { 94 T x = (t - a) / (b - a); 95 return x * x * (3 - 2 * x); 96 } 97 } 98 99 /// Returns: true of i is a power of 2. 100 bool isPowerOf2(T)(T i) nothrow if (isIntegral!T) 101 { 102 assert(i >= 0); 103 return (i != 0) && ((i & (i - 1)) == 0); 104 } 105 106 /// Integer log2 107 /// TODO: use bt intrinsics 108 int ilog2(T)(T i) nothrow if (isIntegral!T) 109 { 110 assert(i > 0); 111 assert(isPowerOf2(i)); 112 int result = 0; 113 while (i > 1) 114 { 115 i = i / 2; 116 result = result + 1; 117 } 118 return result; 119 } 120 121 /// Computes next power of 2. 122 int nextPowerOf2(int i) nothrow 123 { 124 int v = i - 1; 125 v |= v >> 1; 126 v |= v >> 2; 127 v |= v >> 4; 128 v |= v >> 8; 129 v |= v >> 16; 130 v++; 131 assert(isPowerOf2(v)); 132 return v; 133 } 134 135 /// Computes next power of 2. 136 long nextPowerOf2(long i) nothrow 137 { 138 long v = i - 1; 139 v |= v >> 1; 140 v |= v >> 2; 141 v |= v >> 4; 142 v |= v >> 8; 143 v |= v >> 16; 144 v |= v >> 32; 145 v++; 146 assert(isPowerOf2(v)); 147 return v; 148 } 149 150 /// Computes sin(x)/x accurately. 151 /// See: http://www.plunk.org/~hatch/rightway.php 152 T sinOverX(T)(T x) 153 { 154 if (1 + x * x == 1) 155 return 1; 156 else 157 return sin(x) / x; 158 } 159 160 161 /// Signed integer modulo a/b where the remainder is guaranteed to be in [0..b[, 162 /// even if a is negative. Only support positive dividers. 163 T moduloWrap(T)(T a, T b) pure nothrow if (isSigned!T) 164 in 165 { 166 assert(b > 0); 167 } 168 body 169 { 170 if (a >= 0) 171 a = a % b; 172 else 173 { 174 auto rem = a % b; 175 x = (rem == 0) ? 0 : (-rem + b); 176 } 177 178 assert(x >= 0 && x < b); 179 return x; 180 } 181 182 unittest 183 { 184 assert(nextPowerOf2(13) == 16); 185 } 186 187 /** 188 * Find the root of a linear polynomial a + b x = 0 189 * Returns: Number of roots. 190 */ 191 size_t solveLinear(T)(T a, T b, out T root) pure nothrow if (isFloatingPoint!T) 192 { 193 if (b == 0) 194 { 195 return 0; 196 } 197 else 198 { 199 root = -a / b; 200 return 1; 201 } 202 } 203 204 205 /** 206 * Finds the root roots of a quadratic polynomial a + b x + c x^2 = 0 207 * Params: 208 * outRoots = array of root results, should have room for at least 2 elements. 209 * Returns: Number of roots in outRoots. 210 */ 211 size_t solveQuadratic(T)(T a, T b, T c, T[] outRoots) pure nothrow if (isFloatingPoint!T) 212 { 213 assert(outRoots.length >= 2); 214 if (c == 0) 215 return solveLinear(a, b, outRoots[0]); 216 217 T delta = b * b - 4 * a * c; 218 if (delta < 0.0 ) 219 return 0; 220 221 delta = sqrt(delta); 222 T oneOver2a = 0.5 / a; 223 224 outRoots[0] = oneOver2a * (-b - delta); 225 outRoots[1] = oneOver2a * (-b + delta); 226 return 2; 227 } 228 229 230 /** 231 * Finds the roots of a cubic polynomial a + b x + c x^2 + d x^3 = 0 232 * Params: 233 * outRoots = array of root results, should have room for at least 2 elements. 234 * Returns: Number of roots in outRoots. 235 * See_also: $(WEB www.codeguru.com/forum/archive/index.php/t-265551.html) 236 */ 237 size_t solveCubic(T)(T a, T b, T c, T d, T[] outRoots) pure nothrow if (isFloatingPoint!T) 238 { 239 assert(outRoots.length >= 3); 240 if (d == 0) 241 return solveQuadratic(a, b, c, outRoots); 242 243 // adjust coefficients 244 T a1 = c / d, 245 a2 = b / d, 246 a3 = a / d; 247 248 T Q = (a1 * a1 - 3 * a2) / 9, 249 R = (2 * a1 * a1 * a1 - 9 * a1 * a2 + 27 * a3) / 54; 250 251 T Qcubed = Q * Q * Q; 252 T d2 = Qcubed - R * R; 253 254 if (d2 >= 0) 255 { 256 // 3 real roots 257 if (Q < 0.0) 258 return 0; 259 T P = R / sqrt(Qcubed); 260 261 assert(-1 <= P && P <= 1); 262 T theta = acos(P); 263 T sqrtQ = sqrt(Q); 264 265 outRoots[0] = -2 * sqrtQ * cos(theta / 3) - a1 / 3; 266 outRoots[1] = -2 * sqrtQ * cos((theta + 2 * PI) / 3) - a1 / 3; 267 outRoots[2] = -2 * sqrtQ * cos((theta + 4 * PI) / 3) - a1 / 3; 268 return 3; 269 } 270 else 271 { 272 // 1 real root 273 T e = (sqrt(-d) + abs(R)) ^^ cast(T)(1.0 / 3.0); 274 if (R > 0) 275 e = -e; 276 outRoots[0] = e + Q / e - a1 / 3.0; 277 return 1; 278 } 279 } 280 281 /** 282 * Returns the roots of a quartic polynomial a + b x + c x^2 + d x^3 + e x^4 = 0 283 * 284 * Returns number of roots. roots slice should have room for up to 4 elements. 285 * Bugs: doesn't pass unit-test! 286 * See_also: $(WEB mathworld.wolfram.com/QuarticEquation.html) 287 */ 288 size_t solveQuartic(T)(T a, T b, T c, T d, T e, T[] roots) pure nothrow if (isFloatingPoint!T) 289 { 290 assert(roots.length >= 4); 291 292 if (e == 0) 293 return solveCubic(a, b, c, d, roots); 294 295 // Adjust coefficients 296 T a0 = a / e, 297 a1 = b / e, 298 a2 = c / e, 299 a3 = d / e; 300 301 // Find a root for the following cubic equation: 302 // y^3 - a2 y^2 + (a1 a3 - 4 a0) y + (4 a2 a0 - a1 ^2 - a3^2 a0) = 0 303 // aka Resolvent cubic 304 T b0 = 4 * a2 * a0 - a1 * a1 - a3 * a3 * a0; 305 T b1 = a1 * a3 - 4 * a0; 306 T b2 = -a2; 307 T[3] resolventCubicRoots; 308 size_t numRoots = solveCubic!T(b0, b1, b2, 1, resolventCubicRoots[]); 309 assert(numRoots == 3); 310 T y = resolventCubicRoots[0]; 311 if (y < resolventCubicRoots[1]) y = resolventCubicRoots[1]; 312 if (y < resolventCubicRoots[2]) y = resolventCubicRoots[2]; 313 314 // Compute R, D & E 315 T R = 0.25f * a3 * a3 - a2 + y; 316 if (R < 0.0) 317 return 0; 318 R = sqrt(R); 319 320 T D = void, 321 E = void; 322 if (R == 0) 323 { 324 T d1 = 0.75f * a3 * a3 - 2 * a2; 325 T d2 = 2 * sqrt(y * y - 4 * a0); 326 D = sqrt(d1 + d2) * 0.5f; 327 E = sqrt(d1 - d2) * 0.5f; 328 } 329 else 330 { 331 T Rsquare = R * R; 332 T Rrec = 1 / R; 333 T d1 = 0.75f * a3 * a3 - Rsquare - 2 * a2; 334 T d2 = 0.25f * Rrec * (4 * a3 * a2 - 8 * a1 - a3 * a3 * a3); 335 D = sqrt(d1 + d2) * 0.5f; 336 E = sqrt(d1 - d2) * 0.5f; 337 } 338 339 // Compute the 4 roots 340 a3 *= -0.25f; 341 R *= 0.5f; 342 343 roots[0] = a3 + R + D; 344 roots[1] = a3 + R - D; 345 roots[2] = a3 - R + E; 346 roots[3] = a3 - R - E; 347 return 4; 348 } 349 350 351 unittest 352 { 353 bool arrayContainsRoot(double[] arr, double root) 354 { 355 foreach(e; arr) 356 if (abs(e - root) < 1e-7) 357 return true; 358 return false; 359 } 360 361 // test quadratic 362 { 363 double[3] roots; 364 size_t numRoots = solveCubic!double(-2, -3 / 2.0, 3 / 4.0, 1 / 4.0, roots[]); 365 assert(numRoots == 3); 366 assert(arrayContainsRoot(roots[], -4)); 367 assert(arrayContainsRoot(roots[], -1)); 368 assert(arrayContainsRoot(roots[], 2)); 369 } 370 371 // test quartic 372 { 373 double[4] roots; 374 size_t numRoots = solveQuartic!double(0, -2, -1, 2, 1, roots[]); 375 376 assert(numRoots == 4); 377 assert(arrayContainsRoot(roots[], -2)); 378 assert(arrayContainsRoot(roots[], -1)); 379 assert(arrayContainsRoot(roots[], 0)); 380 assert(arrayContainsRoot(roots[], 1)); 381 } 382 }