This repository has been archived by the owner on Jun 10, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 61
/
math.scalar.ts
326 lines (295 loc) · 10.4 KB
/
math.scalar.ts
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
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Scalar computation library
*/
export class Scalar {
/**
* Two pi constants convenient for computation.
*/
// tslint:disable-next-line:variable-name
public static TwoPi: number = Math.PI * 2;
/**
* Boolean : true if the absolute difference between a and b is lower than epsilon (default = 1.401298E-45)
* @param a number
* @param b number
* @param epsilon (default = 1.401298E-45)
* @returns true if the absolute difference between a and b is lower than epsilon (default = 1.401298E-45)
*/
public static WithinEpsilon(a: number, b: number, epsilon = 1.401298E-45): boolean {
const num = a - b;
return -epsilon <= num && num <= epsilon;
}
/**
* Returns a string : the upper case translation of the number i to hexadecimal.
* @param i number
* @returns the upper case translation of the number i to hexadecimal.
*/
public static ToHex(i: number): string {
const str = i.toString(16);
if (i <= 15) {
return ("0" + str).toUpperCase();
}
return str.toUpperCase();
}
/**
* Returns -1 if value is negative and +1 is value is positive.
* @param value the value
* @returns the value itself if it's equal to zero.
*/
public static Sign(value: number): number {
value = +value; // convert to a number
if (value === 0 || isNaN(value)) {
return value;
}
return value > 0 ? 1 : -1;
}
/**
* Returns the value itself if it's between min and max.
* Returns min if the value is lower than min.
* Returns max if the value is greater than max.
* @param value the value to clmap
* @param min the min value to clamp to (default: 0)
* @param max the max value to clamp to (default: 1)
* @returns the clamped value
*/
public static Clamp(value: number, min = 0, max = 1): number {
return Math.min(max, Math.max(min, value));
}
/**
* the log2 of value.
* @param value the value to compute log2 of
* @returns the log2 of value.
*/
public static Log2(value: number): number {
return Math.log(value) * Math.LOG2E;
}
/**
* Loops the value, so that it is never larger than length and never smaller than 0.
*
* This is similar to the modulo operator but it works with floating point numbers.
* For example, using 3.0 for t and 2.5 for length, the result would be 0.5.
* With t = 5 and length = 2.5, the result would be 0.0.
* Note, however, that the behaviour is not defined for negative numbers as it is for the modulo operator
* @param value the value
* @param length the length
* @returns the looped value
*/
public static Repeat(value: number, length: number): number {
return value - Math.floor(value / length) * length;
}
/**
* Normalize the value between 0.0 and 1.0 using min and max values
* @param value value to normalize
* @param min max to normalize between
* @param max min to normalize between
* @returns the normalized value
*/
public static Normalize(value: number, min: number, max: number): number {
return (value - min) / (max - min);
}
/**
* Denormalize the value from 0.0 and 1.0 using min and max values
* @param normalized value to denormalize
* @param min max to denormalize between
* @param max min to denormalize between
* @returns the denormalized value
*/
public static Denormalize(normalized: number, min: number, max: number): number {
return (normalized * (max - min) + min);
}
/**
* Calculates the shortest difference between two given angles given in degrees.
* @param current current angle in degrees
* @param target target angle in degrees
* @returns the delta
*/
public static DeltaAngle(current: number, target: number): number {
let num: number = Scalar.Repeat(target - current, 360.0);
if (num > 180.0) {
num -= 360.0;
}
return num;
}
/**
* PingPongs the value t, so that it is never larger than length and never smaller than 0.
* @param tx value
* @param length length
* @returns The returned value will move back and forth between 0 and length
*/
public static PingPong(tx: number, length: number): number {
const t: number = Scalar.Repeat(tx, length * 2.0);
return length - Math.abs(t - length);
}
/**
* Interpolates between min and max with smoothing at the limits.
*
* This function interpolates between min and max in a similar way to Lerp. However,
* the interpolation will gradually speed up from the start and slow down toward the
* end. This is useful for creating natural-looking animation, fading and other transitions.
* @param from from
* @param to to
* @param tx value
* @returns the smooth stepped value
*/
public static SmoothStep(from: number, to: number, tx: number): number {
let t: number = Scalar.Clamp(tx);
t = -2.0 * t * t * t + 3.0 * t * t;
return to * t + from * (1.0 - t);
}
/**
* Moves a value current towards target.
*
* This is essentially the same as Mathf.Lerp but instead the function will
* ensure that the speed never exceeds maxDelta.
* Negative values of maxDelta pushes the value away from target.
* @param current current value
* @param target target value
* @param maxDelta max distance to move
* @returns resulting value
*/
public static MoveTowards(current: number, target: number, maxDelta: number): number {
let result = 0;
if (Math.abs(target - current) <= maxDelta) {
result = target;
} else {
result = current + Scalar.Sign(target - current) * maxDelta;
}
return result;
}
/**
* Same as MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.
*
* Variables current and target are assumed to be in degrees. For optimization reasons,
* negative values of maxDelta are not supported and may cause oscillation. To push current
* away from a target angle, add 180 to that angle instead.
* @param current current value
* @param target target value
* @param maxDelta max distance to move
* @returns resulting angle
*/
public static MoveTowardsAngle(current: number, target: number, maxDelta: number): number {
const num = Scalar.DeltaAngle(current, target);
let result = 0;
if (-maxDelta < num && num < maxDelta) {
result = target;
} else {
target = current + num;
result = Scalar.MoveTowards(current, target, maxDelta);
}
return result;
}
/**
* Creates a new scalar with values linearly interpolated of "amount" between the start scalar and the end scalar.
* @param start start value
* @param end target value
* @param amount amount to lerp between
* @returns the lerped value
*/
public static Lerp(start: number, end: number, amount: number): number {
return start + ((end - start) * amount);
}
/**
* Same as Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.
* The parameter t is clamped to the range [0, 1]. Variables a and b are assumed to be in degrees.
* @param start start value
* @param end target value
* @param amount amount to lerp between
* @returns the lerped value
*/
public static LerpAngle(start: number, end: number, amount: number): number {
let num: number = Scalar.Repeat(end - start, 360.0);
if (num > 180.0) {
num -= 360.0;
}
return start + num * Scalar.Clamp(amount);
}
/**
* Calculates the linear parameter t that produces the interpolant value within the range [a, b].
* @param a start value
* @param b target value
* @param value value between a and b
* @returns the inverseLerp value
*/
public static InverseLerp(a: number, b: number, value: number): number {
let result = 0;
if (a !== b) {
result = Scalar.Clamp((value - a) / (b - a));
} else {
result = 0.0;
}
return result;
}
/**
* Returns a new scalar located for "amount" (float) on the Hermite spline defined by the scalars
* "value1", "value3", "tangent1", "tangent2".
* @see http://mathworld.wolfram.com/HermitePolynomial.html
* @param value1 spline value
* @param tangent1 spline value
* @param value2 spline value
* @param tangent2 spline value
* @param amount input value
* @returns hermite result
*/
public static Hermite(value1: number, tangent1: number, value2: number, tangent2: number, amount: number): number {
const squared = amount * amount;
const cubed = amount * squared;
const part1 = ((2.0 * cubed) - (3.0 * squared)) + 1.0;
const part2 = (-2.0 * cubed) + (3.0 * squared);
const part3 = (cubed - (2.0 * squared)) + amount;
const part4 = cubed - squared;
return (((value1 * part1) + (value2 * part2)) + (tangent1 * part3)) + (tangent2 * part4);
}
/**
* Returns a random float number between and min and max values
* @param min min value of random
* @param max max value of random
* @returns random value
*/
public static RandomRange(min: number, max: number): number {
if (min === max) { return min; }
return ((Math.random() * (max - min)) + min);
}
/**
* This function returns percentage of a number in a given range.
*
* RangeToPercent(40,20,60) will return 0.5 (50%)
* RangeToPercent(34,0,100) will return 0.34 (34%)
* @param num to convert to percentage
* @param min min range
* @param max max range
* @returns the percentage
*/
public static RangeToPercent(num: number, min: number, max: number): number {
return ((num - min) / (max - min));
}
/**
* This function returns number that corresponds to the percentage in a given range.
*
* PercentToRange(0.34,0,100) will return 34.
* @param percent to convert to number
* @param min min range
* @param max max range
* @returns the number
*/
public static PercentToRange(percent: number, min: number, max: number): number {
return ((max - min) * percent + min);
}
/**
* Returns the angle converted to equivalent value between -Math.PI and Math.PI radians.
* @param angle The angle to normalize in radian.
* @return The converted angle.
*/
public static NormalizeRadians(angle: number): number {
// More precise but slower version kept for reference.
// angle = angle % Tools.TwoPi;
// angle = (angle + Tools.TwoPi) % Tools.TwoPi;
// if (angle > Math.PI) {
// angle -= Tools.TwoPi;
// }
angle -= (Scalar.TwoPi * Math.floor((angle + Math.PI) / Scalar.TwoPi));
return angle;
}
}