Files
sbox-public/engine/Sandbox.System/Math/Vector2.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

734 lines
25 KiB
C#

using Sandbox;
using Sandbox.Interpolation;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
/// <summary>
/// A 2-dimensional vector. Typically represents a position, size, or direction in 2D space.
/// </summary>
[JsonConverter( typeof( Sandbox.Internal.JsonConvert.Vector2Converter ) )]
[StructLayout( LayoutKind.Sequential )]
public partial struct Vector2 : System.IEquatable<Vector2>, IParsable<Vector2>, IInterpolator<Vector2>
{
internal System.Numerics.Vector2 _vec;
/// <summary>
/// X component of this vector.
/// </summary>
[ActionGraphInclude( AutoExpand = true )]
public float x
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
readonly get => _vec.X;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
set => _vec.X = value;
}
/// <summary>
/// Y component of this vector.
/// </summary>
[ActionGraphInclude( AutoExpand = true )]
public float y
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
readonly get => _vec.Y;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
set => _vec.Y = value;
}
/// <summary>
/// Initializes a 2D vector with given components.
/// </summary>
/// <param name="x">The X component.</param>
/// <param name="y">The Y component.</param>
[ActionGraphNode( "vec2.new" ), Title( "Vector2" ), Group( "Math/Geometry/Vector2" )]
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public Vector2( float x, float y ) : this( new System.Numerics.Vector2( x, y ) )
{
}
/// <summary>
/// Initializes a Vector2 from a given Vector2, i.e. creating a copy.
/// </summary>
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public Vector2( in Vector2 other ) : this( other.x, other.y )
{
}
/// <summary>
/// Initializes the 2D vector with all components set to the given value.
/// </summary>
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public Vector2( float all ) : this( all, all )
{
}
/// <summary>
/// Initializes the 2D vector with components from given 3D Vector, discarding the Z component.
/// </summary>
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public Vector2( Vector3 v ) : this( new System.Numerics.Vector2( (float)v.x, (float)v.y ) )
{
}
/// <summary>
/// Initializes the 2D vector with components from given 4D vector, discarding the Z and W components.
/// </summary>
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public Vector2( Vector4 v ) : this( new System.Numerics.Vector2( (float)v.x, (float)v.y ) )
{
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public Vector2( System.Numerics.Vector2 v )
{
_vec = v;
}
/// <summary>
/// Returns a 2D vector with every component set to 1
/// </summary>
public static Vector2 One { get; } = new Vector2( 1 );
/// <summary>
/// Returns a 2D vector with every component set to 0
/// </summary>
public static Vector2 Zero { get; } = new Vector2( 0 );
/// <summary>
/// Returns a 2D vector with Y set to -1. This typically represents up in 2D space.
/// </summary>
public static Vector2 Up { get; } = new Vector2( 0, -1 );
/// <summary>
/// Returns a 2D vector with Y set to 1. This typically represents down in 2D space.
/// </summary>
public static Vector2 Down { get; } = new Vector2( 0, 1 );
/// <summary>
/// Returns a 2D vector with X set to -1. This typically represents the left hand direction in 2D space.
/// </summary>
public static Vector2 Left { get; } = new Vector2( -1, 0 );
/// <summary>
/// Returns a 2D vector with X set to 1. This typically represents the right hand direction in 2D space.
/// </summary>
public static Vector2 Right { get; } = new Vector2( 1, 0 );
/// <summary>
/// Uniformly samples a 2D position from all points with distance at most 1 from the origin.
/// </summary>
[ActionGraphNode( "vec2.random" ), Title( "Random Vector2" ), Group( "Math/Geometry/Vector2" ), Icon( "casino" )]
public static Vector2 Random => SandboxSystem.Random.VectorInCircle();
/// <summary>
/// Returns a point on a circle at given rotation from X axis, counter clockwise.
/// </summary>
[ActionGraphNode( "vec2.fromrads" ), Pure, Title( "Vector2 From Radians" ), Group( "Math/Geometry/Vector2" ), Icon( "architecture" )]
public static Vector2 FromRadians( float radians ) => new Vector2( MathF.Sin( radians ), -MathF.Cos( radians ) );
/// <summary>
/// Returns a point on a circle at given rotation from X axis, counter clockwise.
/// </summary>
[ActionGraphNode( "vec2.fromdegs" ), Pure, Title( "Vector2 From Degrees" ), Group( "Math/Geometry/Vector2" ), Icon( "architecture" )]
public static Vector2 FromDegrees( float degrees ) => FromRadians( degrees.DegreeToRadian() );
/// <summary>
/// Return the same vector but with a length of one
/// </summary>
[JsonIgnore]
public readonly Vector2 Normal => IsNearZeroLength ? Vector2.Zero : System.Numerics.Vector2.Normalize( _vec );
/// <summary>
/// Returns the magnitude of the vector
/// </summary>
[JsonIgnore]
public readonly float Length => _vec.Length();
/// <summary>
/// This is faster than Length, so is better to use in certain circumstances
/// </summary>
[JsonIgnore]
public readonly float LengthSquared => _vec.LengthSquared();
/// <summary>
/// Returns the inverse of this vector, which is useful for scaling vectors
/// </summary>
[JsonIgnore]
public readonly Vector2 Inverse => new( 1.0f / x, 1.0f / y );
/// <summary>
/// Return the angle of this vector in degrees, always between 0 and 360
/// </summary>
[JsonIgnore]
public readonly float Degrees => System.MathF.Atan2( x, -y ).RadianToDegree().NormalizeDegrees();
/// <summary>
/// Returns a vector that runs perpendicular to this one
/// </summary>
[JsonIgnore]
public readonly Vector2 Perpendicular => new Vector2( -y, x );
/// <summary>
/// Returns true if x, y, or z are NaN
/// </summary>
[JsonIgnore]
public readonly bool IsNaN => float.IsNaN( x ) || float.IsNaN( y );
/// <summary>
/// Returns true if x, y, or z are infinity
/// </summary>
[JsonIgnore]
public readonly bool IsInfinity => float.IsInfinity( x ) || float.IsInfinity( y );
/// <summary>
/// Returns true if the squared length is less than 1e-8 (which is really near zero)
/// </summary>
[JsonIgnore]
public readonly bool IsNearZeroLength => LengthSquared <= 1e-8;
/// <summary>
/// Return this vector with given X.
/// </summary>
public readonly Vector2 WithX( float x ) => new Vector2( x, y );
/// <summary>
/// Return this vector with given Y.
/// </summary>
public readonly Vector2 WithY( float y ) => new Vector2( x, y );
/// <summary>
/// Returns true if value on every axis is less than tolerance away from zero
/// </summary>
public readonly bool IsNearlyZero( float tolerance = 0.0001f )
{
var abs = System.Numerics.Vector2.Abs( _vec );
return abs.X < tolerance && abs.Y < tolerance;
}
/// <summary>
/// Returns a vector whose length is limited to given maximum
/// </summary>
public readonly Vector2 ClampLength( float maxLength )
{
if ( LengthSquared <= 0 )
return Zero;
if ( LengthSquared < (maxLength * maxLength) )
return this;
return Normal * maxLength;
}
/// <summary>
/// Returns a vector whose length is limited between given minimum and maximum
/// </summary>
public readonly Vector2 ClampLength( float minLength, float maxLength )
{
float minSqr = minLength * minLength;
float maxSqr = maxLength * maxLength;
float lenSqr = LengthSquared;
if ( lenSqr <= 0.0f )
return Zero;
if ( lenSqr <= minSqr )
return Normal * minLength;
if ( lenSqr >= maxSqr )
return Normal * maxLength;
return this;
}
/// <summary>
/// Returns a vector each axis of which is clamped to between the 2 given vectors. Basically clamps a point to a square.
/// </summary>
/// <param name="otherMin">The mins vector. Values on each axis should be smaller than those of the maxs vector. See <see cref="Sort">Vector2.Sort</see>.</param>
/// <param name="otherMax">The maxs vector. Values on each axis should be larger than those of the mins vector. See <see cref="Sort">Vector2.Sort</see>.</param>
public readonly Vector2 Clamp( Vector2 otherMin, Vector2 otherMax )
{
return new Vector2( Math.Clamp( x, otherMin.x, otherMax.x ), Math.Clamp( y, otherMin.y, otherMax.y ) );
}
/// <summary>
/// Returns a vector each axis of which is clamped to given min and max values.
/// </summary>
/// <param name="min">Minimum value for each axis.</param>
/// <param name="max">Maximum value for each axis.</param>
public readonly Vector2 Clamp( float min, float max ) => Clamp( new Vector2( min ), new Vector2( max ) );
/// <summary>
/// Restricts a vector between a minimum and maximum value.
/// </summary>
/// <param name="value">The vector to restrict.</param>
/// <param name="min">The mins vector. Values on each axis should be smaller than those of the maxs vector. See <see cref="Sort">Vector2.Sort</see>.</param>
/// <param name="max">The maxs vector. Values on each axis should be larger than those of the mins vector. See <see cref="Sort">Vector2.Sort</see>.</param>
/// <returns></returns>
public static Vector2 Clamp( in Vector2 value, in Vector2 min, in Vector2 max ) => System.Numerics.Vector2.Clamp( value, min, max );
/// <summary>
/// Returns a vector that has the minimum values on each axis between this vector and given vector.
/// </summary>
public readonly Vector2 ComponentMin( Vector2 other )
{
return System.Numerics.Vector2.Min( _vec, other._vec );
}
/// <summary>
/// Returns a vector that has the minimum values on each axis between the 2 given vectors.
/// </summary>
public static Vector2 Min( Vector2 a, Vector2 b ) => a.ComponentMin( b );
/// <summary>
/// Returns a vector that has the maximum values on each axis between this vector and given vector.
/// </summary>
public readonly Vector2 ComponentMax( Vector2 other )
{
return System.Numerics.Vector2.Max( _vec, other._vec );
}
/// <summary>
/// Returns a vector that has the maximum values on each axis between the 2 given vectors.
/// </summary>
public static Vector2 Max( Vector2 a, Vector2 b ) => a.ComponentMax( b );
/// <summary>
/// Linearly interpolate from point a to point b.
/// </summary>
[ActionGraphNode( "geom.lerp" ), Pure, Group( "Math/Geometry" ), Icon( "timeline" )]
public static Vector2 Lerp( Vector2 a, Vector2 b, [Range( 0f, 1f )] float frac, bool clamp = true )
{
if ( clamp ) frac = frac.Clamp( 0.0f, 1.0f );
return System.Numerics.Vector2.Lerp( a._vec, b._vec, frac );
}
/// <summary>
/// Linearly interpolate from this vector to given vector.
/// </summary>
public readonly Vector2 LerpTo( Vector2 target, float t, bool clamp = true )
{
return Lerp( this, target, t, clamp );
}
/// <summary>
/// Linearly interpolate from point a to point b with separate fraction for each axis.
/// </summary>
public static Vector2 Lerp( Vector2 a, Vector2 b, Vector2 t, bool clamp = true )
{
if ( clamp ) t = t.Clamp( 0.0f, 1.0f );
return System.Numerics.Vector2.Lerp( a._vec, b._vec, t );
}
/// <summary>
/// Linearly interpolate from this vector to given vector with separate fraction for each axis.
/// </summary>
public readonly Vector2 LerpTo( Vector2 target, Vector2 t, bool clamp = true )
{
return Lerp( this, target, t, clamp );
}
/// <summary>
/// Remaps from one range to another.
/// </summary>
internal readonly Vector2 Remap( Rect oldRange, Rect newRange, bool clamp )
{
return new Vector2(
x.Remap( oldRange.Left, oldRange.Right, newRange.Left, newRange.Right, clamp ),
y.Remap( oldRange.Top, oldRange.Bottom, newRange.Top, newRange.Bottom, clamp ) );
}
/// <summary>
/// Returns the scalar/dot product between the 2 given vectors.
/// </summary>
[ActionGraphNode( "geom.dot" ), Pure, Group( "Math/Geometry" ), Icon( "fiber_manual_record" )]
public static float Dot( Vector2 a, Vector2 b )
{
return System.Numerics.Vector2.Dot( a._vec, b._vec );
}
/// <summary>
/// Returns the scalar/dot product between this and the given vector.
/// </summary>
public readonly float Dot( in Vector2 b ) => Dot( this, b );
/// <summary>
/// Returns distance between the 2 given vectors.
/// </summary>
public static float DistanceBetween( Vector2 a, Vector2 b ) => System.Numerics.Vector2.Distance( a._vec, b._vec );
/// <summary>
/// Returns distance between the 2 given vectors.
/// </summary>
[ActionGraphNode( "geom.dist" ), Pure, Title( "Distance" ), Group( "Math/Geometry" ), Icon( "straighten" )]
public static float Distance( in Vector2 a, in Vector2 b ) => System.Numerics.Vector2.Distance( a._vec, b._vec );
/// <summary>
/// Returns distance between this and given vectors.
/// </summary>
public readonly float Distance( Vector2 target ) => DistanceBetween( this, target );
/// <summary>
/// Returns squared distance between the 2 given vectors. This is faster than <see cref="DistanceBetween">DistanceBetween</see>,
/// and can be used for things like comparing distances, as long as only squared values are used.
/// </summary>
public static float DistanceBetweenSquared( Vector2 a, Vector2 b )
{
return (b - a).LengthSquared;
}
/// <summary>
/// Returns squared distance between the 2 given vectors. This is faster than <see cref="DistanceBetween">DistanceBetween</see>,
/// and can be used for things like comparing distances, as long as only squared values are used.
/// </summary>
public static float DistanceSquared( in Vector2 a, in Vector2 b ) => System.Numerics.Vector2.DistanceSquared( a._vec, b._vec );
/// <summary>
/// Returns squared distance between the 2 given vectors. This is faster than <see cref="Distance(Vector2)">Distance</see>,
/// and can be used for things like comparing distances, as long as only squared values are used.
/// </summary>
public readonly float DistanceSquared( Vector2 target ) => DistanceBetweenSquared( this, target );
/// <summary>
/// Calculates the normalized direction vector from one point to another in 2D space.
/// </summary>
public static Vector2 Direction( in Vector2 from, in Vector2 to )
{
return (to - from).Normal;
}
/// <summary>
/// Given a vector like 1,1,1 and direction 1,0,0, will return 0,1,1.
/// This is useful for velocity collision type events, where you want to
/// cancel out velocity based on a normal.
/// For this to work properly, direction should be a normal, but you can scale
/// how much you want to subtract by scaling the direction. Ie, passing in a direction
/// with a length of 0.5 will remove half the direction.
/// </summary>
public readonly Vector2 SubtractDirection( in Vector2 direction, float strength = 1.0f )
{
return this - (direction * Dot( direction ) * strength);
}
/// <summary>
/// Returns a new vector whos length is closer to given target length by given amount.
/// </summary>
public readonly Vector2 Approach( float length, float amount )
{
return Normal * Length.Approach( length, amount );
}
/// <summary>
/// Returns a new vector with all values positive. -5 becomes 5, etc.
/// </summary>
public readonly Vector2 Abs()
{
return System.Numerics.Vector2.Abs( _vec );
}
/// <summary>
/// Returns a new vector with all values positive. -5 becomes 5, etc.
/// </summary>
public static Vector2 Abs( in Vector2 value )
{
return System.Numerics.Vector2.Abs( value._vec );
}
/// <summary>
/// Returns a reflected vector based on incoming direction and plane normal. Like a ray reflecting off of a mirror.
/// </summary>
public static Vector2 Reflect( in Vector2 direction, in Vector2 normal )
{
return System.Numerics.Vector2.Reflect( direction._vec, normal._vec );
}
/// <summary>
/// Sort these two vectors into min and max. This doesn't just swap the vectors, it sorts each component.
/// So that min will come out containing the minimum x and y values.
/// </summary>
public static void Sort( ref Vector2 min, ref Vector2 max )
{
var a = new Vector2( Math.Min( min.x, max.x ), Math.Min( min.y, max.y ) );
var b = new Vector2( Math.Max( min.x, max.x ), Math.Max( min.y, max.y ) );
min = a;
max = b;
}
/// <summary>
/// Returns true if we're nearly equal to the passed vector.
/// </summary>
/// <param name="v">The value to compare with</param>
/// <param name="delta">The max difference between component values</param>
/// <returns>True if nearly equal</returns>
public readonly bool AlmostEqual( Vector2 v, float delta = 0.0001f )
{
if ( Math.Abs( x - v.x ) > delta ) return false;
if ( Math.Abs( y - v.y ) > delta ) return false;
return true;
}
/// <summary>
/// Calculates position of a point on a cubic bezier curve at given fraction.
/// </summary>
/// <param name="source">Point A of the curve.</param>
/// <param name="target">Point B of the curve.</param>
/// <param name="sourceTangent">Tangent for the Point A.</param>
/// <param name="targetTangent">Tangent for the Point B.</param>
/// <param name="t">How far along the path to get a point on. Range is 0 to 1, inclusive.</param>
/// <returns>The point on the curve</returns>
public static Vector2 CubicBezier( in Vector2 source, in Vector2 target, in Vector2 sourceTangent, in Vector2 targetTangent, float t )
{
t = t.Clamp( 0, 1 );
var invT = 1 - t;
return invT * invT * invT * source +
3 * invT * invT * t * sourceTangent +
3 * invT * t * t * targetTangent +
t * t * t * target;
}
/// <summary>
/// Snap to grid along all 3 axes.
/// </summary>
public readonly Vector2 SnapToGrid( float gridSize, bool sx = true, bool sy = true )
{
return new Vector2( sx ? x.SnapToGrid( gridSize ) : x, sy ? y.SnapToGrid( gridSize ) : y );
}
/// <summary>
/// Returns the distance between two direction vectors in degrees.
/// </summary>
public static float GetAngle( in Vector2 v1, in Vector2 v2 )
{
return MathF.Acos( Dot( v1.Normal, v2.Normal ).Clamp( -1, 1 ) ).RadianToDegree();
}
/// <summary>
/// Returns the distance between this vector and another in degrees.
/// </summary>
public readonly float Angle( in Vector2 other )
{
return GetAngle( this, other );
}
/// <summary>
/// Try to add to this vector. If we're already over max then don't add.
/// If we're over max when we add, clamp in that direction so we're not.
/// </summary>
public readonly Vector2 AddClamped( in Vector2 toAdd, float maxLength )
{
var dir = toAdd.Normal;
// Already over - just return self
var dot = Dot( dir );
if ( dot > maxLength ) return this;
// Add it
var vec = this + toAdd;
dot = vec.Dot( dir );
if ( dot < maxLength ) return vec;
// We're over, take off the rest
vec -= dir * (dot - maxLength);
return vec;
}
/// <summary>
/// Rotate this vector around given point by given angle in degrees and
/// return the result as a new vector.
/// </summary>
/// <param name="center"></param>
/// <param name="angleDegrees"></param>
/// <returns></returns>
public readonly Vector2 RotateAround( in Vector2 center, float angleDegrees )
{
var radians = angleDegrees.DegreeToRadian();
var cos = MathF.Cos( radians );
var sin = MathF.Sin( radians );
var dx = x - center.x;
var dy = y - center.y;
return new Vector2(
center.x + (dx * cos - dy * sin),
center.y + (dx * sin + dy * cos) );
}
#region operators
public float this[int index]
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
readonly get => _vec[index];
[MethodImpl( MethodImplOptions.AggressiveInlining )]
set => _vec[index] = value;
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator +( Vector2 c1, Vector2 c2 ) => c1._vec + c2._vec;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator -( Vector2 c1, Vector2 c2 ) => c1._vec - c2._vec;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator -( Vector2 c1 ) => System.Numerics.Vector2.Negate( c1._vec );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator *( Vector2 c1, float f ) => c1._vec * f;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator *( float f, Vector2 c1 ) => c1._vec * f;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator *( Vector2 c1, Vector2 c2 ) => c1._vec * c2._vec;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator /( Vector2 c1, Vector2 c2 ) => c1._vec / c2._vec;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static Vector2 operator /( Vector2 c1, float c2 ) => c1._vec / c2;
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static implicit operator Vector2( System.Numerics.Vector2 value ) => new Vector2( value );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static implicit operator System.Numerics.Vector2( Vector2 value ) => new System.Numerics.Vector2( value.x, value.y );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static implicit operator Vector2( double value ) => new Vector2( (float)value, (float)value );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static implicit operator Vector2( Vector3 value ) => new Vector2( value );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static implicit operator Vector2( Vector4 value ) => new Vector2( value );
#endregion
#region equality
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static bool operator ==( Vector2 left, Vector2 right ) => left.Equals( right );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public static bool operator !=( Vector2 left, Vector2 right ) => !(left == right);
public override readonly bool Equals( object obj ) => obj is Vector2 o && Equals( o );
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public readonly bool Equals( Vector2 o ) => (_vec) == (o._vec);
public readonly override int GetHashCode() => _vec.GetHashCode();
#endregion
/// <summary>
/// Formats the vector into a string "x,y"
/// </summary>
public override readonly string ToString()
{
var _x = x;
var _y = y;
// avoid -0
if ( _x.AlmostEqual( 0 ) ) _x = 0.0f;
if ( _y.AlmostEqual( 0 ) ) _y = 0.0f;
return $"{_x:0.###},{_y:0.###}";
}
/// <summary>
/// Given a string, try to convert this into a Vector2. Example formatting is "x,y", "[x,y]", "x y", etc.
/// </summary>
public static Vector2 Parse( string str )
{
if ( TryParse( str, CultureInfo.InvariantCulture, out var res ) )
return res;
return default;
}
/// <inheritdoc cref="Parse(string)" />
public static bool TryParse( string str, out Vector2 result )
{
return TryParse( str, CultureInfo.InvariantCulture, out result );
}
/// <inheritdoc cref="Parse(string)" />
public static Vector2 Parse( string str, IFormatProvider provider )
{
return Parse( str );
}
/// <inheritdoc cref="Parse(string)" />
public static bool TryParse( [NotNullWhen( true )] string str, IFormatProvider provider, [MaybeNullWhen( false )] out Vector2 result )
{
result = Vector2.Zero;
if ( string.IsNullOrWhiteSpace( str ) )
return false;
str = str.Trim( '[', ']', ' ', '\n', '\r', '\t', '"' );
var components = str.Split( new[] { ' ', ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries );
if ( components.Length != 2 )
return false;
if ( !float.TryParse( components[0], NumberStyles.Float, provider, out float x ) ||
!float.TryParse( components[1], NumberStyles.Float, provider, out float y ) )
{
return false;
}
result = new Vector2( x, y );
return true;
}
/// <summary>
/// Move to the target vector, by amount acceleration
/// </summary>
public readonly Vector2 WithAcceleration( Vector2 target, float accelerate )
{
if ( target.IsNearZeroLength )
return this;
Vector2 wishDir = target.Normal;
float wishSpeed = target.Length;
// See if we are changing direction a bit
var currentSpeed = Dot( wishDir );
// Reduce wishspeed by the amount of veer
var addSpeed = wishSpeed - currentSpeed;
// If not going to add any speed, done.
if ( addSpeed <= 0.0f )
return this;
// Determine amount of acceleration
var accelSpeed = accelerate * wishSpeed;
// Cap at addSpeed
if ( accelSpeed > addSpeed )
accelSpeed = addSpeed;
return this + wishDir * accelSpeed;
}
public readonly Vector2 WithFriction( float frictionAmount, float stopSpeed = 140.0f )
{
var speed = Length;
if ( speed < 0.01f ) return this;
// Bleed off some speed, but if we have less than the bleed
// threshold, bleed the threshold amount
float control = (speed < stopSpeed) ? stopSpeed : speed;
// Add the amount to the drop amount
var drop = control * frictionAmount;
// Scale the velocity
float newSpeed = speed - drop;
if ( newSpeed < 0 ) newSpeed = 0;
if ( newSpeed == speed ) return this;
newSpeed /= speed;
return this * newSpeed;
}
Vector2 IInterpolator<Vector2>.Interpolate( Vector2 a, Vector2 b, float delta )
{
return a.LerpTo( b, delta );
}
}