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