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 factorialLookUp = new Dictionary(); 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( List data, float t, Func 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 GetCurveWeights( Curve curve, int num, bool normalize = true ) { var sum = 0f; var weights = new List(); 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 ); } } }