1 /* 2 Copyright (c) 2011-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Utility math functions 31 * 32 * Copyright: Timur Gafarov 2011-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov 35 */ 36 module dlib.math.utils; 37 38 private 39 { 40 import core.stdc.stdlib; 41 import std.math; 42 } 43 44 public: 45 46 /** 47 * Very small value 48 */ 49 enum EPSILON = 0.00000001; 50 51 /** 52 * Axes of Cartesian space 53 */ 54 enum Axis 55 { 56 x = 0, y = 1, z = 2 57 } 58 59 /** 60 * Convert degrees to radians 61 */ 62 T degtorad(T) (T angle) nothrow 63 { 64 return (angle / 180.0) * PI; 65 } 66 67 /** 68 * Convert radians to degrees 69 */ 70 T radtodeg(T) (T angle) nothrow 71 { 72 return (angle / PI) * 180.0; 73 } 74 75 /** 76 * Find maximum of two values 77 */ 78 T max2(T) (T x, T y) nothrow 79 { 80 return (x > y)? x : y; 81 } 82 83 /// 84 unittest 85 { 86 assert(max2(2, 1) == 2); 87 } 88 89 /** 90 * Find minimum of two values 91 */ 92 T min2(T) (T x, T y) nothrow 93 { 94 return (x < y)? x : y; 95 } 96 97 /// 98 unittest 99 { 100 assert(min2(2, 1) == 1); 101 } 102 103 /** 104 * Find maximum of three values 105 */ 106 T max3(T) (T x, T y, T z) nothrow 107 { 108 T temp = (x > y)? x : y; 109 return (temp > z) ? temp : z; 110 } 111 112 /// 113 unittest 114 { 115 assert(max3(3, 2, 1) == 3); 116 } 117 118 /** 119 * Find minimum of three values 120 */ 121 T min3(T) (T x, T y, T z) nothrow 122 { 123 T temp = (x < y)? x : y; 124 return (temp < z) ? temp : z; 125 } 126 127 /// 128 unittest 129 { 130 assert(min3(3, 2, 1) == 1); 131 } 132 133 /** 134 * Limit to given range 135 */ 136 static if (__traits(compiles, (){import std.algorithm: clamp;})) 137 { 138 public import std.algorithm: clamp; 139 } 140 else 141 { 142 T clamp(T) (T v, T minimal, T maximal) nothrow 143 { 144 if (v > minimal) 145 { 146 if (v < maximal) return v; 147 else return maximal; 148 } 149 else return minimal; 150 } 151 } 152 153 /** 154 * Is less than EPSILON 155 */ 156 bool isConsiderZero(T) (T f) nothrow 157 { 158 return (abs(f) < EPSILON); 159 } 160 161 /** 162 * Is power of 2 163 */ 164 bool isPowerOfTwo(T)(T x) nothrow 165 { 166 return (x != 0) && ((x & (x - 1)) == 0); 167 } 168 169 /// 170 unittest 171 { 172 assert(isPowerOfTwo(16)); 173 assert(!isPowerOfTwo(20)); 174 } 175 176 /** 177 * Round to next power of 2 178 */ 179 T nextPowerOfTwo(T) (T k) nothrow 180 { 181 if (k == 0) 182 return 1; 183 k--; 184 for (T i = 1; i < T.sizeof * 8; i <<= 1) 185 k = k | k >> i; 186 return k + 1; 187 } 188 189 /// 190 unittest 191 { 192 assert(nextPowerOfTwo(0) == 1); 193 assert(nextPowerOfTwo(5) == 8); 194 } 195 196 /** 197 * Round to next power of 10 198 */ 199 T nextPowerOfTen(T) (T k) nothrow 200 { 201 return pow(10, cast(int)ceil(log10(k))); 202 } 203 204 /// 205 unittest 206 { 207 assert(nextPowerOfTen(80) == 100); 208 } 209 210 /** 211 * Sum of all elements of an array 212 */ 213 deprecated("use reduce!((a, b) => a + b) instead") 214 T sum(T) (T[] array...) nothrow 215 { 216 T result = 0; 217 foreach(v; array) 218 result += v; 219 return result; 220 } 221 222 /** 223 * Negate all elements of an array 224 */ 225 deprecated("use map!(a => -a) instead") 226 T[] invertArray(T) (T[] array...) nothrow 227 { 228 auto result = new T[array.length]; 229 foreach(i, v; array) 230 result[i] = -v; 231 return result; 232 } 233 234 /** 235 * If all elements are zeros 236 */ 237 deprecated("use reduce!((a, b) => a + b == 0) instead") 238 bool allIsZero(T) (T[] array...) nothrow 239 { 240 foreach(i, v; array) 241 if (v != 0) return false; 242 return true; 243 } 244 245 /** 246 * If at least one element is zero 247 */ 248 bool oneOfIsZero(T) (T[] array...) nothrow 249 { 250 foreach(i, v; array) 251 if (v == 0) return true; 252 return false; 253 } 254 255 /** 256 * Byte operations 257 */ 258 version (BigEndian) 259 { 260 ushort bigEndian(ushort value) nothrow 261 { 262 return value; 263 } 264 265 /// 266 unittest 267 { 268 assert(bigEndian(cast(ushort)0x00FF) == 0x00FF); 269 } 270 271 uint bigEndian(uint value) nothrow 272 { 273 return value; 274 } 275 276 /// 277 unittest 278 { 279 assert(bigEndian(cast(uint)0x000000FF) == cast(uint)0x000000FF); 280 } 281 282 ushort networkByteOrder(ushort value) nothrow 283 { 284 return value; 285 } 286 287 /// 288 unittest 289 { 290 assert(networkByteOrder(cast(ushort)0x00FF) == 0x00FF); 291 } 292 293 uint networkByteOrder(uint value) nothrow 294 { 295 return value; 296 } 297 298 /// 299 unittest 300 { 301 assert(networkByteOrder(cast(uint)0x000000FF) == cast(uint)0x000000FF); 302 } 303 } 304 305 version (LittleEndian) 306 { 307 ushort bigEndian(ushort value) nothrow 308 { 309 return ((value & 0xFF) << 8) | ((value >> 8) & 0xFF); 310 } 311 312 /// 313 unittest 314 { 315 assert(bigEndian(cast(ushort)0x00FF) == 0xFF00); 316 } 317 318 uint bigEndian(uint value) nothrow 319 { 320 return value << 24 321 | (value & 0x0000FF00) << 8 322 | (value & 0x00FF0000) >> 8 323 | value >> 24; 324 } 325 326 /// 327 unittest 328 { 329 assert(bigEndian(cast(uint)0x000000FF) == cast(uint)0xFF000000); 330 } 331 332 ushort networkByteOrder(ushort value) nothrow 333 { 334 return bigEndian(value); 335 } 336 337 /// 338 unittest 339 { 340 assert(networkByteOrder(cast(ushort)0x00FF) == 0xFF00); 341 } 342 343 uint networkByteOrder(uint value) nothrow 344 { 345 return bigEndian(value); 346 } 347 348 /// 349 unittest 350 { 351 assert(networkByteOrder(cast(uint)0x000000FF) == cast(uint)0xFF000000); 352 } 353 } 354 355 uint bytesToUint(ubyte[4] src) nothrow 356 { 357 return (src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3]); 358 } 359 360 /// 361 unittest 362 { 363 assert(bytesToUint([0xee, 0x10, 0xab, 0xff]) == 0xee10abff); 364 } 365 366 /** 367 * Field of view angle Y from X 368 */ 369 T fovYfromX(T) (T xfov, T aspectRatio) nothrow 370 { 371 xfov = degtorad(xfov); 372 T yfov = 2.0 * atan(tan(xfov * 0.5)/aspectRatio); 373 return radtodeg(yfov); 374 } 375 376 /** 377 * Field of view angle X from Y 378 */ 379 T fovXfromY(T) (T yfov, T aspectRatio) nothrow 380 { 381 yfov = degtorad(yfov); 382 T xfov = 2.0 * atan(tan(yfov * 0.5) * aspectRatio); 383 return radtodeg(xfov); 384 } 385 386 /** 387 * Sign of a number 388 */ 389 int sign(T)(T x) nothrow 390 { 391 return (x > 0) - (x < 0); 392 } 393 394 /** 395 * Swap values 396 */ 397 void swap(T)(T* a, T* b) 398 { 399 T c = *a; 400 *a = *b; 401 *b = c; 402 } 403 404 /** 405 * Is perfect square 406 */ 407 bool isPerfectSquare(float n) nothrow 408 { 409 float r = sqrt(n); 410 return(r * r == n); 411 } 412 413 /// 414 unittest 415 { 416 assert(isPerfectSquare(64.0f)); 417 }