forked from playcanvas/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmath.js
More file actions
349 lines (314 loc) · 12.2 KB
/
math.js
File metadata and controls
349 lines (314 loc) · 12.2 KB
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
/**
* @name math
* @namespace
* @description Math API.
*/
const math = {
/**
* @constant
* @type {number}
* @name math.DEG_TO_RAD
* @description Conversion factor between degrees and radians.
* @example
* // Convert 180 degrees to pi radians
* var rad = 180 * pc.math.DEG_TO_RAD;
*/
DEG_TO_RAD: Math.PI / 180,
/**
* @constant
* @type {number}
* @name math.RAD_TO_DEG
* @description Conversion factor between degrees and radians.
* @example
* // Convert pi radians to 180 degrees
* var deg = Math.PI * pc.math.RAD_TO_DEG;
*/
RAD_TO_DEG: 180 / Math.PI,
/**
* @function
* @name math.clamp
* @description Clamp a number between min and max inclusive.
* @param {number} value - Number to clamp.
* @param {number} min - Min value.
* @param {number} max - Max value.
* @returns {number} The clamped value.
*/
clamp: function (value, min, max) {
if (value >= max) return max;
if (value <= min) return min;
return value;
},
/**
* @function
* @name math.intToBytes24
* @description Convert an 24 bit integer into an array of 3 bytes.
* @param {number} i - Number holding an integer value.
* @returns {number[]} An array of 3 bytes.
* @example
* // Set bytes to [0x11, 0x22, 0x33]
* var bytes = pc.math.intToBytes24(0x112233);
*/
intToBytes24: function (i) {
const r = (i >> 16) & 0xff;
const g = (i >> 8) & 0xff;
const b = (i) & 0xff;
return [r, g, b];
},
/**
* @function
* @name math.intToBytes32
* @description Convert an 32 bit integer into an array of 4 bytes.
* @returns {number[]} An array of 4 bytes.
* @param {number} i - Number holding an integer value.
* @example
* // Set bytes to [0x11, 0x22, 0x33, 0x44]
* var bytes = pc.math.intToBytes32(0x11223344);
*/
intToBytes32: function (i) {
const r = (i >> 24) & 0xff;
const g = (i >> 16) & 0xff;
const b = (i >> 8) & 0xff;
const a = (i) & 0xff;
return [r, g, b, a];
},
/**
* @function
* @name math.bytesToInt24
* @description Convert 3 8 bit Numbers into a single unsigned 24 bit Number.
* @example
* // Set result1 to 0x112233 from an array of 3 values
* var result1 = pc.math.bytesToInt24([0x11, 0x22, 0x33]);
*
* // Set result2 to 0x112233 from 3 discrete values
* var result2 = pc.math.bytesToInt24(0x11, 0x22, 0x33);
* @param {number} r - A single byte (0-255).
* @param {number} g - A single byte (0-255).
* @param {number} b - A single byte (0-255).
* @returns {number} A single unsigned 24 bit Number.
*/
bytesToInt24: function (r, g, b) {
if (r.length) {
b = r[2];
g = r[1];
r = r[0];
}
return ((r << 16) | (g << 8) | b);
},
/**
* @function
* @name math.bytesToInt32
* @description Convert 4 1-byte Numbers into a single unsigned 32bit Number.
* @returns {number} A single unsigned 32bit Number.
* @example
* // Set result1 to 0x11223344 from an array of 4 values
* var result1 = pc.math.bytesToInt32([0x11, 0x22, 0x33, 0x44]);
*
* // Set result2 to 0x11223344 from 4 discrete values
* var result2 = pc.math.bytesToInt32(0x11, 0x22, 0x33, 0x44);
* @param {number} r - A single byte (0-255).
* @param {number} g - A single byte (0-255).
* @param {number} b - A single byte (0-255).
* @param {number} a - A single byte (0-255).
*/
bytesToInt32: function (r, g, b, a) {
if (r.length) {
a = r[3];
b = r[2];
g = r[1];
r = r[0];
}
// Why ((r << 24)>>>32)?
// << operator uses signed 32 bit numbers, so 128<<24 is negative.
// >>> used unsigned so >>>32 converts back to an unsigned.
// See http://stackoverflow.com/questions/1908492/unsigned-integer-in-javascript
return ((r << 24) | (g << 16) | (b << 8) | a) >>> 32;
},
/**
* @function
* @name math.lerp
* @returns {number} The linear interpolation of two numbers.
* @description Calculates the linear interpolation of two numbers.
* @param {number} a - Number to linearly interpolate from.
* @param {number} b - Number to linearly interpolate to.
* @param {number} alpha - The value controlling the result of interpolation. When alpha is 0,
* a is returned. When alpha is 1, b is returned. Between 0 and 1, a linear interpolation between
* a and b is returned. alpha is clamped between 0 and 1.
*/
lerp: function (a, b, alpha) {
return a + (b - a) * math.clamp(alpha, 0, 1);
},
/**
* @function
* @name math.lerpAngle
* @description Calculates the linear interpolation of two angles ensuring that interpolation
* is correctly performed across the 360 to 0 degree boundary. Angles are supplied in degrees.
* @returns {number} The linear interpolation of two angles.
* @param {number} a - Angle (in degrees) to linearly interpolate from.
* @param {number} b - Angle (in degrees) to linearly interpolate to.
* @param {number} alpha - The value controlling the result of interpolation. When alpha is 0,
* a is returned. When alpha is 1, b is returned. Between 0 and 1, a linear interpolation between
* a and b is returned. alpha is clamped between 0 and 1.
*/
lerpAngle: function (a, b, alpha) {
if (b - a > 180) {
b -= 360;
}
if (b - a < -180) {
b += 360;
}
return math.lerp(a, b, math.clamp(alpha, 0, 1));
},
/**
* @function
* @name math.powerOfTwo
* @description Returns true if argument is a power-of-two and false otherwise.
* @param {number} x - Number to check for power-of-two property.
* @returns {boolean} true if power-of-two and false otherwise.
*/
powerOfTwo: function (x) {
return ((x !== 0) && !(x & (x - 1)));
},
/**
* @function
* @name math.nextPowerOfTwo
* @description Returns the next power of 2 for the specified value.
* @param {number} val - The value for which to calculate the next power of 2.
* @returns {number} The next power of 2.
*/
nextPowerOfTwo: function (val) {
val--;
val |= (val >> 1);
val |= (val >> 2);
val |= (val >> 4);
val |= (val >> 8);
val |= (val >> 16);
val++;
return val;
},
/**
* @function
* @name math.random
* @description Return a pseudo-random number between min and max.
* The number generated is in the range [min, max), that is inclusive of the minimum but exclusive of the maximum.
* @param {number} min - Lower bound for range.
* @param {number} max - Upper bound for range.
* @returns {number} Pseudo-random number between the supplied range.
*/
random: function (min, max) {
const diff = max - min;
return Math.random() * diff + min;
},
/**
* @function
* @name math.smoothstep
* @description The function interpolates smoothly between two input values based on
* a third one that should be between the first two. The returned value is clamped
* between 0 and 1.
* <br/>The slope (i.e. derivative) of the smoothstep function starts at 0 and ends at 0.
* This makes it easy to create a sequence of transitions using smoothstep to interpolate
* each segment rather than using a more sophisticated or expensive interpolation technique.
* <br/>See http://en.wikipedia.org/wiki/Smoothstep for more details.
* @param {number} min - The lower bound of the interpolation range.
* @param {number} max - The upper bound of the interpolation range.
* @param {number} x - The value to interpolate.
* @returns {number} The smoothly interpolated value clamped between zero and one.
*/
smoothstep: function (min, max, x) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * (3 - 2 * x);
},
/**
* @function
* @name math.smootherstep
* @description An improved version of the {@link math.smoothstep} function which has zero
* 1st and 2nd order derivatives at t=0 and t=1.
* <br/>See http://en.wikipedia.org/wiki/Smoothstep for more details.
* @param {number} min - The lower bound of the interpolation range.
* @param {number} max - The upper bound of the interpolation range.
* @param {number} x - The value to interpolate.
* @returns {number} The smoothly interpolated value clamped between zero and one.
*/
smootherstep: function (min, max, x) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * x * (x * (x * 6 - 15) + 10);
},
/**
* @function
* @name math.roundUp
* @description Rounds a number up to nearest multiple.
* @param {number} numToRound - The number to round up.
* @param {number} multiple - The multiple to round up to.
* @returns {number} A number rounded up to nearest multiple.
*/
roundUp: function (numToRound, multiple) {
if (multiple === 0)
return numToRound;
return Math.ceil(numToRound / multiple) * multiple;
},
/**
* @function
* @name math.float2Half
* @description Converts float number to half float representation.
* @param {number} val - The float number.
* @returns {number} A 16 bit number representing half float representation as used by GPU.
*/
float2Half: (function () {
// based on based on https://esdiscuss.org/topic/float16array
const floatView = new Float32Array(1);
const int32View = new Int32Array(floatView.buffer);
// This method is faster than the OpenEXR implementation (very often
// used, eg. in Ogre), with the additional benefit of rounding, inspired
// by James Tursa?s half-precision code.
return function (val) {
floatView[0] = val;
const x = int32View[0];
let bits = (x >> 16) & 0x8000; // Get the sign
let m = (x >> 12) & 0x07ff; // Keep one extra bit for rounding
const e = (x >> 23) & 0xff; // Using int is faster here
// If zero, or denormal, or exponent underflows too much for a denormal half, return signed zero.
if (e < 103) {
return bits;
}
// If NaN, return NaN. If Inf or exponent overflow, return Inf.
if (e > 142) {
bits |= 0x7c00;
// If exponent was 0xff and one mantissa bit was set, it means NaN,
// not Inf, so make sure we set one mantissa bit too.
bits |= ((e === 255) ? 0 : 1) && (x & 0x007fffff);
return bits;
}
// If exponent underflows but not too much, return a denormal
if (e < 113) {
m |= 0x0800;
// Extra rounding may overflow and set mantissa to 0 and exponent to 1, which is OK.
bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
return bits;
}
bits |= ((e - 112) << 10) | (m >> 1);
// Extra rounding. An overflow will set mantissa to 0 and increment the exponent, which is OK.
bits += m & 1;
return bits;
};
}()),
/**
* @function
* @private
* @name math.between
* @description Checks whether a given number resides between two other given numbers.
* @param {number} num - The number to check the position of.
* @param {number} a - The first upper or lower threshold to check between.
* @param {number} b - The second upper or lower threshold to check between.
* @param {boolean} inclusive - If true, a num param which is equal to a or b will return true.
* @returns {boolean} true if between or false otherwise.
*/
between: function (num, a, b, inclusive) {
const min = Math.min(a, b);
const max = Math.max(a, b);
return inclusive ? num >= min && num <= max : num > min && num < max;
}
};
export { math };