1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 /**
21  * \file math/func.h
22  * \brief Common math functions
23  */
24 
25 #pragma once
26 
27 
28 #include "math/const.h"
29 
30 
31 #include <cmath>
32 #include <cstdlib>
33 
34 
35 // Math module namespace
36 namespace Math
37 {
38 
39 
40 //! Compares \a a and \a b within \a tolerance
41 inline bool IsEqual(float a, float b, float tolerance = Math::TOLERANCE)
42 {
43     return fabs(a - b) < tolerance;
44 }
45 
46 //! Compares \a a to zero within \a tolerance
47 inline bool IsZero(float a, float tolerance = Math::TOLERANCE)
48 {
49     return Math::IsEqual(a, 0.0f, tolerance);
50 }
51 
52 //! Minimum
Min(float a,float b)53 inline float Min(float a, float b)
54 {
55     if ( a <= b ) return a;
56     else          return b;
57 }
58 
Min(float a,float b,float c)59 inline float Min(float a, float b, float c)
60 {
61     return Min( Min(a, b), c );
62 }
63 
Min(float a,float b,float c,float d)64 inline float Min(float a, float b, float c, float d)
65 {
66     return Math::Min( Math::Min(a, b), Math::Min(c, d) );
67 }
68 
Min(float a,float b,float c,float d,float e)69 inline float Min(float a, float b, float c, float d, float e)
70 {
71     return Math::Min( Math::Min(a, b), Math::Min(c, d), e );
72 }
73 
74 //! Maximum
Max(float a,float b)75 inline float Max(float a, float b)
76 {
77     if ( a >= b ) return a;
78     else          return b;
79 }
80 
Max(float a,float b,float c)81 inline float Max(float a, float b, float c)
82 {
83     return Math::Max( Math::Max(a, b), c );
84 }
85 
Max(float a,float b,float c,float d)86 inline float Max(float a, float b, float c, float d)
87 {
88     return Math::Max( Math::Max(a, b), Math::Max(c, d) );
89 }
90 
Max(float a,float b,float c,float d,float e)91 inline float Max(float a, float b, float c, float d, float e)
92 {
93     return Math::Max( Math::Max(a, b), Math::Max(c, d), e );
94 }
95 
96 //! Clamps the value to a range specified by min and max
97 template<typename T>
Clamp(T value,T min,T max)98 inline T Clamp(T value, T min, T max)
99 {
100     if (value < min) return min;
101     else if (value > max) return max;
102     else return value;
103 }
104 
105 //! Returns the normalized value (0 .. 1)
Norm(float a)106 inline float Norm(float a)
107 {
108     if ( a < 0.0f ) return 0.0f;
109     if ( a > 1.0f ) return 1.0f;
110     return a;
111 }
112 
113 //! Swaps two integers
Swap(int & a,int & b)114 inline void Swap(int &a, int &b)
115 {
116     int c = a;
117     a = b;
118     b = c;
119 }
120 
121 //! Swaps two real numbers
Swap(float & a,float & b)122 inline void Swap(float &a, float &b)
123 {
124     float c = a;
125     a = b;
126     b = c;
127 }
128 
129 //! Returns the modulo of a floating point number
130 /** Mod(8.1, 4) = 0.1
131         Mod(n, 1) = fractional part of n */
Mod(float a,float m)132 inline float Mod(float a, float m)
133 {
134     return a - ( static_cast<int>(a / m) ) * m;
135 }
136 
137 //! Returns a random value between 0 and 1.
Rand()138 inline float Rand()
139 {
140     return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
141 }
142 
143 //! Returns whether \a x is an even power of 2
IsPowerOfTwo(unsigned int x)144 inline bool IsPowerOfTwo(unsigned int x)
145 {
146    return x && !(x & (x - 1));
147 }
148 
149 //! Returns the next nearest power of two to \a x
NextPowerOfTwo(int x)150 inline int NextPowerOfTwo(int x)
151 {
152   double logbase2 = log(static_cast<float>(x)) / Math::LOG_2;
153   return static_cast<int>(pow(2, ceil(logbase2)) + 0.5);
154 }
155 
156 
157 //! Returns a normalized angle, that is in other words between 0 and 2 * PI
NormAngle(float angle)158 inline float NormAngle(float angle)
159 {
160     angle = Math::Mod(angle, PI*2.0f);
161     if ( angle < 0.0f )
162         return PI*2.0f + angle;
163 
164     return angle;
165 }
166 
167 //! Test if a angle is between two terminals
TestAngle(float angle,float min,float max)168 inline bool TestAngle(float angle, float min, float max)
169 {
170     angle = Math::NormAngle(angle);
171     min   = Math::NormAngle(min);
172     max   = Math::NormAngle(max);
173 
174     if ( min > max )
175         return ( angle <= max || angle >= min );
176 
177     return ( angle >= min && angle <= max );
178 }
179 
180 //! Calculates a value (radians) proportional between a and b (degrees)
PropAngle(float a,float b,float p)181 inline float PropAngle(float a, float b, float p)
182 {
183     float aa = a * DEG_TO_RAD;
184     float bb = b * DEG_TO_RAD;
185 
186     return aa + p * (bb - aa);
187 }
188 
189 //! Calculates the angle to rotate the angle \a a to the angle \a g
190 /** A positive angle is counterclockwise (CCW). */
Direction(float a,float g)191 inline float Direction(float a, float g)
192 {
193     a = Math::NormAngle(a);
194     g = Math::NormAngle(g);
195 
196     if ( a < g )
197     {
198         if ( a+PI*2.0f-g < g-a ) a += PI*2.0f;
199     }
200     else
201     {
202         if ( g+PI*2.0f-a < a-g ) g += PI*2.0f;
203     }
204 
205     return g-a;
206 }
207 
208 //! Managing the dead zone of a joystick.
209 /**
210 \verbatim
211 in:   -1            0            1
212 --|-------|----o----|-------|-->
213              <---->
214               dead
215 out:  -1       0         0       1
216 \endverbatim */
Neutral(float value,float dead)217 inline float Neutral(float value, float dead)
218 {
219     if ( fabs(value) <= dead )
220     {
221         return 0.0f;
222     }
223     else
224     {
225         if ( value > 0.0f ) return (value-dead)/(1.0f-dead);
226         else                return (value+dead)/(1.0f-dead);
227     }
228 }
229 
230 //! Gently advances a desired value from its current value
231 /** Over time, the progression is more rapid. */
Smooth(float actual,float hope,float time)232 inline float Smooth(float actual, float hope, float time)
233 {
234     float future = actual + (hope-actual)*time;
235 
236     if ( hope > actual )
237     {
238         if ( future > hope ) future = hope;
239     }
240     if ( hope < actual )
241     {
242         if ( future < hope ) future = hope;
243     }
244 
245     return future;
246 }
247 
248 //! Bounces any movement
249 /**
250 \verbatim
251 out
252  |
253 1+------o-------o---
254  |    o | o   o |  | bounce
255  |   o  |   o---|---
256  |  o   |       |
257  | o    |       |
258 -o------|-------+----> progress
259 0|      |       1
260  |<---->|middle
261 \endverbatim */
262 inline float Bounce(float progress, float middle = 0.3f, float bounce = 0.4f)
263 {
264     if ( progress < middle )
265     {
266         progress = progress/middle;    // 0..1
267         return 0.5f+sinf(progress*PI-PI/2.0f)/2.0f;
268     }
269     else
270     {
271         progress = (progress-middle)/(1.0f-middle);    // 0..1
272         return (1.0f-bounce/2.0f)+sinf((0.5f+progress*2.0f)*PI)*(bounce/2.0f);
273     }
274 }
275 
276 
277 } // namespace Math
278 
279