forked from libgdx/libgdx
/
MathUtils.java
420 lines (355 loc) · 16 KB
/
MathUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.math;
import java.util.Random;
/** Utility and fast math functions.
* <p>
* Thanks to Riven on JavaGaming.org for the basis of sin/cos/floor/ceil.
* @author Nathan Sweet */
public final class MathUtils {
private MathUtils () {
}
static public final float nanoToSec = 1 / 1000000000f;
// ---
static public final float FLOAT_ROUNDING_ERROR = 0.000001f; // 32 bits
static public final float PI = (float) Math.PI;
static public final float PI2 = PI * 2;
static public final float HALF_PI = PI / 2;
static public final float E = (float) Math.E;
static private final int SIN_BITS = 14; // 16KB. Adjust for accuracy.
static private final int SIN_MASK = ~(-1 << SIN_BITS);
static private final int SIN_COUNT = SIN_MASK + 1;
static private final float radFull = PI2;
static private final float degFull = 360;
static private final float radToIndex = SIN_COUNT / radFull;
static private final float degToIndex = SIN_COUNT / degFull;
/** multiply by this to convert from radians to degrees */
static public final float radiansToDegrees = 180f / PI;
static public final float radDeg = radiansToDegrees;
/** multiply by this to convert from degrees to radians */
static public final float degreesToRadians = PI / 180;
static public final float degRad = degreesToRadians;
static private class Sin {
static final float[] table = new float[SIN_COUNT];
static {
for (int i = 0; i < SIN_COUNT; i++)
table[i] = (float)Math.sin((i + 0.5f) / SIN_COUNT * radFull);
for (int i = 0; i < 360; i += 90)
table[(int)(i * degToIndex) & SIN_MASK] = (float)Math.sin(i * degreesToRadians);
}
}
/** Returns the sine in radians from a lookup table. For optimal precision, use radians between -PI2 and PI2 (both
* inclusive). */
static public float sin (float radians) {
return Sin.table[(int)(radians * radToIndex) & SIN_MASK];
}
/** Returns the cosine in radians from a lookup table. For optimal precision, use radians between -PI2 and PI2 (both
* inclusive). */
static public float cos (float radians) {
return Sin.table[(int)((radians + HALF_PI) * radToIndex) & SIN_MASK];
}
/** Returns the sine in degrees from a lookup table. For optimal precision, use radians between -360 and 360 (both
* inclusive). */
static public float sinDeg (float degrees) {
return Sin.table[(int)(degrees * degToIndex) & SIN_MASK];
}
/** Returns the cosine in degrees from a lookup table. For optimal precision, use radians between -360 and 360 (both
* inclusive). */
static public float cosDeg (float degrees) {
return Sin.table[(int)((degrees + 90) * degToIndex) & SIN_MASK];
}
// ---
/** Returns atan2 in radians, less accurate than Math.atan2 but may be faster. Average error of 0.00231 radians (0.1323
* degrees), largest error of 0.00488 radians (0.2796 degrees). */
static public float atan2 (float y, float x) {
if (x == 0f) {
if (y > 0f) return HALF_PI;
if (y == 0f) return 0f;
return -HALF_PI;
}
final float atan, z = y / x;
if (Math.abs(z) < 1f) {
atan = z / (1f + 0.28f * z * z);
if (x < 0f) return atan + (y < 0f ? -PI : PI);
return atan;
}
atan = HALF_PI - z / (z * z + 0.28f);
return y < 0f ? atan - PI : atan;
}
/** Returns acos in radians; less accurate than Math.acos but may be faster. Average error of 0.00002845 radians
* (0.0016300649 degrees), largest error of 0.000067548 radians (0.0038702153 degrees). This implementation does not
* return NaN if given an out-of-range input (Math.acos does return NaN), unless the input is NaN.
* @param a acos is defined only when a is between -1f and 1f, inclusive
* @return between {@code 0} and {@code PI} when a is in the defined range */
static public float acos(float a) {
float a2 = a * a; // a squared
float a3 = a * a2; // a cubed
if (a >= 0f) {
return (float) Math.sqrt(1f - a) *
(1.5707288f - 0.2121144f * a + 0.0742610f * a2 - 0.0187293f * a3);
}
else {
return 3.14159265358979323846f - (float) Math.sqrt(1f + a) *
(1.5707288f + 0.2121144f * a + 0.0742610f * a2 + 0.0187293f * a3);
}
}
/** Returns asin in radians; less accurate than Math.asin but may be faster. Average error of 0.000028447 radians
* (0.0016298931 degrees), largest error of 0.000067592 radians (0.0038727364 degrees). This implementation does not
* return NaN if given an out-of-range input (Math.asin does return NaN), unless the input is NaN.
* @param a asin is defined only when a is between -1f and 1f, inclusive
* @return between {@code -HALF_PI} and {@code HALF_PI} when a is in the defined range */
static public float asin(float a) {
float a2 = a * a; // a squared
float a3 = a * a2; // a cubed
if (a >= 0f) {
return 1.5707963267948966f - (float) Math.sqrt(1f - a) *
(1.5707288f - 0.2121144f * a + 0.0742610f * a2 - 0.0187293f * a3);
}
else {
return -1.5707963267948966f + (float) Math.sqrt(1f + a) *
(1.5707288f + 0.2121144f * a + 0.0742610f * a2 + 0.0187293f * a3);
}
}
// ---
static public Random random = new RandomXS128();
/** Returns a random number between 0 (inclusive) and the specified value (inclusive). */
static public int random (int range) {
return random.nextInt(range + 1);
}
/** Returns a random number between start (inclusive) and end (inclusive). */
static public int random (int start, int end) {
return start + random.nextInt(end - start + 1);
}
/** Returns a random number between 0 (inclusive) and the specified value (inclusive). */
static public long random (long range) {
return (long)(random.nextDouble() * range);
}
/** Returns a random number between start (inclusive) and end (inclusive). */
static public long random (long start, long end) {
return start + (long)(random.nextDouble() * (end - start));
}
/** Returns a random boolean value. */
static public boolean randomBoolean () {
return random.nextBoolean();
}
/** Returns true if a random value between 0 and 1 is less than the specified value. */
static public boolean randomBoolean (float chance) {
return MathUtils.random() < chance;
}
/** Returns random number between 0.0 (inclusive) and 1.0 (exclusive). */
static public float random () {
return random.nextFloat();
}
/** Returns a random number between 0 (inclusive) and the specified value (exclusive). */
static public float random (float range) {
return random.nextFloat() * range;
}
/** Returns a random number between start (inclusive) and end (exclusive). */
static public float random (float start, float end) {
return start + random.nextFloat() * (end - start);
}
/** Returns -1 or 1, randomly. */
static public int randomSign () {
return 1 | (random.nextInt() >> 31);
}
/** Returns a triangularly distributed random number between -1.0 (exclusive) and 1.0 (exclusive), where values around zero are
* more likely.
* <p>
* This is an optimized version of {@link #randomTriangular(float, float, float) randomTriangular(-1, 1, 0)} */
public static float randomTriangular () {
return random.nextFloat() - random.nextFloat();
}
/** Returns a triangularly distributed random number between {@code -max} (exclusive) and {@code max} (exclusive), where values
* around zero are more likely.
* <p>
* This is an optimized version of {@link #randomTriangular(float, float, float) randomTriangular(-max, max, 0)}
* @param max the upper limit */
public static float randomTriangular (float max) {
return (random.nextFloat() - random.nextFloat()) * max;
}
/** Returns a triangularly distributed random number between {@code min} (inclusive) and {@code max} (exclusive), where the
* {@code mode} argument defaults to the midpoint between the bounds, giving a symmetric distribution.
* <p>
* This method is equivalent of {@link #randomTriangular(float, float, float) randomTriangular(min, max, (min + max) * 0.5f)}
* @param min the lower limit
* @param max the upper limit */
public static float randomTriangular (float min, float max) {
return randomTriangular(min, max, (min + max) * 0.5f);
}
/** Returns a triangularly distributed random number between {@code min} (inclusive) and {@code max} (exclusive), where values
* around {@code mode} are more likely.
* @param min the lower limit
* @param max the upper limit
* @param mode the point around which the values are more likely */
public static float randomTriangular (float min, float max, float mode) {
float u = random.nextFloat();
float d = max - min;
if (u <= (mode - min) / d) return min + (float)Math.sqrt(u * d * (mode - min));
return max - (float)Math.sqrt((1 - u) * d * (max - mode));
}
// ---
/** Returns the next power of two. Returns the specified value if the value is already a power of two. */
static public int nextPowerOfTwo (int value) {
if (value == 0) return 1;
value--;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return value + 1;
}
static public boolean isPowerOfTwo (int value) {
return value != 0 && (value & value - 1) == 0;
}
// ---
static public short clamp (short value, short min, short max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static public int clamp (int value, int min, int max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static public long clamp (long value, long min, long max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static public float clamp (float value, float min, float max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static public double clamp (double value, double min, double max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
// ---
/** Linearly interpolates between fromValue to toValue on progress position. */
static public float lerp (float fromValue, float toValue, float progress) {
return fromValue + (toValue - fromValue) * progress;
}
/** Linearly normalizes value from a range. Range must not be empty. This is the inverse of {@link #lerp(float, float, float)}.
* @param rangeStart Range start normalized to 0
* @param rangeEnd Range end normalized to 1
* @param value Value to normalize
* @return Normalized value. Values outside of the range are not clamped to 0 and 1 */
static public float norm (float rangeStart, float rangeEnd, float value) {
return (value - rangeStart) / (rangeEnd - rangeStart);
}
/** Linearly map a value from one range to another. Input range must not be empty. This is the same as chaining
* {@link #norm(float, float, float)} from input range and {@link #lerp(float, float, float)} to output range.
* @param inRangeStart Input range start
* @param inRangeEnd Input range end
* @param outRangeStart Output range start
* @param outRangeEnd Output range end
* @param value Value to map
* @return Mapped value. Values outside of the input range are not clamped to output range */
static public float map (float inRangeStart, float inRangeEnd, float outRangeStart, float outRangeEnd, float value) {
return outRangeStart + (value - inRangeStart) * (outRangeEnd - outRangeStart) / (inRangeEnd - inRangeStart);
}
/** Linearly interpolates between two angles in radians. Takes into account that angles wrap at two pi and always takes the
* direction with the smallest delta angle.
*
* @param fromRadians start angle in radians
* @param toRadians target angle in radians
* @param progress interpolation value in the range [0, 1]
* @return the interpolated angle in the range [0, PI2[ */
public static float lerpAngle (float fromRadians, float toRadians, float progress) {
float delta = ((toRadians - fromRadians + PI2 + PI) % PI2) - PI;
return (fromRadians + delta * progress + PI2) % PI2;
}
/** Linearly interpolates between two angles in degrees. Takes into account that angles wrap at 360 degrees and always takes
* the direction with the smallest delta angle.
*
* @param fromDegrees start angle in degrees
* @param toDegrees target angle in degrees
* @param progress interpolation value in the range [0, 1]
* @return the interpolated angle in the range [0, 360[ */
public static float lerpAngleDeg (float fromDegrees, float toDegrees, float progress) {
float delta = ((toDegrees - fromDegrees + 360 + 180) % 360) - 180;
return (fromDegrees + delta * progress + 360) % 360;
}
// ---
static private final int BIG_ENOUGH_INT = 16 * 1024;
static private final double BIG_ENOUGH_FLOOR = BIG_ENOUGH_INT;
static private final double CEIL = 0.9999999;
static private final double BIG_ENOUGH_CEIL = 16384.999999999996;
static private final double BIG_ENOUGH_ROUND = BIG_ENOUGH_INT + 0.5f;
/** Returns the largest integer less than or equal to the specified float. This method will only properly floor floats from
* -(2^14) to (Float.MAX_VALUE - 2^14). */
static public int floor (float value) {
return (int)(value + BIG_ENOUGH_FLOOR) - BIG_ENOUGH_INT;
}
/** Returns the largest integer less than or equal to the specified float. This method will only properly floor floats that are
* positive. Note this method simply casts the float to int. */
static public int floorPositive (float value) {
return (int)value;
}
/** Returns the smallest integer greater than or equal to the specified float. This method will only properly ceil floats from
* -(2^14) to (Float.MAX_VALUE - 2^14). */
static public int ceil (float value) {
return BIG_ENOUGH_INT - (int)(BIG_ENOUGH_FLOOR - value);
}
/** Returns the smallest integer greater than or equal to the specified float. This method will only properly ceil floats that
* are positive. */
static public int ceilPositive (float value) {
return (int)(value + CEIL);
}
/** Returns the closest integer to the specified float. This method will only properly round floats from -(2^14) to
* (Float.MAX_VALUE - 2^14). */
static public int round (float value) {
return (int)(value + BIG_ENOUGH_ROUND) - BIG_ENOUGH_INT;
}
/** Returns the closest integer to the specified float. This method will only properly round floats that are positive. */
static public int roundPositive (float value) {
return (int)(value + 0.5f);
}
/** Returns true if the value is zero (using the default tolerance as upper bound) */
static public boolean isZero (float value) {
return Math.abs(value) <= FLOAT_ROUNDING_ERROR;
}
/** Returns true if the value is zero.
* @param tolerance represent an upper bound below which the value is considered zero. */
static public boolean isZero (float value, float tolerance) {
return Math.abs(value) <= tolerance;
}
/** Returns true if a is nearly equal to b. The function uses the default floating error tolerance.
* @param a the first value.
* @param b the second value. */
static public boolean isEqual (float a, float b) {
return Math.abs(a - b) <= FLOAT_ROUNDING_ERROR;
}
/** Returns true if a is nearly equal to b.
* @param a the first value.
* @param b the second value.
* @param tolerance represent an upper bound below which the two values are considered equal. */
static public boolean isEqual (float a, float b, float tolerance) {
return Math.abs(a - b) <= tolerance;
}
/** @return the logarithm of value with base a */
static public float log (float a, float value) {
return (float)(Math.log(value) / Math.log(a));
}
/** @return the logarithm of value with base 2 */
static public float log2 (float value) {
return log(2, value);
}
}