forked from FreyaHolmer/Mathfs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Mathfs.cs
1311 lines (1027 loc) · 77.2 KB
/
Mathfs.cs
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
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Some of this code is similar to Unity's original Mathf source to match functionality.
// The original Mathf.cs source https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Math/Mathf.cs
// ...and the trace amounts of it left in here is copyright (c) Unity Technologies with license: https://unity3d.com/legal/licenses/Unity_Reference_Only_License
//
// Collected and expanded upon to by Freya Holmér (https://github.com/FreyaHolmer/Mathfs)
using System;
using UnityEngine;
using Uei = UnityEngine.Internal;
using System.Linq; // used for arbitrary count min/max functions, so it's safe and won't allocate garbage don't worry~
using System.Runtime.CompilerServices;
namespace Freya {
/// <summary>The core math helper class. It has functions mostly for single values, but also vector helpers</summary>
public static class Mathfs {
const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining;
#region Constants
/// <summary>The circle constant. Defined as the circumference of a circle divided by its radius. Equivalent to 2*pi</summary>
public const float TAU = 6.28318530717959f;
/// <summary>An obscure circle constant. Defined as the circumference of a circle divided by its diameter. Equivalent to 0.5*tau</summary>
public const float PI = 3.14159265359f;
/// <summary>Euler's number. The base of the natural logarithm. f(x)=e^x is equal to its own derivative</summary>
public const float E = 2.71828182846f;
/// <summary>The golden ratio. It is the value of a/b where a/b = (a+b)/a. It's the positive root of x^2-x-1</summary>
public const float GOLDEN_RATIO = 1.61803398875f;
/// <summary>The square root of two. The length of the vector (1,1)</summary>
public const float SQRT2 = 1.41421356237f;
/// <summary>The reciprocal of the square root of two. The components of the vector (1,1)</summary>
public const float RSQRT2 = 1f / SQRT2;
/// <summary>Multiply an angle in degrees by this, to convert it to radians</summary>
public const float Deg2Rad = TAU / 360f;
/// <summary>Multiply an angle in radians by this, to convert it to degrees</summary>
public const float Rad2Deg = 360f / TAU;
#endregion
#region Math operations
/// <summary>Returns the square root of the given value</summary>
[MethodImpl( INLINE )] public static float Sqrt( float value ) => (float)Math.Sqrt( value );
/// <summary>Returns the square root of each component</summary>
[MethodImpl( INLINE )] public static Vector2 Sqrt( Vector2 v ) => new Vector2( Sqrt( v.x ), Sqrt( v.y ) );
/// <inheritdoc cref="Mathfs.Sqrt(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Sqrt( Vector3 v ) => new Vector3( Sqrt( v.x ), Sqrt( v.y ), Sqrt( v.z ) );
/// <inheritdoc cref="Mathfs.Sqrt(Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Sqrt( Vector4 v ) => new Vector4( Sqrt( v.x ), Sqrt( v.y ), Sqrt( v.z ), Sqrt( v.w ) );
/// <summary>Returns the cube root of the given value, properly handling negative values unlike Pow(v,1/3)</summary>
[MethodImpl( INLINE )] public static float Cbrt( float value ) => value < 0 ? -Pow( -value, 1f / 3f ) : Pow( value, 1f / 3f );
/// <summary>Returns <c>value</c> raised to the power of <c>exponent</c></summary>
[MethodImpl( INLINE )] public static float Pow( float value, float exponent ) => (float)Math.Pow( value, exponent );
/// <summary>Returns e to the power of the given value</summary>
[MethodImpl( INLINE )] public static float Exp( float power ) => (float)Math.Exp( power );
/// <summary>Returns the logarithm of a value, with the given base</summary>
[MethodImpl( INLINE )] public static float Log( float value, float @base ) => (float)Math.Log( value, @base );
/// <summary>Returns the natural logarithm of the given value</summary>
[MethodImpl( INLINE )] public static float Log( float value ) => (float)Math.Log( value );
/// <summary>Returns the base 10 logarithm of the given value</summary>
[MethodImpl( INLINE )] public static float Log10( float value ) => (float)Math.Log10( value );
/// <summary>Returns the binomial coefficient n over k</summary>
public static ulong BinomialCoef( uint n, uint k ) {
// source: https://blog.plover.com/math/choose.html
ulong r = 1;
if( k > n ) return 0;
for( ulong d = 1; d <= k; d++ ) {
r *= n--;
r /= d;
}
return r;
// mathematically clean but extremely prone to overflow
//return Factorial( n ) / ( Factorial( k ) * Factorial( n - k ) );
}
/// <summary>Returns the Factorial of a given value from 0 to 12</summary>
/// <param name="value">A value between 0 and 12 (integers can't store the factorial of 13 or above)</param>
[MethodImpl( INLINE )] public static int Factorial( uint value ) {
if( value <= 12 )
return factorialInt[value];
if( value <= 20 )
throw new OverflowException( $"The Factorial of {value} is too big for integer representation, please use {nameof(FactorialLong)} instead" );
throw new OverflowException( $"The Factorial of {value} is too big for integer representation" );
}
/// <summary>Returns the Factorial of a given value from 0 to 20</summary>
/// <param name="value">A value between 0 and 20 (neither long nor ulong can store values large enough for the factorial of 21)</param>
[MethodImpl( INLINE )] public static long FactorialLong( uint value ) {
if( value <= 20 )
return factorialLong[value];
throw new OverflowException( $"The Factorial of {value} is too big for integer representation, even unsigned longs, soooo, rip" );
}
static readonly long[] factorialLong = {
/*0*/ 1,
/*1*/ 1,
/*2*/ 2,
/*3*/ 6,
/*4*/ 24,
/*5*/ 120,
/*6*/ 720,
/*7*/ 5040,
/*8*/ 40320,
/*9*/ 362880,
/*10*/ 3628800,
/*11*/ 39916800,
/*12*/ 479001600,
/*13*/ 6227020800,
/*14*/ 87178291200,
/*15*/ 1307674368000,
/*16*/ 20922789888000,
/*17*/ 355687428096000,
/*18*/ 6402373705728000,
/*19*/ 121645100408832000,
/*20*/ 2432902008176640000
};
static readonly int[] factorialInt = {
/*0*/ 1,
/*1*/ 1,
/*2*/ 2,
/*3*/ 6,
/*4*/ 24,
/*5*/ 120,
/*6*/ 720,
/*7*/ 5040,
/*8*/ 40320,
/*9*/ 362880,
/*10*/ 3628800,
/*11*/ 39916800,
/*12*/ 479001600
};
#endregion
#region Floating point shenanigans
/// <summary>A very small value, used for various floating point inaccuracy thresholds</summary>
public static readonly float Epsilon = UnityEngineInternal.MathfInternal.IsFlushToZeroEnabled ? UnityEngineInternal.MathfInternal.FloatMinNormal : UnityEngineInternal.MathfInternal.FloatMinDenormal;
/// <summary>A very small value, multiplied by 8, used for various floating point approximate checks</summary>
public static readonly float Epsilon8 = Epsilon * 8;
/// <summary>float.PositiveInfinity</summary>
public const float Infinity = float.PositiveInfinity;
/// <summary>float.NegativeInfinity</summary>
public const float NegativeInfinity = float.NegativeInfinity;
/// <summary>Returns whether or not two values are approximately equal.
/// They are considered equal if they are within a <c>Mathfs.Epsilon*8</c> or <c>max(a,b)*0.000001f</c> range of each other</summary>
/// <param name="a">The first value to compare</param>
/// <param name="b">The second value to compare</param>
[MethodImpl( INLINE )] public static bool Approximately( float a, float b ) => Abs( b - a ) < Max( 0.000001f * Max( Abs( a ), Abs( b ) ), Epsilon8 );
/// <summary>Returns whether or not three values are approximately equal.
/// They are considered equal if they are within a <c>Mathfs.Epsilon*8</c> or <c>max(a,b)*0.000001f</c> range of each other</summary>
/// <param name="a">The first value to compare</param>
/// <param name="b">The second value to compare</param>
/// <param name="c">The third value to compare</param>
[MethodImpl( INLINE )] public static bool Approximately( float a, float b, float c ) => Approximately( Min( a, b, c ), Max( a, b, c ) );
/// <summary>Returns whether or not four values are approximately equal.
/// They are considered equal if they are within a <c>Mathfs.Epsilon*8</c> or <c>max(a,b)*0.000001f</c> range of each other</summary>
/// <param name="a">The first value to compare</param>
/// <param name="b">The second value to compare</param>
/// <param name="c">The third value to compare</param>
/// <param name="d">The fourth value to compare</param>
[MethodImpl( INLINE )] public static bool Approximately( float a, float b, float c, float d ) => Approximately( Min( a, b, c, d ), Max( a, b, c, d ) );
/// <summary>Returns whether or not the given values are approximately equal.
/// They are considered equal if they are within a <c>Mathfs.Epsilon*8</c> or <c>max(a,b)*0.000001f</c> range of each other</summary>
[MethodImpl( INLINE )] public static bool Approximately( params float[] values ) => Approximately( Min( values ), Max( values ) );
/// <inheritdoc cref="Approximately(float,float)"/>
[MethodImpl( INLINE )] public static bool Approximately( Vector2 a, Vector2 b ) => Approximately( a.x, b.x ) && Approximately( a.y, b.y );
/// <inheritdoc cref="Approximately(float,float)"/>
[MethodImpl( INLINE )] public static bool Approximately( Vector3 a, Vector3 b ) => Approximately( a.x, b.x ) && Approximately( a.y, b.y ) && Approximately( a.z, b.z );
/// <inheritdoc cref="Approximately(float,float)"/>
[MethodImpl( INLINE )] public static bool Approximately( Vector4 a, Vector4 b ) => Approximately( a.x, b.x ) && Approximately( a.y, b.y ) && Approximately( a.z, b.z ) && Approximately( a.w, b.w );
/// <inheritdoc cref="Approximately(float,float)"/>
[MethodImpl( INLINE )] public static bool Approximately( Color a, Color b ) => Approximately( a.r, b.r ) && Approximately( a.g, b.g ) && Approximately( a.b, b.b ) && Approximately( a.a, b.a );
#endregion
#region Trigonometry
/// <summary>Returns the cosine of the given angle. Equivalent to the x-component of a unit vector with the same angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Cos( float angRad ) => (float)Math.Cos( angRad );
/// <summary>Returns the sine of the given angle. Equivalent to the y-component of a unit vector with the same angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Sin( float angRad ) => (float)Math.Sin( angRad );
/// <summary>Returns the tangent of the given angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Tan( float angRad ) => (float)Math.Tan( angRad );
/// <summary>Returns the arc cosine of the given value, in radians</summary>
/// <param name="value">A value between -1 and 1</param>
[MethodImpl( INLINE )] public static float Acos( float value ) => (float)Math.Acos( value );
/// <summary>Returns the arc sine of the given value, in radians</summary>
/// <param name="value">A value between -1 and 1</param>
[MethodImpl( INLINE )] public static float Asin( float value ) => (float)Math.Asin( value );
/// <summary>Returns the arc tangent of the given value, in radians</summary>
/// <param name="value">A value between -1 and 1</param>
[MethodImpl( INLINE )] public static float Atan( float value ) => (float)Math.Atan( value );
/// <summary>Returns the angle of a vector. I don't recommend using this function, it's confusing~ Use Mathfs.DirToAng instead</summary>
/// <param name="y">The y component of the vector. They're flipped yeah I know but this is how everyone implements if for some godforsaken reason</param>
/// <param name="x">The x component of the vector. They're flipped yeah I know but this is how everyone implements if for some godforsaken reason</param>
[MethodImpl( INLINE )] public static float Atan2( float y, float x ) => (float)Math.Atan2( y, x );
/// <summary>Returns the cosecant of the given angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Csc( float angRad ) => 1f / (float)Math.Sin( angRad );
/// <summary>Returns the secant of the given angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Sec( float angRad ) => 1f / (float)Math.Cos( angRad );
/// <summary>Returns the cotangent of the given angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Cot( float angRad ) => 1f / (float)Math.Tan( angRad );
/// <summary>Returns the versine of the given angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Ver( float angRad ) => 1 - (float)Math.Cos( angRad );
/// <summary>Returns the coversine of the given angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Cvs( float angRad ) => 1 - (float)Math.Sin( angRad );
/// <summary>Returns the chord of the given angle</summary>
/// <param name="angRad">Angle in radians</param>
[MethodImpl( INLINE )] public static float Crd( float angRad ) => 2 * (float)Math.Sin( angRad / 2 );
#endregion
#region Hyperbolic Trigonometry
/// <summary>Returns the hyperbolic cosine of the given hyperbolic angle</summary>
[MethodImpl( INLINE )] public static float Cosh( float x ) => (float)Math.Cosh( x );
/// <summary>Returns the hyperbolic sine of the given hyperbolic angle</summary>
[MethodImpl( INLINE )] public static float Sinh( float x ) => (float)Math.Sinh( x );
/// <summary>Returns the hyperbolic tangent of the given hyperbolic angle</summary>
[MethodImpl( INLINE )] public static float Tanh( float x ) => (float)Math.Tanh( x );
/// <summary>Returns the hyperbolic arc cosine of the given value</summary>
[MethodImpl( INLINE )] public static float Acosh( float x ) => (float)Math.Log( x + Sqrt( x * x - 1 ) );
/// <summary>Returns the hyperbolic arc sine of the given value</summary>
[MethodImpl( INLINE )] public static float Asinh( float x ) => (float)Math.Log( x + Sqrt( x * x + 1 ) );
/// <summary>Returns the hyperbolic arc tangent of the given value</summary>
[MethodImpl( INLINE )] public static float Atanh( float x ) => (float)( 0.5 * Math.Log( ( 1 + x ) / ( 1 - x ) ) );
#endregion
#region Absolute Values
/// <summary>Returns the absolute value. Basically makes negative numbers positive</summary>
[MethodImpl( INLINE )] public static float Abs( float value ) => Math.Abs( value );
/// <inheritdoc cref="Mathfs.Abs(float)"/>
[MethodImpl( INLINE )] public static int Abs( int value ) => Math.Abs( value );
/// <summary>Returns the absolute value, per component. Basically makes negative numbers positive</summary>
[MethodImpl( INLINE )] public static Vector2 Abs( Vector2 v ) => new Vector2( Abs( v.x ), Abs( v.y ) );
/// <inheritdoc cref="Mathfs.Abs(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Abs( Vector3 v ) => new Vector3( Abs( v.x ), Abs( v.y ), Abs( v.z ) );
/// <inheritdoc cref="Mathfs.Abs(Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Abs( Vector4 v ) => new Vector4( Abs( v.x ), Abs( v.y ), Abs( v.z ), Abs( v.w ) );
/// <inheritdoc cref="Mathfs.Abs(Vector2)"/>
[MethodImpl( INLINE )] public static Vector2Int Abs( Vector2Int v ) => new Vector2Int( Abs( v.x ), Abs( v.y ) );
/// <inheritdoc cref="Mathfs.Abs(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3Int Abs( Vector3Int v ) => new Vector3Int( Abs( v.x ), Abs( v.y ), Abs( v.z ));
#endregion
#region Clamping
/// <summary>Returns the value clamped between <c>min</c> and <c>max</c></summary>
/// <param name="value">The value to clamp</param>
/// <param name="min">The minimum value</param>
/// <param name="max">The maximum value</param>
public static float Clamp( float value, float min, float max ) => value < min ? min : value > max ? max : value;
/// <summary>Clamps each component between <c>min</c> and <c>max</c></summary>
public static Vector2 Clamp( Vector2 v, Vector2 min, Vector2 max ) =>
new Vector2(
v.x < min.x ? min.x : v.x > max.x ? max.x : v.x,
v.y < min.y ? min.y : v.y > max.y ? max.y : v.y
);
/// <inheritdoc cref="Mathfs.Clamp(Vector2,Vector2,Vector2)"/>
public static Vector3 Clamp( Vector3 v, Vector3 min, Vector3 max ) =>
new Vector3(
v.x < min.x ? min.x : v.x > max.x ? max.x : v.x,
v.y < min.y ? min.y : v.y > max.y ? max.y : v.y,
v.z < min.z ? min.z : v.z > max.z ? max.z : v.z
);
/// <inheritdoc cref="Mathfs.Clamp(Vector2,Vector2,Vector2)"/>
public static Vector4 Clamp( Vector4 v, Vector4 min, Vector4 max ) =>
new Vector4(
v.x < min.x ? min.x : v.x > max.x ? max.x : v.x,
v.y < min.y ? min.y : v.y > max.y ? max.y : v.y,
v.z < min.z ? min.z : v.z > max.z ? max.z : v.z,
v.w < min.w ? min.w : v.w > max.w ? max.w : v.w
);
/// <inheritdoc cref="Mathfs.Clamp(Vector2,Vector2,Vector2)"/>
public static Vector2Int Clamp( Vector2Int v, Vector2Int min, Vector2Int max ) =>
new Vector2Int(
v.x < min.x ? min.x : v.x > max.x ? max.x : v.x,
v.y < min.y ? min.y : v.y > max.y ? max.y : v.y
);
/// <inheritdoc cref="Mathfs.Clamp(Vector2,Vector2,Vector2)"/>
public static Vector3Int Clamp( Vector3Int v, Vector3Int min, Vector3Int max ) =>
new Vector3Int(
v.x < min.x ? min.x : v.x > max.x ? max.x : v.x,
v.y < min.y ? min.y : v.y > max.y ? max.y : v.y,
v.z < min.z ? min.z : v.z > max.z ? max.z : v.z
);
/// <inheritdoc cref="Mathfs.Clamp(float,float,float)"/>
public static int Clamp( int value, int min, int max ) => value < min ? min : value > max ? max : value;
/// <summary>Returns the value clamped between 0 and 1</summary>
public static float Clamp01( float value ) => value < 0f ? 0f : value > 1f ? 1f : value;
/// <summary>Clamps each component between 0 and 1</summary>
public static Vector2 Clamp01( Vector2 v ) =>
new Vector2(
v.x < 0f ? 0f : v.x > 1f ? 1f : v.x,
v.y < 0f ? 0f : v.y > 1f ? 1f : v.y
);
/// <inheritdoc cref="Mathfs.Clamp01(Vector2)"/>
public static Vector3 Clamp01( Vector3 v ) =>
new Vector3(
v.x < 0f ? 0f : v.x > 1f ? 1f : v.x,
v.y < 0f ? 0f : v.y > 1f ? 1f : v.y,
v.z < 0f ? 0f : v.z > 1f ? 1f : v.z
);
/// <inheritdoc cref="Mathfs.Clamp01(Vector2)"/>
public static Vector4 Clamp01( Vector4 v ) =>
new Vector4(
v.x < 0f ? 0f : v.x > 1f ? 1f : v.x,
v.y < 0f ? 0f : v.y > 1f ? 1f : v.y,
v.z < 0f ? 0f : v.z > 1f ? 1f : v.z,
v.w < 0f ? 0f : v.w > 1f ? 1f : v.w
);
/// <inheritdoc cref="Mathfs.Clamp01(Vector2)"/>
public static Vector2Int Clamp01( Vector2Int v ) =>
new Vector2Int(
v.x < 0 ? 0 : v.x > 1 ? 1 : v.x,
v.y < 0 ? 0 : v.y > 1 ? 1 : v.y
);
/// <inheritdoc cref="Mathfs.Clamp01(Vector2)"/>
public static Vector3Int Clamp01( Vector3Int v ) =>
new Vector3Int(
v.x < 0 ? 0 : v.x > 1 ? 1 : v.x,
v.y < 0 ? 0 : v.y > 1 ? 1 : v.y,
v.z < 0 ? 0 : v.z > 1 ? 1 : v.z
);
/// <summary>Clamps the value between -1 and 1</summary>
public static float ClampNeg1to1( float value ) => value < -1f ? -1f : value > 1f ? 1f : value;
/// <summary>Clamps each component between -1 and 1</summary>
public static Vector2 ClampNeg1to1( Vector2 v ) =>
new Vector2(
v.x < -1f ? -1f : v.x > 1f ? 1f : v.x,
v.y < -1f ? -1f : v.y > 1f ? 1f : v.y
);
/// <inheritdoc cref="Mathfs.ClampNeg1to1(Vector2)"/>
public static Vector3 ClampNeg1to1( Vector3 v ) =>
new Vector3(
v.x < -1f ? -1f : v.x > 1f ? 1f : v.x,
v.y < -1f ? -1f : v.y > 1f ? 1f : v.y,
v.z < -1f ? -1f : v.z > 1f ? 1f : v.z
);
/// <inheritdoc cref="Mathfs.ClampNeg1to1(Vector2)"/>
public static Vector4 ClampNeg1to1( Vector4 v ) =>
new Vector4(
v.x < -1f ? -1f : v.x > 1f ? 1f : v.x,
v.y < -1f ? -1f : v.y > 1f ? 1f : v.y,
v.z < -1f ? -1f : v.z > 1f ? 1f : v.z,
v.w < -1f ? -1f : v.w > 1f ? 1f : v.w
);
/// <inheritdoc cref="Mathfs.ClampNeg1to1(Vector2)"/>
public static Vector2Int ClampNeg1to1( Vector2Int v ) =>
new Vector2Int(
v.x < -1 ? -1 : v.x > 1 ? 1 : v.x,
v.y < -1 ? -1 : v.y > 1 ? 1 : v.y
);
/// <inheritdoc cref="Mathfs.ClampNeg1to1(Vector2)"/>
public static Vector3Int ClampNeg1to1( Vector3Int v ) =>
new Vector3Int(
v.x < -1 ? -1 : v.x > 1 ? 1 : v.x,
v.y < -1 ? -1 : v.y > 1 ? 1 : v.y,
v.z < -1 ? -1 : v.z > 1 ? 1 : v.z
);
#endregion
#region Min & Max
/// <summary>Returns the smallest of the two values</summary>
[MethodImpl( INLINE )] public static float Min( float a, float b ) => a < b ? a : b;
/// <summary>Returns the smallest of the three values</summary>
[MethodImpl( INLINE )] public static float Min( float a, float b, float c ) => Min( Min( a, b ), c );
/// <summary>Returns the smallest of the four values</summary>
[MethodImpl( INLINE )] public static float Min( float a, float b, float c, float d ) => Min( Min( a, b ), Min( c, d ) );
/// <summary>Returns the largest of the two values</summary>
[MethodImpl( INLINE )] public static float Max( float a, float b ) => a > b ? a : b;
/// <summary>Returns the largest of the three values</summary>
[MethodImpl( INLINE )] public static float Max( float a, float b, float c ) => Max( Max( a, b ), c );
/// <summary>Returns the largest of the four values</summary>
[MethodImpl( INLINE )] public static float Max( float a, float b, float c, float d ) => Max( Max( a, b ), Max( c, d ) );
/// <summary>Returns the smallest of the two values</summary>
[MethodImpl( INLINE )] public static int Min( int a, int b ) => a < b ? a : b;
/// <summary>Returns the smallest of the three values</summary>
[MethodImpl( INLINE )] public static int Min( int a, int b, int c ) => Min( Min( a, b ), c );
/// <summary>Returns the smallest of the four values</summary>
[MethodImpl( INLINE )] public static int Min( int a, int b, int c, int d ) => Min( Min( a, b ), Min( c, d ) );
/// <summary>Returns the largest of the two values</summary>
[MethodImpl( INLINE )] public static int Max( int a, int b ) => a > b ? a : b;
/// <summary>Returns the largest of the three values</summary>
[MethodImpl( INLINE )] public static int Max( int a, int b, int c ) => Max( Max( a, b ), c );
/// <summary>Returns the largest of the four values</summary>
[MethodImpl( INLINE )] public static int Max( int a, int b, int c, int d ) => Max( Max( a, b ), Max( c, d ) );
/// <summary>Returns the smallest of the given values</summary>
[MethodImpl( INLINE )] public static float Min( params float[] values ) => values.Min();
/// <summary>Returns the largest of the given values</summary>
[MethodImpl( INLINE )] public static float Max( params float[] values ) => values.Max();
/// <summary>Returns the smallest of the given values</summary>
[MethodImpl( INLINE )] public static int Min( params int[] values ) => values.Min();
/// <summary>Returns the largest of the given values</summary>
[MethodImpl( INLINE )] public static int Max( params int[] values ) => values.Max();
/// <summary>Returns the minimum value of all components in the vector</summary>
[MethodImpl( INLINE )] public static float Min( Vector2 v ) => Min( v.x, v.y );
/// <inheritdoc cref="Mathfs.Min(Vector2)"/>
[MethodImpl( INLINE )] public static float Min( Vector3 v ) => Min( v.x, v.y, v.z );
/// <inheritdoc cref="Mathfs.Min(Vector2)"/>
[MethodImpl( INLINE )] public static float Min( Vector4 v ) => Min( v.x, v.y, v.z, v.w );
/// <summary>Returns the maximum value of all components in the vector</summary>
[MethodImpl( INLINE )] public static float Max( Vector2 v ) => Max( v.x, v.y );
/// <inheritdoc cref="Mathfs.Max(Vector2)"/>
[MethodImpl( INLINE )] public static float Max( Vector3 v ) => Max( v.x, v.y, v.z );
/// <inheritdoc cref="Mathfs.Max(Vector2)"/>
[MethodImpl( INLINE )] public static float Max( Vector4 v ) => Max( v.x, v.y, v.z, v.w );
/// <inheritdoc cref="Mathfs.Min(Vector2)"/>
[MethodImpl( INLINE )] public static int Min( Vector2Int v ) => Min( v.x, v.y );
/// <inheritdoc cref="Mathfs.Min(Vector2)"/>
[MethodImpl( INLINE )] public static int Min( Vector3Int v ) => Min( v.x, v.y, v.z );
/// <inheritdoc cref="Mathfs.Max(Vector2)"/>
[MethodImpl( INLINE )] public static int Max( Vector2Int v ) => Max( v.x, v.y );
/// <inheritdoc cref="Mathfs.Max(Vector2)"/>
[MethodImpl( INLINE )] public static int Max( Vector3Int v ) => Max( v.x, v.y, v.z );
#endregion
#region Signs & Rounding
/// <summary>The sign of the value. Returns -1 if negative, returns 1 if greater than or equal to 0</summary>
[MethodImpl( INLINE )] public static float Sign( float value ) => value >= 0f ? 1 : -1;
/// <summary>The sign of each component. Returns -1 if negative, returns 1 if greater than or equal to 0</summary>
[MethodImpl( INLINE )] public static Vector2 Sign( Vector2 value ) => new Vector2( value.x >= 0f ? 1 : -1, value.y >= 0f ? 1 : -1 );
/// <inheritdoc cref="Mathfs.Sign(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Sign( Vector3 value ) => new Vector3( value.x >= 0f ? 1 : -1, value.y >= 0f ? 1 : -1, value.z >= 0f ? 1 : -1 );
/// <inheritdoc cref="Mathfs.Sign(Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Sign( Vector4 value ) => new Vector4( value.x >= 0f ? 1 : -1, value.y >= 0f ? 1 : -1, value.z >= 0f ? 1 : -1, value.w >= 0f ? 1 : -1 );
/// <summary>Returns the sign of the value, either -1 if negative, or 1 if positive or 0</summary>
[MethodImpl( INLINE )] public static int Sign( int value ) => value >= 0 ? 1 : -1;
/// <summary>The sign of the value as an integer. Returns -1 if negative, returns 1 if greater than or equal to 0</summary>
[MethodImpl( INLINE )] public static int SignAsInt( float value ) => value >= 0f ? 1 : -1;
/// <summary>The sign of the value. Returns -1 if negative, return 0 if zero (or within the given threshold), returns 1 if positive</summary>
[MethodImpl( INLINE )] public static float SignWithZero( float value, float zeroThreshold = 0.000001f ) => Abs( value ) < zeroThreshold ? 0 : Sign( value );
/// <summary>The sign of each component. Returns -1 if negative, return 0 if zero (or within the given threshold), returns 1 if positive</summary>
[MethodImpl( INLINE )] public static Vector2 SignWithZero( Vector2 value, float zeroThreshold = 0.000001f ) =>
new Vector2(
Abs( value.x ) < zeroThreshold ? 0 : Sign( value.x ),
Abs( value.y ) < zeroThreshold ? 0 : Sign( value.y )
);
/// <inheritdoc cref="Mathfs.SignWithZero(Vector2,float)"/>
[MethodImpl( INLINE )] public static Vector3 SignWithZero( Vector3 value, float zeroThreshold = 0.000001f ) =>
new Vector3(
Abs( value.x ) < zeroThreshold ? 0 : Sign( value.x ),
Abs( value.y ) < zeroThreshold ? 0 : Sign( value.y ),
Abs( value.z ) < zeroThreshold ? 0 : Sign( value.z )
);
/// <inheritdoc cref="Mathfs.SignWithZero(Vector2,float)"/>
[MethodImpl( INLINE )] public static Vector4 SignWithZero( Vector4 value, float zeroThreshold = 0.000001f ) =>
new Vector4(
Abs( value.x ) < zeroThreshold ? 0 : Sign( value.x ),
Abs( value.y ) < zeroThreshold ? 0 : Sign( value.y ),
Abs( value.z ) < zeroThreshold ? 0 : Sign( value.z ),
Abs( value.w ) < zeroThreshold ? 0 : Sign( value.w )
);
/// <summary>Returns the sign of the value, either -1 if negative, 0 if zero, 1 if positive</summary>
[MethodImpl( INLINE )] public static int SignWithZero( int value ) => value == 0 ? 0 : Sign( value );
/// <summary>The sign of the value. Returns -1 if negative, return 0 if zero (or within the given threshold), returns 1 if positive</summary>
[MethodImpl( INLINE )] public static int SignWithZeroAsInt( float value, float zeroThreshold = 0.000001f ) => Abs( value ) < zeroThreshold ? 0 : SignAsInt( value );
/// <summary>Rounds the value down to the nearest integer</summary>
[MethodImpl( INLINE )] public static float Floor( float value ) => (float)Math.Floor( value );
/// <summary>Rounds the vector components down to the nearest integer</summary>
[MethodImpl( INLINE )] public static Vector2 Floor( Vector2 value ) => new Vector2( (float)Math.Floor( value.x ), (float)Math.Floor( value.y ) );
/// <inheritdoc cref="Mathfs.Floor(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Floor( Vector3 value ) => new Vector3( (float)Math.Floor( value.x ), (float)Math.Floor( value.y ), (float)Math.Floor( value.z ) );
/// <inheritdoc cref="Mathfs.Floor(Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Floor( Vector4 value ) => new Vector4( (float)Math.Floor( value.x ), (float)Math.Floor( value.y ), (float)Math.Floor( value.z ), (float)Math.Floor( value.w ) );
/// <summary>Rounds the value down to the nearest integer, returning an int value</summary>
[MethodImpl( INLINE )] public static int FloorToInt( float value ) => (int)Math.Floor( value );
/// <summary>Rounds the vector components down to the nearest integer, returning an integer vector</summary>
[MethodImpl( INLINE )] public static Vector2Int FloorToInt( Vector2 value ) => new Vector2Int( (int)Math.Floor( value.x ), (int)Math.Floor( value.y ) );
/// <inheritdoc cref="Mathfs.FloorToInt(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3Int FloorToInt( Vector3 value ) => new Vector3Int( (int)Math.Floor( value.x ), (int)Math.Floor( value.y ), (int)Math.Floor( value.z ) );
/// <summary>Rounds the value up to the nearest integer</summary>
[MethodImpl( INLINE )] public static float Ceil( float value ) => (float)Math.Ceiling( value );
/// <summary>Rounds the vector components up to the nearest integer</summary>
[MethodImpl( INLINE )] public static Vector2 Ceil( Vector2 value ) => new Vector2( (float)Math.Ceiling( value.x ), (float)Math.Ceiling( value.y ) );
/// <inheritdoc cref="Mathfs.Ceil(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Ceil( Vector3 value ) => new Vector3( (float)Math.Ceiling( value.x ), (float)Math.Ceiling( value.y ), (float)Math.Ceiling( value.z ) );
/// <inheritdoc cref="Mathfs.Ceil(Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Ceil( Vector4 value ) => new Vector4( (float)Math.Ceiling( value.x ), (float)Math.Ceiling( value.y ), (float)Math.Ceiling( value.z ), (float)Math.Ceiling( value.w ) );
/// <summary>Rounds the value up to the nearest integer, returning an int value</summary>
[MethodImpl( INLINE )] public static int CeilToInt( float value ) => (int)Math.Ceiling( value );
/// <summary>Rounds the vector components up to the nearest integer, returning an integer vector</summary>
[MethodImpl( INLINE )] public static Vector2Int CeilToInt( Vector2 value ) => new Vector2Int( (int)Math.Ceiling( value.x ), (int)Math.Ceiling( value.y ) );
/// <inheritdoc cref="Mathfs.CeilToInt(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3Int CeilToInt( Vector3 value ) => new Vector3Int( (int)Math.Ceiling( value.x ), (int)Math.Ceiling( value.y ), (int)Math.Ceiling( value.z ) );
/// <summary>Rounds the value to the nearest integer</summary>
[MethodImpl( INLINE )] public static float Round( float value ) => (float)Math.Round( value );
/// <summary>Rounds the vector components to the nearest integer</summary>
[MethodImpl( INLINE )] public static Vector2 Round( Vector2 value ) => new Vector2( (float)Math.Round( value.x ), (float)Math.Round( value.y ) );
/// <inheritdoc cref="Mathfs.Round(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Round( Vector3 value ) => new Vector3( (float)Math.Round( value.x ), (float)Math.Round( value.y ), (float)Math.Round( value.z ) );
/// <inheritdoc cref="Mathfs.Round(Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Round( Vector4 value ) => new Vector4( (float)Math.Round( value.x ), (float)Math.Round( value.y ), (float)Math.Round( value.z ), (float)Math.Round( value.w ) );
/// <summary>Rounds the value to the nearest value, snapped to the given interval size</summary>
[MethodImpl( INLINE )] public static float Round( float value, float snapInterval ) => Round( value / snapInterval ) * snapInterval;
/// <summary>Rounds the vector components to the nearest value, snapped to the given interval size</summary>
[MethodImpl( INLINE )] public static Vector2 Round( Vector2 value, float snapInterval ) => new Vector2( Round( value.x, snapInterval ), Round( value.y, snapInterval ) );
/// <inheritdoc cref="Mathfs.Round(Vector2,float)"/>
[MethodImpl( INLINE )] public static Vector3 Round( Vector3 value, float snapInterval ) => new Vector3( Round( value.x, snapInterval ), Round( value.y, snapInterval ), Round( value.z, snapInterval ) );
/// <inheritdoc cref="Mathfs.Round(Vector2,float)"/>
[MethodImpl( INLINE )] public static Vector4 Round( Vector4 value, float snapInterval ) => new Vector4( Round( value.x, snapInterval ), Round( value.y, snapInterval ), Round( value.z, snapInterval ), Round( value.w, snapInterval ) );
/// <summary>Rounds the value to the nearest integer, returning an int value</summary>
[MethodImpl( INLINE )] public static int RoundToInt( float value ) => (int)Math.Round( value );
/// <summary>Rounds the vector components to the nearest integer, returning an integer vector</summary>
[MethodImpl( INLINE )] public static Vector2Int RoundToInt( Vector2 value ) => new Vector2Int( (int)Math.Round( value.x ), (int)Math.Round( value.y ) );
/// <inheritdoc cref="Mathfs.RoundToInt(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3Int RoundToInt( Vector3 value ) => new Vector3Int( (int)Math.Round( value.x ), (int)Math.Round( value.y ), (int)Math.Round( value.z ) );
#endregion
#region Range Repeating
/// <summary>Returns the fractional part of the value. Equivalent to <c>x - floor(x)</c></summary>
[MethodImpl( INLINE )] public static float Frac( float x ) => x - Floor( x );
/// <summary>Returns the fractional part of the value for each component. Equivalent to <c>v - floor(v)</c></summary>
[MethodImpl( INLINE )] public static Vector2 Frac( Vector2 v ) => new Vector2( v.x - Floor( v.x ), v.y - Floor( v.y ) );
/// <inheritdoc cref="Mathfs.Frac(Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Frac( Vector3 v ) => new Vector3( v.x - Floor( v.x ), v.y - Floor( v.y ), v.z - Floor( v.z ) );
/// <inheritdoc cref="Mathfs.Frac(Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Frac( Vector4 v ) => new Vector4( v.x - Floor( v.x ), v.y - Floor( v.y ), v.z - Floor( v.z ), v.w - Floor( v.w ) );
/// <summary>Repeats the given value in the interval specified by length</summary>
[MethodImpl( INLINE )] public static float Repeat( float value, float length ) => Clamp( value - Floor( value / length ) * length, 0.0f, length );
/// <summary>Modulo, but, behaves the way you want with negative values, for stuff like array[(n+1)%length] etc.</summary>
[MethodImpl( INLINE )] public static int Mod( int value, int length ) => ( value % length + length ) % length;
/// <summary>Repeats a value within a range, going back and forth</summary>
[MethodImpl( INLINE )] public static float PingPong( float t, float length ) => length - Abs( Repeat( t, length * 2f ) - length );
/// <summary>Returns the height of in a triangle wave at time <c>t</c> going from <c>0</c> to <c>1</c> and back to <c>0</c> within the the given <c>period</c></summary>
[MethodImpl( INLINE )] public static float TriangleWave( float t, float period = 1f ) {
float x = t / period;
return 1f - Abs( 2 * ( x - Floor( x ) ) - 1 );
}
#endregion
#region Smoothing & Easing Curves
/// <summary>Applies cubic smoothing to the 0-1 interval, also known as the smoothstep function. Similar to an EaseInOut operation</summary>
[MethodImpl( INLINE )] public static float Smooth01( float x ) => x * x * ( 3 - 2 * x );
/// <summary>Applies quintic smoothing to the 0-1 interval, also known as the smootherstep function. Similar to an EaseInOut operation</summary>
[MethodImpl( INLINE )] public static float Smoother01( float x ) => x * x * x * ( x * ( x * 6 - 15 ) + 10 );
/// <summary>Applies trigonometric smoothing to the 0-1 interval. Similar to an EaseInOut operation</summary>
[MethodImpl( INLINE )] public static float SmoothCos01( float x ) => Cos( x * PI ) * -0.5f + 0.5f;
/// <summary>Applies a gamma curve or something idk I've never used this function before but it was part of Unity's original Mathfs.cs and it's undocumented</summary>
public static float Gamma( float value, float absmax, float gamma ) {
bool negative = value < 0F;
float absval = Abs( value );
if( absval > absmax )
return negative ? -absval : absval;
float result = Pow( absval / absmax, gamma ) * absmax;
return negative ? -result : result;
}
#endregion
#region Value & Vector interpolation
/// <summary>Blends between a and b, based on the t-value. When t = 0 it returns a, when t = 1 it returns b, and any values between are blended linearly</summary>
/// <param name="a">The start value, when t is 0</param>
/// <param name="b">The end value, when t is 1</param>
/// <param name="t">The t-value from 0 to 1 representing position along the lerp</param>
[MethodImpl( INLINE )] public static float Lerp( float a, float b, float t ) => ( 1f - t ) * a + t * b;
/// <summary>Blends between a and b of each component, based on the t-value of each component in the t-vector. When t = 0 it returns a, when t = 1 it returns b, and any values between are blended linearly</summary>
/// <param name="a">The start value, when t is 0</param>
/// <param name="b">The end value, when t is 1</param>
/// <param name="t">The t-values from 0 to 1 representing position along the lerp</param>
[MethodImpl( INLINE )] public static Vector2 Lerp( Vector2 a, Vector2 b, Vector2 t ) => new Vector2( Lerp( a.x, b.x, t.x ), Lerp( a.y, b.y, t.y ) );
/// <inheritdoc cref="Mathfs.Lerp(Vector2,Vector2,Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Lerp( Vector3 a, Vector3 b, Vector3 t ) => new Vector3( Lerp( a.x, b.x, t.x ), Lerp( a.y, b.y, t.y ), Lerp( a.z, b.z, t.z ) );
/// <inheritdoc cref="Mathfs.Lerp(Vector2,Vector2,Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Lerp( Vector4 a, Vector4 b, Vector4 t ) => new Vector4( Lerp( a.x, b.x, t.x ), Lerp( a.y, b.y, t.y ), Lerp( a.z, b.z, t.z ), Lerp( a.w, b.w, t.w ) );
/// <summary>Linearly blends between two rectangles, moving and resizing from the center. Note: this lerp is unclamped</summary>
/// <param name="a">The start value, when t is 0</param>
/// <param name="b">The end value, when t is 1</param>
/// <param name="t">The t-values from 0 to 1 representing position along the lerp</param>
public static Rect Lerp( Rect a, Rect b, float t ) {
Vector2 center = Vector2.LerpUnclamped( a.center, b.center, t );
Vector2 size = Vector2.LerpUnclamped( a.size, b.size, t );
return new Rect( default, size ) { center = center };
}
/// <summary>Blends between a and b, based on the t-value. When t = 0 it returns a, when t = 1 it returns b, and any values between are blended linearly</summary>
/// <param name="a">The start value, when t is 0</param>
/// <param name="b">The end value, when t is 1</param>
/// <param name="t">The t-value from 0 to 1 representing position along the lerp, clamped between 0 and 1</param>
[MethodImpl( INLINE )] public static float LerpClamped( float a, float b, float t ) => Lerp( a, b, Clamp01( t ) );
/// <summary>Lerps between a and b, applying cubic smoothing to the t-value</summary>
/// <param name="a">The start value, when t is 0</param>
/// <param name="b">The end value, when t is 1</param>
/// <param name="t">The t-value from 0 to 1 representing position along the lerp, clamped between 0 and 1</param>
[MethodImpl( INLINE )] public static float LerpSmooth( float a, float b, float t ) => Lerp( a, b, Smooth01( Clamp01( t ) ) );
/// <summary>Given a value between a and b, returns its normalized location in that range, as a t-value (interpolant) from 0 to 1</summary>
/// <param name="a">The start of the range, where it would return 0</param>
/// <param name="b">The end of the range, where it would return 1</param>
/// <param name="value">A value between a and b. Note: values outside this range are still valid, and will be extrapolated</param>
[MethodImpl( INLINE )] public static float InverseLerp( float a, float b, float value ) => ( value - a ) / ( b - a );
/// <summary>Given a value between a and b, returns its normalized location in that range, as a t-value (interpolant) from 0 to 1.
/// This safe version returns 0 if a == b, instead of a division by zero</summary>
/// <param name="a">The start of the range, where it would return 0</param>
/// <param name="b">The end of the range, where it would return 1</param>
/// <param name="value">A value between a and b. Note: values outside this range are still valid, and will be extrapolated</param>
[MethodImpl( INLINE )] public static float InverseLerpSafe( float a, float b, float value ) {
float den = b - a;
if( den == 0 )
return 0;
return ( value - a ) / den;
}
/// <summary>Given values between a and b in each component, returns their normalized locations in the given ranges, as t-values (interpolants) from 0 to 1</summary>
/// <param name="a">The start of the ranges, where it would return 0</param>
/// <param name="b">The end of the ranges, where it would return 1</param>
/// <param name="v">A value between a and b. Note: values outside this range are still valid, and will be extrapolated</param>
[MethodImpl( INLINE )] public static Vector2 InverseLerp( Vector2 a, Vector2 b, Vector2 v ) => new Vector2( ( v.x - a.x ) / ( b.x - a.x ), ( v.y - a.y ) / ( b.y - a.y ) );
/// <inheritdoc cref="Mathfs.InverseLerp(Vector2,Vector2,Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 InverseLerp( Vector3 a, Vector3 b, Vector3 v ) => new Vector3( ( v.x - a.x ) / ( b.x - a.x ), ( v.y - a.y ) / ( b.y - a.y ), ( v.z - a.z ) / ( b.z - a.z ) );
/// <inheritdoc cref="Mathfs.InverseLerp(Vector2,Vector2,Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 InverseLerp( Vector4 a, Vector4 b, Vector4 v ) => new Vector4( ( v.x - a.x ) / ( b.x - a.x ), ( v.y - a.y ) / ( b.y - a.y ), ( v.z - a.z ) / ( b.z - a.z ), ( v.w - a.w ) / ( b.w - a.w ) );
/// <summary>Given a value between a and b, returns its normalized location in that range, as a t-value (interpolant) clamped between 0 and 1</summary>
/// <param name="a">The start of the range, where it would return 0</param>
/// <param name="b">The end of the range, where it would return 1</param>
/// <param name="value">A value between a and b</param>
[MethodImpl( INLINE )] public static float InverseLerpClamped( float a, float b, float value ) => Clamp01( ( value - a ) / ( b - a ) );
/// <summary>Given a value between a and b, returns its normalized location in that range, as a t-value (interpolant) from 0 to 1, with cubic smoothing applied.
/// Equivalent to "smoothstep" in shader code</summary>
/// <param name="a">The start of the range, where it would return 0</param>
/// <param name="b">The end of the range, where it would return 1</param>
/// <param name="value">A value between a and b. Note: values outside this range are still valid, and will be extrapolated</param>
[MethodImpl( INLINE )] public static float InverseLerpSmooth( float a, float b, float value ) => Smooth01( Clamp01( ( value - a ) / ( b - a ) ) );
/// <summary>Remaps a value from the input range [iMin to iMax] into the output range [oMin to oMax].
/// Equivalent to Lerp(oMin,oMax,InverseLerp(iMin,iMax,value))</summary>
/// <param name="iMin">The start value of the input range</param>
/// <param name="iMax">The end value of the input range</param>
/// <param name="oMin">The start value of the output range</param>
/// <param name="oMax">The end value of the output range</param>
/// <param name="value">The value to remap</param>
[MethodImpl( INLINE )] public static float Remap( float iMin, float iMax, float oMin, float oMax, float value ) => Lerp( oMin, oMax, InverseLerp( iMin, iMax, value ) );
/// <inheritdoc cref="Mathfs.Remap(float,float,float,float,float)"/>
[MethodImpl( INLINE )] public static float Remap( float iMin, float iMax, float oMin, float oMax, int value ) => Lerp( oMin, oMax, InverseLerp( iMin, iMax, value ) );
/// <summary>Remaps values from the input range [iMin to iMax] into the output range [oMin to oMax] on a per-component basis.
/// Equivalent to Lerp(oMin,oMax,InverseLerp(iMin,iMax,value))</summary>
/// <param name="iMin">The start values of the input ranges</param>
/// <param name="iMax">The end values of the input ranges</param>
/// <param name="oMin">The start values of the output ranges</param>
/// <param name="oMax">The end values of the output ranges</param>
/// <param name="value">The values to remap</param>
[MethodImpl( INLINE )] public static Vector2 Remap( Vector2 iMin, Vector2 iMax, Vector2 oMin, Vector2 oMax, Vector2 value ) => Lerp( oMin, oMax, InverseLerp( iMin, iMax, value ) );
/// <inheritdoc cref="Mathfs.Remap(Vector2,Vector2,Vector2,Vector2,Vector2)"/>
[MethodImpl( INLINE )] public static Vector3 Remap( Vector3 iMin, Vector3 iMax, Vector3 oMin, Vector3 oMax, Vector3 value ) => Lerp( oMin, oMax, InverseLerp( iMin, iMax, value ) );
/// <inheritdoc cref="Mathfs.Remap(Vector2,Vector2,Vector2,Vector2,Vector2)"/>
[MethodImpl( INLINE )] public static Vector4 Remap( Vector4 iMin, Vector4 iMax, Vector4 oMin, Vector4 oMax, Vector4 value ) => Lerp( oMin, oMax, InverseLerp( iMin, iMax, value ) );
/// <summary>Remaps a value from the input range [iMin to iMax] into the output range [oMin to oMax], clamping to make sure it does not extrapolate.
/// Equivalent to Lerp(oMin,oMax,InverseLerpClamped(iMin,iMax,value))</summary>
/// <param name="iMin">The start value of the input range</param>
/// <param name="iMax">The end value of the input range</param>
/// <param name="oMin">The start value of the output range</param>
/// <param name="oMax">The end value of the output range</param>
/// <param name="value">The value to remap</param>
[MethodImpl( INLINE )] public static float RemapClamped( float iMin, float iMax, float oMin, float oMax, float value ) => Lerp( oMin, oMax, InverseLerpClamped( iMin, iMax, value ) );
/// <summary>Remaps a value from the input range [iMin to iMax] into the output range [0 to 1].
/// Equivalent to Lerp(0f,1f,InverseLerp(iMin,iMax,value))</summary>
[MethodImpl( INLINE )] public static float Remap01( float iMin, float iMax, float value ) => Lerp( 0f, 1f, InverseLerp( iMin, iMax, value ) );
/// <summary>Remaps a value from the input range [iMin to iMax] into the output range [0 to 1], clamping to make sure it does not extrapolate.
/// Equivalent to Lerp(0f,1f,InverseLerpClamped(iMin,iMax,value))</summary>
/// <param name="inputMin">The start value of the input range</param>
/// <param name="inputMax">The end value of the input range</param>
/// <param name="value">The value to remap</param>
[MethodImpl( INLINE )]
public static float Remap01Clamped( float inputMin, float inputMax, float value ) => Lerp( 0f, 1f, InverseLerpClamped( inputMin, inputMax, value ) );
/// <summary>Remaps a value from the input Rect to the output Rect</summary>
/// <param name="iRect">The input Rect</param>
/// <param name="oRect">The output Rect</param>
/// <param name="iPos">The input position in the input Rect space</param>
[MethodImpl( INLINE )] public static Vector2 Remap( Rect iRect, Rect oRect, Vector2 iPos ) => Remap( iRect.min, iRect.max, oRect.min, oRect.max, iPos );
/// <summary>Remaps a value from the input Bounds to the output Bounds</summary>
/// <param name="iBounds">The input Bounds</param>
/// <param name="oBounds">The output Bounds</param>
/// <param name="iPos">The input position in the input Bounds space</param>
[MethodImpl( INLINE )] public static Vector3 Remap( Bounds iBounds, Bounds oBounds, Vector3 iPos ) => Remap( iBounds.min, iBounds.max, oBounds.min, oBounds.max, iPos );
/// <summary>Exponential interpolation, the multiplicative version of lerp, useful for values such as scaling or zooming</summary>
/// <param name="a">The start value</param>
/// <param name="b">The end value</param>
/// <param name="t">The t-value from 0 to 1 representing position along the eerp</param>
[MethodImpl( INLINE )] public static float Eerp( float a, float b, float t ) => Pow( a, 1 - t ) * Pow( b, t );
/// <summary>Inverse exponential interpolation, the multiplicative version of InverseLerp, useful for values such as scaling or zooming</summary>
/// <param name="a">The start value</param>
/// <param name="b">The end value</param>
/// <param name="v">A value between a and b. Note: values outside this range are still valid, and will be extrapolated</param>
[MethodImpl( INLINE )] public static float InverseEerp( float a, float b, float v ) => Log( a / v ) / Log( a / b );
#endregion
#region Movement helpers
/// <summary>Moves a value <c>current</c> towards <c>target</c></summary>
/// <param name="current">The current value</param>
/// <param name="target">The value to move towards</param>
/// <param name="maxDelta">The maximum change that should be applied to the value</param>
public static float MoveTowards( float current, float target, float maxDelta ) {
if( Abs( target - current ) <= maxDelta )
return target;
return current + Sign( target - current ) * maxDelta;
}
/// <summary>Gradually changes a value towards a desired goal over time.
/// The value is smoothed by some spring-damper like function, which will never overshoot.
/// The function can be used to smooth any kind of value, positions, colors, scalars</summary>
/// <param name="current">The current position</param>
/// <param name="target">The position we are trying to reach</param>
/// <param name="currentVelocity">The current velocity, this value is modified by the function every time you call it</param>
/// <param name="smoothTime">Approximately the time it will take to reach the target. A smaller value will reach the target faster</param>
/// <param name="maxSpeed"> Optionally allows you to clamp the maximum speed</param>
public static float SmoothDamp( float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed = Infinity ) {
float deltaTime = Time.deltaTime;
return SmoothDamp( current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime );
}
/// <summary>Gradually changes a value towards a desired goal over time.
/// The value is smoothed by some spring-damper like function, which will never overshoot.
/// The function can be used to smooth any kind of value, positions, colors, scalars</summary>
/// <param name="current">The current position</param>
/// <param name="target">The position we are trying to reach</param>
/// <param name="currentVelocity">The current velocity, this value is modified by the function every time you call it</param>
/// <param name="smoothTime">Approximately the time it will take to reach the target. A smaller value will reach the target faster</param>
/// <param name="maxSpeed"> Optionally allows you to clamp the maximum speed</param>
/// <param name="deltaTime">The time since the last call to this function. By default Time.deltaTime</param>
public static float SmoothDamp( float current, float target, ref float currentVelocity, float smoothTime, [Uei.DefaultValue( "Mathf.Infinity" )] float maxSpeed, [Uei.DefaultValue( "Time.deltaTime" )] float deltaTime ) {
// Based on Game Programming Gems 4 Chapter 1.10
smoothTime = Max( 0.0001F, smoothTime );
float omega = 2F / smoothTime;
float x = omega * deltaTime;
float exp = 1F / ( 1F + x + 0.48F * x * x + 0.235F * x * x * x );
float change = current - target;
float originalTo = target;
// Clamp maximum speed
float maxChange = maxSpeed * smoothTime;
change = Clamp( change, -maxChange, maxChange );
target = current - change;
float temp = ( currentVelocity + omega * change ) * deltaTime;
currentVelocity = ( currentVelocity - omega * temp ) * exp;
float output = target + ( change + temp ) * exp;
// Prevent overshooting
if( originalTo - current > 0.0F == output > originalTo ) {
output = originalTo;
currentVelocity = ( output - originalTo ) / deltaTime;
}
return output;
}
#endregion
#region Weighted sums
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
[MethodImpl( INLINE )] public static float WeightedSum( Vector2 w, float a, float b ) => a * w.x + b * w.y;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
/// <param name="c">The third value, weighted by <c>w.z</c></param>
[MethodImpl( INLINE )] public static float WeightedSum( Vector3 w, float a, float b, float c ) => a * w.x + b * w.y + c * w.z;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
/// <param name="c">The third value, weighted by <c>w.z</c></param>
/// <param name="d">The fourth value, weighted by <c>w.w</c></param>
[MethodImpl( INLINE )] public static float WeightedSum( Vector4 w, float a, float b, float c, float d ) => a * w.x + b * w.y + c * w.z + d * w.w;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
[MethodImpl( INLINE )] public static Vector2 WeightedSum( Vector2 w, Vector2 a, Vector2 b ) => a * w.x + b * w.y;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
/// <param name="c">The third value, weighted by <c>w.z</c></param>
[MethodImpl( INLINE )] public static Vector2 WeightedSum( Vector3 w, Vector2 a, Vector2 b, Vector2 c ) => a * w.x + b * w.y + c * w.z;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
/// <param name="c">The third value, weighted by <c>w.z</c></param>
/// <param name="d">The fourth value, weighted by <c>w.w</c></param>
[MethodImpl( INLINE )] public static Vector2 WeightedSum( Vector4 w, Vector2 a, Vector2 b, Vector2 c, Vector2 d ) => a * w.x + b * w.y + c * w.z + d * w.w;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
[MethodImpl( INLINE )] public static Vector3 WeightedSum( Vector3 w, Vector3 a, Vector3 b ) => a * w.x + b * w.y;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
/// <param name="c">The third value, weighted by <c>w.z</c></param>
[MethodImpl( INLINE )] public static Vector3 WeightedSum( Vector3 w, Vector3 a, Vector3 b, Vector3 c ) => a * w.x + b * w.y + c * w.z;
/// <summary>Multiplies each component of <c>w</c> by the input values, and returns their sum</summary>
/// <param name="w">The weights (per component) to apply to the rest of the values</param>
/// <param name="a">The first value, weighted by <c>w.x</c></param>
/// <param name="b">The second value, weighted by <c>w.y</c></param>
/// <param name="c">The third value, weighted by <c>w.z</c></param>
/// <param name="d">The fourth value, weighted by <c>w.w</c></param>