1// tFundamentals.h 
2// 
3// Core math functions needed by the rest or of the module as well as for external use. Functions include trigonometric 
4// functions, intervals, angle manipulation, power functions, and other analytic functions. 
5// 
6// Copyright (c) 2004, 2017, 2019, 2020 Tristan Grimmer. 
7// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
8// granted, provided that the above copyright notice and this permission notice appear in all copies. 
9// 
10// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
11// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
12// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
13// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
14// PERFORMANCE OF THIS SOFTWARE. 
15 
16#pragma once 
17#include <math.h> 
18#include <functional> 
19#include "Foundation/tPlatform.h" 
20#include "Foundation/tConstants.h" 
21namespace tMath 
22
23 
24 
25enum class tAngleMode 
26
27 Radians, // Circle divided into 2Pi angle units. 
28 Degrees, // Circle divided into 360 angle units. 
29 Norm256, // Circle divided into 256 angle units. 
30 NormOne, // Circle divided into one angle unit. 
31 Default = Radians
32}; 
33 
34 
35// Interval Notation. When you see [a,b] or (a,b) the square bracket means include the endpoint and the rounded brackets 
36// mean exclude. See http://www.coolmath.com/algebra/07-solving-inequalities/03-interval-notation-01. When a function 
37// takes a bias argument, a Low bias will cause the return value to include the lower extent of the interval and exclude 
38// the high extent. A High bias will exclude the low end and include the high end. As a notational convenience When a 
39// function takes a bias argument, we'll write the interval as [(a,b)] and the table below shows how to interpret it: 
40// 
41// Bias Interval 
42// Full/Outer [a,b] 
43// Low/Left [a,b) 
44// High/Right (a,b] 
45// Center/Inner (a,b) 
46enum class tIntervalBias 
47
48 Full, Outer = Full
49 Low, Left = Low
50 High, Right = High
51 Center, Inner = Center 
52}; 
53 
54// These are handy functions that return appropriate comparison operators when computing biased intervals. 
55std::function<bool(float,float)> tBiasLess(tIntervalBias); 
56std::function<bool(float,float)> tBiasGreater(tIntervalBias); 
57 
58inline int tAbs(int val) { return (val < 0) ? -val : val; } 
59inline float tAbs(float val) { return (val < 0.0f) ? -val : val; } 
60inline double tAbs(double val) { return (val < 0.0) ? -val : val; } 
61 
62// A mathematical modulo. Does not just return remainder like the % operator. i.e. Handles negatives 'properly'. 
63// The & and fmod functions are also here but called more appropriately tRem (for remainder).  
64inline int tMod(int n, int d) { int m = n % d; if (m < 0) m = (d < 0) ? m - d : m + d; return m; } 
65inline float tMod(float n, float d) { float m = fmod(n, d); if (m < 0.0f) m = (d < 0.0f) ? m - d : m + d; return m; } 
66inline int tRem(int n, int d) { return n % d; } 
67inline float tRem(float n, float d) { return fmodf(n, d); } 
68 
69template<typename T> inline T tMin(const T& a, const T& b) { return a < b ? a : b; } 
70template<typename T> inline T tMax(const T& a, const T& b) { return a > b ? a : b; } 
71template<typename T> inline T tMin(const T& a, const T& b, const T& c) { T ab = a < b ? a : b; return ab < c ? ab : c; } 
72template<typename T> inline T tMax(const T& a, const T& b, const T& c) { T ab = a > b ? a : b; return ab > c ? ab : c; } 
73template<typename T> inline T tMin(const T& a, const T& b, const T& c, const T& d) { T ab = a < b ? a : b; T cd = c < d ? c : d; return ab < cd ? ab : cd; } 
74template<typename T> inline T tMax(const T& a, const T& b, const T& c, const T& d) { T ab = a > b ? a : b; T cd = c > d ? c : d; return ab > cd ? ab : cd; } 
75template<typename T> inline T tClamp(T val, T min, T max) { return (val < min) ? min : ((val > max) ? max : val); } 
76template<typename T> inline T tClampMin(T val, T min) { return (val < min) ? min : val; } 
77template<typename T> inline T tClampMax(T val, T max) { return (val > max) ? max : val; } 
78template<typename T> inline T tSaturate(T val) { return (val < T(0)) ? T(0) : ((val > T(1)) ? T(1) : val); } 
79template<typename T> inline bool tInIntervalII(const T val, const T min, const T max) { if ((val >= min) && (val <= max)) return true; return false; } // Implements val E [min, max] 
80template<typename T> inline bool tInIntervalIE(const T val, const T min, const T max) { if ((val >= min) && (val < max)) return true; return false; } // Implements val E [min, max) 
81template<typename T> inline bool tInIntervalEI(const T val, const T min, const T max) { if ((val > min) && (val <= max)) return true; return false; } // Implements val E (min, max] 
82template<typename T> inline bool tInIntervalEE(const T val, const T min, const T max) { if ((val > min) && (val < max)) return true; return false; } // Implements val E (min, max) 
83template<typename T> inline bool tInInterval(const T val, const T min, const T max) /* Returns val E [min, max] */ { return tInIntervalII(val, min, max); } 
84template<typename T> inline bool tInRange(const T val, const T min, const T max) /* Returns val E [min, max] */ { return tInIntervalII(val, min, max); } 
85template<typename T> inline T tSign(T val) { return val < T(0) ? T(-1) : val > T(0) ? T(1) : T(0); } 
86template<typename T> inline T tBinarySign(T val) { return val < T(0) ? T(-1) : T(1); } // Same as Sign but does not return 0 ever. Two return values only. 
87template<typename T> inline bool tIsZero(T a) { return (a == 0); } 
88template<typename T> inline bool tApproxEqual(T a, T b, float e = Epsilon) { return (tAbs(a-b) < e); } 
89template<typename T> inline bool tEquals(T a, T b) { return a == b; } 
90template<typename T> inline bool tNotEqual(T a, T b) { return a != b; } 
91 
92// For the 'ti' versions of the below functions, the 'i' means 'in-place' (ref var). Supports chaining. 
93template<typename T> inline T& tiClamp(T& val, T min, T max) { val = (val < min) ? min : ((val > max) ? max : val); return val; } 
94template<typename T> inline T& tiClampMin(T& val, T min) { val = (val < min) ? min : val; return val; } 
95template<typename T> inline T& tiClampMax(T& val, T max) { val = (val > max) ? max : val; return val; } 
96template<typename T> inline T& tiSaturate(T& val) { val = (val < T(0)) ? T(0) : ((val > T(1)) ? T(1) : val); return val; } 
97 
98struct tDivt { int Quotient; int Remainder; }; 
99tDivt tDiv(int numerator, int denominator); 
100struct tDiv32t { int32 Quotient; int32 Remainder; }; 
101tDiv32t tDiv32(int32 numerator, int32 denominator); 
102struct tDivU32t { uint32 Quotient; uint32 Remainder; }; 
103tDivU32t tDivU32(uint32 numerator, uint32 denominator); 
104struct tDiv64t { int64 Quotient; int64 Remainder; }; 
105tDiv64t tDiv64(int64 numerator, int64 denominator); 
106struct tDivU64t { uint64 Quotient; uint64 Remainder; }; 
107tDivU64t tDivU64(uint64 numerator, uint64 denominator); 
108 
109// Use this instead of casting to int. The only difference is it rounds instead of truncating and is way faster -- The 
110// FPU stays in rounding mode so pipeline not flushed. 
111inline int tFloatToInt(float val) { return int(val + 0.5f); } 
112 
113// tCeiling and tFloor both need to change the FPU from round mode to truncate. Could have performance hit. 
114inline float tCeiling(float v) { return ceilf(v); } 
115inline float tFloor(float v) { return floorf(v); } 
116inline float tRound(float v) { return floorf(v + 0.5f); } 
117 
118// This round lets you say round to the nearest [value]. For example, tRound(5.17f, 0.2f) = 5.2f 
119float tRound(float v, float nearest); 
120 
121// For the 'ti' versions of the below functions, the 'i' means 'in-place' (ref var). A ref is also returned so it's 
122// easy to chain calls. 
123inline float& tiCeiling(float& v) { v = ceilf(v); return v; } 
124inline float& tiFloor(float& v) { v = floorf(v); return v; } 
125inline float& tiRound(float& v) { v = floorf(v + 0.5f); return v; } 
126inline float& tiRound(float& v, float nearest) { v = tRound(v, nearest); return v; } 
127inline int& tiAbs(int& v) { v = (v < 0) ? -v : v; return v; } 
128inline float& tiAbs(float& v) { v = (v < 0.0f) ? -v : v; return v; } 
129inline double& tiAbs(double& v) { v = (v < 0.0) ? -v : v; return v; } 
130 
131// The following Abs function deserves a little explanation. Some linear algebra texts use the term absolute value and 
132// norm interchangeably. Others suggest that the absolute value of a matrix is the matrix with each component 
133// being the absolute value of the original. This is what the following template function returns -- not the L2 norm 
134// (scalar length). 
135template <typename T> inline T tAbs(T v) { T result; for (int c = 0; c < T::GetNumComponents(); c++) result[c] = tAbs(v[c]); return result; } 
136inline float tFrac(float val) { return tAbs(val - float(int(val))); } 
137inline float tSquare(float v) { return v*v; } 
138inline float tCube(float v) { return v*v*v; } 
139inline float tSqrt(float x) { return sqrtf(x); } 
140float tSqrtFast(float x); 
141inline float tRecipSqrt(float x) { return 1.0f/sqrtf(x); } 
142float tRecipSqrtFast(float x); 
143 
144inline float tDegToRad(float deg) { return deg * Pi / 180.0f; } 
145inline float tRadToDeg(float rad) { return rad * 180.0f / Pi; } 
146inline float tSin(float x) { return sinf(x); } 
147inline float tSinFast(float x); // For x E [0, Pi/2]. 
148inline float tCos(float x) { return cosf(x); } 
149inline float tCosFast(float x) /* For x E [0, Pi/2]. */ { float s = tSinFast(x); return tSqrtFast(1.0f - s*s); } 
150inline void tCosSin(float& cos, float& sin, float x); 
151inline void tCosSinFast(float& cos, float& sin, float x); // For x E [0, Pi/2]. 
152inline float tTan(float x) { return tanf(x); } 
153inline float tArcSin(float x) { return asinf(x); } 
154inline float tArcCos(float x) { return acosf(x); } 
155inline float tArcTan(float y, float x) /* Order is y, x. Returns angle of a slope (rise/run). */ { return atan2f(y, x); } 
156inline float tArcTan(float m) { return atanf(m); } 
157inline float tExp(float x) { return expf(x); } 
158inline float tLog(float x) /* Natural logarithm. */ { return logf(x); } 
159inline float tSa(float x) /* Unnormalized (sampling) sinc. */ { if (x == 0.0f) return 1.0f; return tSin(x) / x; } 
160inline float tSinc(float x) /* Normalized sinc. */ { if (x == 0.0f) return 1.0f; float pix = Pi*x; return tSin(pix) / pix; } 
161 
162// For the 'ti' versions of the below functions, the 'i' means 'in-place' (ref var). 
163inline float& tiDegToRad(float& ang) { ang = ang * Pi / 180.0f; return ang; } 
164inline float& tiRadToDeg(float& ang) { ang = ang * 180.0f / Pi; return ang; } 
165 
166inline float tPow(float a, float b) { return powf(a, b); } 
167inline double tPow(double a, double b) { return pow(a, b); } 
168 
169// Returns integral base 2 logarithm. If v is <= 0 returns MinInt32. If v is a power of 2 you will get an exact 
170// result. If v is not a power of two it will return the logarithm of the next lowest power of 2. For example, 
171// Log2(2) = 1, Log2(3) = 1, and Log2(4) = 2. 
172inline int tLog2(int v); 
173 
174// For the 'ti' versions of the functions, the 'i' means 'in-place' (ref var). 
175inline bool tIsPower2(int v) { if (v < 1) return false; return (v & (v-1)) ? false : true; } 
176inline uint& tiNextLowerPower2(uint& v) { uint pow2 = 1; while (pow2 < v) pow2 <<= 1; pow2 >>= 1; v = pow2 ? pow2 : 1; return v; } 
177inline uint tNextLowerPower2(uint v) { uint pow2 = 1; while (pow2 < v) pow2 <<= 1; pow2 >>= 1; return pow2 ? pow2 : 1; } 
178inline uint& tiNextHigherPower2(uint& v) { uint pow2 = 1; while (pow2 <= v) pow2 <<= 1; v = pow2; return v; } 
179inline uint tNextHigherPower2(uint v) { uint pow2 = 1; while (pow2 <= v) pow2 <<= 1; return pow2; } 
180inline uint& tiClosestPower2(uint& v) { if (tIsPower2(v)) return v; int h = tNextHigherPower2(v); int l = tNextLowerPower2(v); v = ((h - v) < (v - l)) ? h : l; return v; } 
181inline uint tClosestPower2(uint v) { if (tIsPower2(v)) return v; int h = tNextHigherPower2(v); int l = tNextLowerPower2(v); return ((h - v) < (v - l)) ? h : l; } 
182float& tiNormalizeAngle(float& angle, tIntervalBias = tIntervalBias::Low); // Results in angle E [(-Pi,Pi)]. 
183inline float tNormalizedAngle(float angle, tIntervalBias bias = tIntervalBias::Low) { tiNormalizeAngle(angle, bias); return angle; } 
184float& tiNormalizeAngle2Pi(float& angle, tIntervalBias = tIntervalBias::Low); // Results in angle E [(0,2Pi)]. 
185inline float tNormalizedAngle2Pi(float angle, tIntervalBias bias = tIntervalBias::Low) { tiNormalizeAngle2Pi(angle, bias); return angle; } 
186 
187// Gets the range (y) value of a normal distribution with mean = 0, and given variance. Pass in the domain (x) value. 
188inline float tNormalDist(float variance, float x) { return tPow(2*Pi*variance, -0.5f) * tExp(-tPow(x, 2.0f) / (2.0f*variance)); } 
189 
190// These functions compute f(x) with x E [0, 1]. When not flipped f(0) = 0 and f(1) = 1. Furthermore, 0 <= f(x) <= 1. 
191// Curve shape may be controlled by one or more constant (c) arguments. These functions always exist in the unit square, 
192// even when flipped. Essentially flipping in x flips around the x=1/2 line, and y about the y=1/2 line. 
193enum tUnitFlip 
194
195 tUnitFlip_None = 0x00000000
196 tUnitFlip_X = 0x00000001
197 tUnitFlip_Y = 0x00000002
198 tUnitFlip_XY = tUnitFlip_X | tUnitFlip_Y 
199}; 
200 
201// Plot: http://www.wolframalpha.com/input/?i=Plot%5B%28Sin%28x*pi-pi%2F2%29%2B1%29%2F2%2C+%7Bx%2C0%2C1%7D%5D 
202float tUnitSin(float x, uint32 = tUnitFlip_None); 
203 
204// Plot: http://www.wolframalpha.com/input/?i=Plot%5BSin%28x*pi%2F2%29%2C+%7Bx%2C0%2C1%7D%5D 
205float tUnitSinHalf(float x, uint32 = tUnitFlip_None); 
206 
207// Plot: http://www.wolframalpha.com/input/?i=Plot%5Bpow%28x%2C+c%29%2C+%7Bx%2C0%2C1%7D%2C+%7Bc%2C0.1%2C3%7D%5D 
208// c E (0, inf). c < 1 pulls towards top left. c > 1 pulls towards bottom right. 
209float tUnitPow(float x, float c = 1.0f, uint32 = tUnitFlip_None); 
210 
211// Plot: http://www.wolframalpha.com/input/?i=Plot+Piecewise%5B%7B%7Bpow%282x%2C+3%29%2F2%2C+x%3C0.5%7D%2C+%7B1+-+%28pow%281-2%28x-1%2F2%29%2C+3%29%29%2F2%2C+x%3E0.5%7D%7D%5D%2C+%7Bx%2C0%2C1%7D 
212float tUnitPowPlateau(float x, float c = 1.0f, uint32 = tUnitFlip_None); 
213 
214// Plot: http://www.wolframalpha.com/input/?i=Plot+sqrt%281+-+%281-x%29%5E2%29%2C+%7Bx%2C0%2C1%7D 
215float tUnitArc(float x, uint32 = tUnitFlip_None);  
216 
217 
218
219 
220 
221// Implementation below this line. 
222 
223 
224inline std::function<bool(float,float)> tMath::tBiasLess(tIntervalBias bias
225
226 switch (bias
227
228 case tIntervalBias::Right
229 case tIntervalBias::Outer
230 return [](float a, float b) { return a <= b; }; 
231 
232 case tIntervalBias::Left
233 case tIntervalBias::Inner
234 default
235 return [](float a, float b) { return a < b; }; 
236
237
238 
239 
240inline std::function<bool(float,float)> tMath::tBiasGreater(tIntervalBias bias
241
242 switch (bias
243
244 case tIntervalBias::Left
245 case tIntervalBias::Outer
246 return [](float a, float b) { return a >= b; }; 
247 
248 case tIntervalBias::Right
249 case tIntervalBias::Inner
250 default
251 return [](float a, float b) { return a > b; }; 
252
253
254 
255 
256inline tMath::tDivt tMath::tDiv(int numerator, int denominator
257
258 div_t d = div(numerator, denominator); 
259 tDivt r
260 r.Quotient = d.quot
261 r.Remainder = d.rem
262 return r
263
264 
265 
266inline tMath::tDiv32t tMath::tDiv32(int32 numerator, int32 denominator
267
268 div_t d = div(numerator, denominator); 
269 tDiv32t r
270 r.Quotient = d.quot
271 r.Remainder = d.rem
272 return r
273
274 
275 
276inline tMath::tDivU32t tMath::tDivU32(uint32 numerator, uint32 denominator
277
278 tDivU32t r
279 r.Quotient = numerator/denominator
280 r.Remainder = numerator - r.Quotient*denominator
281 return r
282
283 
284 
285inline tMath::tDiv64t tMath::tDiv64(int64 numerator, int64 denominator
286
287 lldiv_t d = div(numerator, denominator); 
288 tDiv64t r
289 r.Quotient = d.quot
290 r.Remainder = d.rem
291 return r
292
293 
294 
295inline tMath::tDivU64t tMath::tDivU64(uint64 numerator, uint64 denominator
296
297 tDivU64t r
298 r.Quotient = numerator/denominator
299 r.Remainder = numerator - r.Quotient*denominator
300 return r
301
302 
303 
304inline float tMath::tRound(float v, float nearest
305
306 if (tApproxEqual(nearest, 0.0f)) 
307 return v
308 
309 tiClamp(nearest, 0.000001f, 1000000.0f); 
310 float numNearests = v/nearest
311 float rnded = tRound(numNearests); 
312 return rnded * nearest
313
314 
315 
316inline int tMath::tLog2(int x
317
318 if (x <= 0
319 return 0x80000000
320 
321 float f = float(x); 
322 return ((( *(uint32*)((void*)&f) ) & 0x7f800000) >> 23) - 127
323
324 
325 
326inline float tMath::tSqrtFast(float x
327
328 #ifdef PLATFORM_WINDOWS 
329 __m128 in = _mm_set_ss(x); 
330 __m128 out = _mm_sqrt_ss(in); 
331 return *(float*)(&out); 
332 #else 
333 return tSqrt(x); 
334 #endif 
335
336 
337 
338inline float tMath::tRecipSqrtFast(float x
339
340 #ifdef PLATFORM_WINDOWS 
341 __m128 in = _mm_set_ss(x); 
342 __m128 out = _mm_rsqrt_ss(in); 
343 return *(float*)(&out); 
344 #else 
345 return (1.0f / tSqrt(x)); 
346 #endif 
347
348 
349 
350inline float tMath::tSinFast(float x
351
352 float x2 = x*x
353 float r = 7.61e-03f
354 r *= x2
355 r -= 1.6605e-01f
356 r *= x2
357 r += 1.0f
358 r *= x
359 
360 return r
361
362 
363 
364inline void tMath::tCosSin(float& c, float& s, float x
365
366 // We can make a faster version by using pythagoras: cos^2 + sin^2 = 1. However, I don't feel like dealing with the negative roots right 
367 // now, so it's implemented naively. 
368 c = tCos(x); 
369 s = tSin(x); 
370
371 
372 
373inline void tMath::tCosSinFast(float& c, float& s, float x
374
375 // The fast versions are domain limited so we can use pythagoras without worrying about the negative roots. 
376 s = tSinFast(x); 
377 c = tSqrtFast(1.0f - s*s); 
378
379 
380 
381inline float& tMath::tiNormalizeAngle(float& a, tIntervalBias bias
382
383 std::function<bool(float,float)> less = tBiasLess(bias); 
384 std::function<bool(float,float)> greater = tBiasGreater(bias); 
385 while (less(a, -Pi)) a += TwoPi
386 while (greater(a, Pi)) a -= TwoPi
387 return a
388
389 
390 
391inline float& tMath::tiNormalizeAngle2Pi(float& a, tIntervalBias bias
392
393 std::function<bool(float,float)> less = tBiasLess(bias); 
394 std::function<bool(float,float)> greater = tBiasGreater(bias); 
395 while (less(a, 0.0f)) a += TwoPi
396 while (greater(a, TwoPi)) a -= TwoPi
397 return a
398
399 
400 
401inline float tMath::tUnitSin(float x, uint32 flip
402
403 tiClamp(x, 0.0f, 1.0f); 
404 x = (flip & tUnitFlip_X) ? 1.0f-x : x
405 float y = (tSin(x*Pi - PiOver2) + 1.0f)/2.0f
406 y = (flip & tUnitFlip_Y) ? 1.0f-y : y
407 return y
408
409 
410 
411inline float tMath::tUnitSinHalf(float x, uint32 flip
412
413 tiClamp(x, 0.0f, 1.0f); 
414 x = (flip & tUnitFlip_X) ? 1.0f-x : x
415 float y = tSin(x*PiOver2); 
416 y = (flip & tUnitFlip_Y) ? 1.0f-y : y
417 return y
418
419 
420 
421inline float tMath::tUnitPow(float x, float c, uint32 flip
422
423 tiClamp(x, 0.0f, 1.0f); 
424 x = (flip & tUnitFlip_X) ? 1.0f-x : x
425 float y = tPow(x, c); 
426 y = (flip & tUnitFlip_Y) ? 1.0f-y : y
427 return y
428
429 
430 
431inline float tMath::tUnitPowPlateau(float x, float c, uint32 flip
432
433 tiClamp(x, 0.0f, 1.0f); 
434 x = (flip & tUnitFlip_X) ? 1.0f-x : x
435 
436 float y = 0.0f
437 if (x < 0.5f
438 y = 0.5f*tUnitPow(2.0f*x, c); 
439 else 
440 y = 0.5f + 0.5f*tUnitPow(2.0f*(x-0.5f), c, tUnitFlip_XY); 
441 
442 y = (flip & tUnitFlip_Y) ? 1.0f-y : y
443 return y
444
445 
446 
447inline float tMath::tUnitArc(float x, uint32 flip
448
449 tiClamp(x, 0.0f, 1.0f); 
450 x = (flip & tUnitFlip_X) ? 1.0f-x : x
451 float y = tSqrt(1.0f - (1.0f-x)*(1.0f-x)); 
452 y = (flip & tUnitFlip_Y) ? 1.0f-y : y
453 return y
454
455