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 }