Code/Util/MathM.cs
using System;
using Sandbox;
namespace Meteor.VehicleTool;

public static class MathM
{
	public static float ExpDecay( float a, float b, float decay, float dt ) => b + (a - b) * MathF.Exp( -decay * dt );
	public static float AngleDifference( float a, float b ) => ((((b - a) % 360) + 540) % 360) - 180;
	public static float ExpDecayAngle( float a, float b, float decay, float dt ) => ExpDecay( a, a + AngleDifference( a, b ), decay, dt );

	/// <summary>
	///     Converts angular velocity (rad/s) to rotations per minute.
	/// </summary>
	public static float AngularVelocityToRPM( this float angularVelocity ) => angularVelocity * 9.5492965855137f;

	/// <summary>
	///     Converts rotations per minute to angular velocity (rad/s).
	/// </summary>
	public static float RPMToAngularVelocity( this float RPM ) => RPM * 0.10471975511966f;


	public static float Map( float x, float a, float b, float c, float d ) => (x - a) / (b - a) * (d - c) + c;

	public static float Fade( float n, float min, float mid, float max )
	{
		if ( n < min || n > max )
			return 0;

		if ( n > mid )
			min = mid - (max - mid);

		return MathF.Cos( (1 - ((n - min) / (mid - min))) * (MathF.PI / 2) );
	}

	public static Vector3 MeterToInch( this Vector3 v ) => new( v.x.MeterToInch(), v.y.MeterToInch(), v.z.MeterToInch() );
	public static Vector3 InchToMeter( this Vector3 v ) => new( v.x.InchToMeter(), v.y.InchToMeter(), v.z.InchToMeter() );
	public static Vector3 InchToMillimeter( this Vector3 v ) => new( v.x.InchToMillimeter(), v.y.InchToMillimeter(), v.z.InchToMillimeter() );
	public static Vector3 MillimeterToInch( this Vector3 v ) => new( v.x.MillimeterToInch(), v.y.MillimeterToInch(), v.z.MillimeterToInch() );

	public static float SignedAngle( this Vector3 from, Vector3 to, Vector3 axis )
	{
		float unsignedAngle = Vector3.GetAngle( from, to );

		float cross_x = from.y * to.z - from.z * to.y;
		float cross_y = from.z * to.x - from.x * to.z;
		float cross_z = from.x * to.y - from.y * to.x;
		float sign = MathF.Sign( axis.x * cross_x + axis.y * cross_y + axis.z * cross_z );
		return unsignedAngle * sign;
	}


	public struct LinearSmoothFloat( float inRate = 1, float outRate = 1, float inValue = 0 )
	{
		public float value = inValue;
		public float Get( float sample, float dt )
		{
			var dif = sample - value;
			if ( dif * value >= 0 )
			{
				value += dif * Math.Min( outRate * dt / Math.Abs( dif ), 1 );
			}
			else
				value += dif * Math.Min( inRate * dt / Math.Abs( dif ), 1 );

			return Math.Clamp( value, -1, 1 );
		}
	}
	public struct NonLinearSmoothFloat( float inRate = 1, float outRate = 1, float inValue = 0 )
	{
		public float value = inValue;
		public float Get( float sample, float dt )
		{
			var dif = sample - value;
			float ratedt;
			if ( dif * value >= 0 )
				ratedt = outRate * dt;
			else
				ratedt = inRate * dt;

			value += dif * ratedt / (1 + ratedt);
			return value;
		}
	}

	public struct SpringFloat( float spring = 10, float damp = 2, float inValue = 0 )
	{
		public float value = inValue;
		public float vel;
		public float Get( float sample, float dt )
		{
			vel = vel * Math.Max( 1 - damp * dt, 0 ) + (sample - value) * Math.Min( spring * dt, 1 / dt );
			value += vel * dt;

			return value;
		}
	}

}