mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-04 04:18:27 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
734 lines
25 KiB
C#
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 );
|
|
}
|
|
}
|