rj-action-library/Runtime/Math/MathX.cs

433 lines
10 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Godot;
using System.Text;
using System;
namespace Rokojori
{
public class MathX
{
public const float fps120Delta = 1/120f;
static Dictionary<int,int> factorialLookUp = new Dictionary<int, int>();
public static int Factorial( int i )
{
if ( factorialLookUp.ContainsKey( i ) )
{
return factorialLookUp[ i ];
}
var result = 1;
if ( i > 1 )
{
var before = Factorial( i - 1 );
result = before * i;
}
factorialLookUp[ i ] = result;
return factorialLookUp[ i ];
}
public static float Min( params float[] values )
{
var value = float.MaxValue;
for ( int i = 0; i < values.Length; i++ )
{
value = Mathf.Min( values[ i ], value );
}
return value;
}
public static float Max( params float[] values )
{
var value = - float.MaxValue;
for ( int i = 0; i < values.Length; i++ )
{
value = Mathf.Max( values[ i ], value );
}
return value;
}
public static float Sample( float tl, float tr, float bl, float br, Vector2 uv )
{
return Mathf.Lerp( tl, tr, uv.X ) +
( bl - tl ) * uv.Y * ( 1.0f - uv.X ) +
( br - tr ) * uv.X * uv.Y;
}
public static float Sample(
float ba, float bb, float bc, float bd,
float ta, float tb, float tc, float td,
Vector3 uvw
)
{
var bottom = Sample( ba, bb, bc, bd, new Vector2( uvw.X, uvw.Y ) );
var top = Sample( ta, tb, tc, td, new Vector2( uvw.X, uvw.Y ) );
return Mathf.Lerp( bottom, top, uvw.Z );
}
public static float Fract( float value )
{
return value - Mathf.Floor( value );
}
public static float TimeLerpAmountExp( float t, float time )
{
return Mathf.Exp( -t * time );
}
public static float TimeLerp( float lastValue, float nextValue, float t, float time )
{
return Mathf.Lerp( nextValue, lastValue, TimeLerpAmountExp( t, time ) );
}
public static float AbsoluteDeltaAngle( float degreesA, float degreesB )
{
return Mathf.Abs( AngleDelta( degreesA, degreesB ) );
}
public static float Smoothstep ( float edge0, float edge1, float x )
{
x = MathX.Clamp01( ( x - edge0 ) / ( edge1 - edge0 ) );
return x * x * ( 3.0f - 2.0f * x );
}
public static float SnapRounded( float value, float snappingDistance )
{
return Mathf.Round( value / snappingDistance ) * snappingDistance;
}
public static float SnapCeiled( float value, float snappingDistance )
{
return Mathf.Ceil( value / snappingDistance ) * snappingDistance;
}
public static float SnapFloored( float value, float snappingDistance )
{
return Mathf.Floor( value / snappingDistance ) * snappingDistance;
}
public static float AngleDelta( float degreesA, float degreesB)
{
var angleDelta = degreesB - degreesA;
angleDelta = (angleDelta + 180f) % 360f - 180f;
return angleDelta;
}
static float CustomModulo( float a, float n )
{
return a - Mathf.Floor( a / n ) * n;
}
public static float Clamp01( float value )
{
return Mathf.Clamp( value, 0, 1 );
}
public static float Normalize( float value, float min, float max )
{
return ( value - min ) / ( max - min );
}
public static float NormalizeClamped( float value, float min, float max )
{
return MathX.Clamp01( Normalize( value, min, max ) );
}
public static float Map( float value, float inputMin, float inputMax,
float outputMin, float outputMax )
{
var normalized = Normalize( value, inputMin, inputMax );
return normalized * ( outputMax - outputMin ) + outputMin;
}
public static float MapPositive( float value, float inputMax, float outputMax )
{
return Map( value, 0, inputMax, 0, outputMax );
}
public static float MapClamped( float value, float inputMin, float inputMax,
float outputMin, float outputMax )
{
var normalized = NormalizeClamped( value, inputMin, inputMax );
return normalized * ( outputMax - outputMin ) + outputMin;
}
public static float Map01( float value, float outputMin, float outputMax )
{
return value * ( outputMax - outputMin ) + outputMin;
}
public static float MapPolar( float value, float min, float max )
{
return Map01( value * 0.5f + 0.5f, min, max );
}
public static float MapPolarTo01( float value)
{
return value * 0.5f + 0.5f;
}
public static float Repeat( float value, float length )
{
while ( value > length )
{
value -=length;
}
while ( value < 0 )
{
value += length;
}
return value;
}
public static float Triangle( float value )
{
value = MathX.Repeat( value, 1 ) * 2;
if ( value > 1 )
{
value = 2 - value;
}
return value;
}
public static float EaseSine( float value )
{
return Mathf.Sin( Mathf.Pi * ( value * 0.5f + 1.5f ) ) + 1;
}
public static int Sign( int value )
{
return value == 0 ? 0 : value < 0 ? -1 : 1;
}
public static T LerpList<T>( List<T> data, float t, Func<T,T,float,T> lerpElementsFunction,
int dataFillSize = -1 )
{
dataFillSize = dataFillSize == -1 ? data.Count : dataFillSize;
var floatIndex = t * dataFillSize;
if ( floatIndex <= 0 )
{
return data[ 0 ];
}
if ( floatIndex >= ( dataFillSize - 1 ) )
{
return data[ dataFillSize - 1 ];
}
var flooredIndex = Mathf.FloorToInt( floatIndex );
var ceiledIndex = flooredIndex + 1;
var flooredValue = data[ flooredIndex ];
var ceiledValue = data[ ceiledIndex ];
var interpolationAmount = floatIndex - flooredIndex;
return lerpElementsFunction( flooredValue, ceiledValue, interpolationAmount );
}
public static float PolarTriangle( float value )
{
return Triangle( value ) * 2f - 1f;
}
public static float Step( float phase, float phaseStart, float phaseEnd )
{
if ( phase < phaseStart )
{
return 0;
}
if ( phase >= phaseEnd )
{
return 1;
}
return ( phase - phaseStart ) / ( phaseStart - phaseEnd );
}
public static int SafeIndex( int index, int maxElements, bool wrap = false )
{
if ( wrap )
{
return MathX.Repeat( index, maxElements );
}
else
{
return Mathf.Clamp( index, 0, maxElements - 1 );
}
}
public static int Repeat( int value, int range )
{
while ( value < 0 )
{
value += range;
}
while ( value >= range )
{
value -= range;
}
return value;
}
public static float PolarRepeat( float value, float range )
{
while ( value < -range )
{
value += range * 2;
}
while ( value >= range )
{
value -= range * 2;
}
return value;
}
public static Curve Curve( float y0, float y1 )
{
var curve = new Curve();
curve.AddPoint( new Vector2( 0, y0 ) );
curve.AddPoint( new Vector2( 1, y1 ) );
curve.SetPointRightMode( 0, Godot.Curve.TangentMode.Linear );
curve.SetPointLeftMode( 1, Godot.Curve.TangentMode.Linear );
return curve;
}
public static Curve Curve( float y )
{
return Curve( y, y );
}
public static float CurveAngle( Curve c, float t, float samplingRange = 0.01f )
{
var x0 = Mathf.Max( t - samplingRange, 0 );
var x1 = Mathf.Min( t + samplingRange, 1 );
var y0 = c.Sample( x0 );
var y1 = c.Sample( x1 );
return Mathf.Atan2( y1 - y0, x1 - x0 );
}
public static float CurveMaximum( Curve c, int numSamples = 20 )
{
var max = 0f;
for ( int i = 0; i < numSamples; i++ )
{
max = Mathf.Max( max, c.Sample( (float) i / ( numSamples - 1 ) ) );
}
return max;
}
public static List<float> GetCurveWeights( Curve curve, int num, bool normalize = true )
{
var sum = 0f;
var weights = new List<float>();
for ( int i = 0; i < num; i++ )
{
var t = (float)i / ( num - 1 );
var w = curve.Sample( t );
sum += w;
weights.Add( w );
}
if ( normalize )
{
for ( int i = 0; i < num; i++ )
{
weights[ i ] = weights[ i ] / sum;
}
}
return weights;
}
public static float Exponent( float base_, float power )
{
return Mathf.Log( power ) / Mathf.Log( base_ );
}
public static float Base( float exponent, float power )
{
return Mathf.Pow( power, 1f / exponent );
}
public static float SmoothingCoefficient( float ms, float reachingTarget = 0.1f, float frameDurationMS = MathX.fps120Delta )
{
return 1f - Base( ms / frameDurationMS, reachingTarget );
}
public static float SmoothValue( float oldValue, float newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = MathX.fps120Delta )
{
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
}
public static float SmoothDegrees( float oldValue, float newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = MathX.fps120Delta )
{
oldValue = Mathf.Wrap( oldValue, 0, 360 );
newValue = Mathf.Wrap( newValue, 0, 360 );
var difference = newValue - oldValue;
if ( Mathf.Abs( difference ) > 180 )
{
if ( newValue > oldValue )
{
oldValue += 360;
}
else
{
newValue += 360;
}
}
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
}
public static Vector3 SmoothVector3( Vector3 oldValue, Vector3 newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = 8.33333333333f )
{
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
}
public static float PolarAxis( bool negative, bool positive )
{
return ( negative ? -1 : 0 ) + ( positive ? 1 : 0 );
}
}
}