Renames SS14.* to Robust.* (#793)

RobustToolbox projects should be named Robust.* 

This PR changes the RobustToolbox projects from SS14.* to Robust.*

Updates SS14.* prefixes/namespaces to Robust.*
Updates SpaceStation14.sln to RobustToolbox.sln
Updates MSBUILD/SS14.* to MSBUILD/Robust.*
Updates CSProject and MSBuild references for the above
Updates git_helper.py
Removes Runserver and Runclient as they are unusable
This commit is contained in:
Silver
2019-04-15 20:24:59 -06:00
committed by GitHub
parent 3aec6eb561
commit 25926a17b7
693 changed files with 3347 additions and 3346 deletions

View File

@@ -0,0 +1,228 @@
using System;
namespace Robust.Shared.Maths
{
/// <summary>
/// A representation of an angle, in radians.
/// </summary>
[Serializable]
public readonly struct Angle : IApproxEquatable<Angle>, IEquatable<Angle>
{
public static Angle Zero { get; } = new Angle();
public static Angle South { get; } = new Angle(-MathHelper.PiOver2);
/// <summary>
/// Angle in radians.
/// </summary>
public readonly double Theta;
/// <summary>
/// Angle in degrees.
/// </summary>
public double Degrees => MathHelper.RadiansToDegrees(Theta);
/// <summary>
/// Constructs an instance of an Angle.
/// </summary>
/// <param name="theta">The angle in radians.</param>
public Angle(double theta)
{
Theta = theta;
}
/// <summary>
/// Constructs an instance of an angle from an (un)normalized direction vector.
/// </summary>
/// <param name="dir"></param>
public Angle(Vector2 dir)
{
dir = dir.Normalized;
Theta = Math.Atan2(dir.Y, dir.X);
}
/// <summary>
/// Converts this angle to a unit direction vector.
/// </summary>
/// <returns>Unit Direction Vector</returns>
public Vector2 ToVec()
{
var x = Math.Cos(Theta);
var y = Math.Sin(Theta);
return new Vector2((float) x, (float) y);
}
private const double Segment = 2 * Math.PI / 8.0; // Cut the circle into 8 pieces
private const double Offset = Segment / 2.0; // offset the pieces by 1/2 their size
public Direction GetDir()
{
var ang = Theta % (2 * Math.PI);
if (ang < 0.0f) // convert -PI > PI to 0 > 2PI
ang += 2 * (float) Math.PI;
return (Direction) (Math.Floor((ang + Offset) / Segment) % 8);
}
private const double CardinalSegment = 2 * Math.PI / 4.0; // Cut the circle into 4 pieces
private const double CardinalOffset = CardinalSegment / 2.0; // offset the pieces by 1/2 their size
public Direction GetCardinalDir()
{
var ang = Theta % (2 * Math.PI);
if (ang < 0.0f) // convert -PI > PI to 0 > 2PI
ang += 2 * (float) Math.PI;
return (Direction) (Math.Floor((ang + CardinalOffset) / CardinalSegment) * 2 % 8);
}
public bool EqualsApprox(Angle other, double tolerance)
{
return EqualsApprox(this, other, tolerance);
}
public bool EqualsApprox(Angle other)
{
return EqualsApprox(this, other);
}
private static bool EqualsApprox(Angle a, Angle b)
{
// reduce both angles
var aReduced = Reduce(a.Theta);
var bReduced = Reduce(b.Theta);
var aPositive = FlipPositive(aReduced);
var bPositive = FlipPositive(bReduced);
// The second two expressions cover an edge case where one number is barely non-negative while the other number is negative.
// In this case, the negative number will get FlipPositived to ~2pi and the comparison will give a false negative.
return FloatMath.CloseTo(aPositive, bPositive)
|| FloatMath.CloseTo(aPositive + MathHelper.TwoPi, bPositive)
|| FloatMath.CloseTo(aPositive, bPositive + MathHelper.TwoPi);
}
private static bool EqualsApprox(Angle a, Angle b, double tolerance)
{
// reduce both angles
var aReduced = Reduce(a.Theta);
var bReduced = Reduce(b.Theta);
var aPositive = FlipPositive(aReduced);
var bPositive = FlipPositive(bReduced);
// The second two expressions cover an edge case where one number is barely non-negative while the other number is negative.
// In this case, the negative number will get FlipPositived to ~2pi and the comparison will give a false negative.
return FloatMath.CloseTo(aPositive, bPositive, tolerance)
|| FloatMath.CloseTo(aPositive + MathHelper.TwoPi, bPositive, tolerance)
|| FloatMath.CloseTo(aPositive, bPositive + MathHelper.TwoPi, tolerance);
}
/// <summary>
/// Removes revolutions from a positive or negative angle to make it as small as possible.
/// </summary>
private static double Reduce(double theta)
{
// int truncates value (round to 0)
var aTurns = (int) (theta / (2 * Math.PI));
return theta - aTurns * (2 * Math.PI);
}
/// <inheritdoc />
public bool Equals(Angle other)
{
return this.Theta.Equals(other.Theta);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Angle angle == Equals(Theta);
}
/// <inheritdoc />
public override int GetHashCode()
{
return Theta.GetHashCode();
}
public static bool operator ==(Angle a, Angle b)
{
return a.Equals(b);
}
public static bool operator !=(Angle a, Angle b)
{
return !(a == b);
}
/// <summary>
/// Calculates the congruent positive angle of a negative angle. Does nothing to a positive angle.
/// </summary>
private static double FlipPositive(double theta)
{
if (theta >= 0)
return theta;
return theta + 2 * Math.PI;
}
/// <summary>
/// Similar to Lerp but makes sure the angle wraps around to 360 degrees.
/// </summary>
public static Angle Lerp(in Angle a, in Angle b, float factor)
{
var degA = FloatMath.RadToDeg * Angle.Reduce(a);
var degB = FloatMath.RadToDeg * Angle.Reduce(b);
var delta = FloatMath.Repeat((float) (degB - degA), 360);
if (delta > 180)
delta -= 360;
return new Angle(FloatMath.DegToRad * (degA + delta * FloatMath.Clamp01(factor)));
}
/// <summary>
/// Constructs a new angle, from degrees instead of radians.
/// </summary>
/// <param name="degrees">The angle in degrees.</param>
public static Angle FromDegrees(double degrees)
{
return new Angle(MathHelper.DegreesToRadians(degrees));
}
/// <summary>
/// Implicit conversion from Angle to double.
/// </summary>
/// <param name="angle"></param>
public static implicit operator double(Angle angle)
{
return angle.Theta;
}
/// <summary>
/// Implicit conversion from double to Angle.
/// </summary>
/// <param name="theta"></param>
public static implicit operator Angle(double theta)
{
return new Angle(theta);
}
/// <summary>
/// Implicit conversion from float to Angle.
/// </summary>
/// <param name="theta"></param>
public static implicit operator Angle(float theta)
{
return new Angle(theta);
}
public override string ToString()
{
return $"{Theta} rad";
}
}
}

168
Robust.Shared.Maths/Box2.cs Normal file
View File

@@ -0,0 +1,168 @@
using System;
namespace Robust.Shared.Maths
{
/// <summary>
/// Axis Aligned rectangular box in world coordinates.
/// Uses a right-handed coordinate system. This means that X+ is to the right and Y+ up.
/// </summary>
[Serializable]
public readonly struct Box2 : IEquatable<Box2>
{
/// <summary>
/// The X coordinate of the left edge of the box.
/// </summary>
public readonly float Left;
/// <summary>
/// The X coordinate of the right edge of the box.
/// </summary>
public readonly float Right;
/// <summary>
/// The Y coordinate of the top edge of the box.
/// </summary>
public readonly float Top;
/// <summary>
/// The Y coordinate of the bottom of the box.
/// </summary>
public readonly float Bottom;
public Vector2 BottomRight => new Vector2(Right, Bottom);
public Vector2 TopLeft => new Vector2(Left, Top);
public Vector2 TopRight => new Vector2(Right, Top);
public Vector2 BottomLeft => new Vector2(Left, Bottom);
public float Width => Math.Abs(Right - Left);
public float Height => Math.Abs(Bottom - Top);
public Vector2 Size => new Vector2(Width, Height);
public Vector2 Center => BottomLeft + Size / 2;
public Box2(Vector2 bottomLeft, Vector2 topRight) : this(bottomLeft.X, bottomLeft.Y, topRight.X, topRight.Y)
{
}
public Box2(float left, float bottom, float right, float top)
{
Left = left;
Right = right;
Top = top;
Bottom = bottom;
}
public static Box2 FromDimensions(float left, float bottom, float width, float height)
{
return new Box2(left, bottom, left + width, bottom + height);
}
public static Box2 FromDimensions(Vector2 bottomLeft, Vector2 size)
{
return FromDimensions(bottomLeft.X, bottomLeft.Y, size.X, size.Y);
}
public bool Intersects(Box2 other)
{
return other.Bottom <= this.Top && other.Top >= this.Bottom && other.Right >= this.Left &&
other.Left <= this.Right;
}
public bool IsEmpty()
{
return FloatMath.CloseTo(Width, 0.0f) && FloatMath.CloseTo(Height, 0.0f);
}
public bool Encloses(Box2 inner)
{
return this.Left < inner.Left && this.Bottom < inner.Bottom && this.Right > inner.Right &&
this.Top > inner.Top;
}
public bool Contains(float x, float y)
{
return Contains(new Vector2(x, y));
}
public bool Contains(Vector2 point, bool closedRegion = true)
{
var xOk = closedRegion
? point.X >= Left ^ point.X > Right
: point.X > Left ^ point.X >= Right;
var yOk = closedRegion
? point.Y >= Bottom ^ point.Y > Top
: point.Y > Bottom ^ point.Y >= Top;
return xOk && yOk;
}
/// <summary>
/// Uniformly scales the box by a given scalar.
/// This scaling is done such that the center of the resulting box is the same as this box.
/// i.e. it scales around the center of the box, just changing width/height.
/// </summary>
/// <param name="scalar">Value to scale the box by.</param>
/// <returns>Scaled box.</returns>
public Box2 Scale(float scalar)
{
if (scalar < 0)
{
throw new ArgumentOutOfRangeException(nameof(scalar), scalar, "Scalar cannot be negative.");
}
var center = Center;
var halfSize = Size / 2 * scalar;
return new Box2(
center - halfSize,
center + halfSize);
}
/// <summary>Returns a Box2 translated by the given amount.</summary>
public Box2 Translated(Vector2 point)
{
return new Box2(Left + point.X, Bottom + point.Y, Right + point.X, Top + point.Y);
}
public bool Equals(Box2 other)
{
return Left.Equals(other.Left) && Right.Equals(other.Right) && Top.Equals(other.Top) &&
Bottom.Equals(other.Bottom);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
return obj is Box2 box2 && Equals(box2);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Left.GetHashCode();
hashCode = (hashCode * 397) ^ Right.GetHashCode();
hashCode = (hashCode * 397) ^ Top.GetHashCode();
hashCode = (hashCode * 397) ^ Bottom.GetHashCode();
return hashCode;
}
}
/// <summary>
/// Compares two objects for equality by value.
/// </summary>
public static bool operator ==(Box2 a, Box2 b)
{
return FloatMath.CloseTo(a.Bottom, b.Bottom) &&
FloatMath.CloseTo(a.Right, b.Right) &&
FloatMath.CloseTo(a.Top, b.Top) &&
FloatMath.CloseTo(a.Left, b.Left);
}
public static bool operator !=(Box2 a, Box2 b)
{
return !(a == b);
}
public override string ToString()
{
return $"({Left}, {Bottom}, {Right}, {Top})";
}
}
}

View File

@@ -0,0 +1,106 @@
using System;
namespace Robust.Shared.Maths
{
[Serializable]
public readonly struct Box2i : IEquatable<Box2i>
{
public readonly int Left;
public readonly int Right;
public readonly int Top;
public readonly int Bottom;
public Vector2i BottomRight => new Vector2i(Right, Bottom);
public Vector2i TopLeft => new Vector2i(Left, Top);
public Vector2i TopRight => new Vector2i(Right, Top);
public Vector2i BottomLeft => new Vector2i(Left, Bottom);
public int Width => Math.Abs(Right - Left);
public int Height => Math.Abs(Top - Bottom);
public Vector2i Size => new Vector2i(Width, Height);
public Box2i(Vector2i bottomLeft, Vector2i topRight) : this(bottomLeft.X, bottomLeft.Y, topRight.X, topRight.Y)
{
}
public Box2i(int left, int bottom, int right, int top)
{
Left = left;
Right = right;
Top = top;
Bottom = bottom;
}
public static Box2i FromDimensions(int left, int bottom, int width, int height)
{
return new Box2i(left, bottom, left + width, bottom + height);
}
public static Box2i FromDimensions(Vector2i position, Vector2i size)
{
return FromDimensions(position.X, position.Y, size.X, size.Y);
}
public bool Contains(int x, int y)
{
return Contains(new Vector2i(x, y));
}
public bool Contains(Vector2i point, bool closedRegion = true)
{
var xOk = closedRegion
? point.X >= Left ^ point.X > Right
: point.X > Left ^ point.X >= Right;
var yOk = closedRegion
? point.Y >= Bottom ^ point.Y > Top
: point.Y > Bottom ^ point.Y >= Top;
return xOk && yOk;
}
/// <summary>Returns a UIBox2 translated by the given amount.</summary>
public Box2i Translated(Vector2i point)
{
return new Box2i(Left + point.X, Bottom + point.Y, Right + point.X, Top + point.Y);
}
// override object.Equals
public override bool Equals(object obj)
{
if (obj is Box2i box)
{
return Equals(box);
}
return false;
}
public bool Equals(Box2i other)
{
return other.Left == Left && other.Right == Right && other.Bottom == Bottom && other.Top == Top;
}
// override object.GetHashCode
public override int GetHashCode()
{
var code = Left.GetHashCode();
code = (code * 929) ^ Right.GetHashCode();
code = (code * 929) ^ Top.GetHashCode();
code = (code * 929) ^ Bottom.GetHashCode();
return code;
}
public static explicit operator Box2i(Box2 box)
{
return new Box2i((int) box.Left, (int) box.Bottom, (int) box.Right, (int) box.Top);
}
public static implicit operator Box2(Box2i box)
{
return new Box2(box.Left, box.Bottom, box.Right, box.Top);
}
public override string ToString()
{
return $"({Left}, {Bottom}, {Right}, {Top})";
}
}
}

View File

@@ -0,0 +1,112 @@
using System;
namespace Robust.Shared.Maths
{
/// <summary>
/// Represents a circle with a 2D position and a radius.
/// </summary>
[Serializable]
public readonly struct Circle : IEquatable<Circle>
{
/// <summary>
/// Position of the circle in 2D space.
/// </summary>
public readonly Vector2 Position;
/// <summary>
/// Radius of the circle.
/// </summary>
public readonly float Radius;
/// <summary>
/// Constructs an instance of this struct.
/// </summary>
public Circle(Vector2 position, float radius)
{
Position = position;
Radius = radius;
}
/// <summary>
/// Checks if this circle contains a point.
/// </summary>
public bool Contains(Vector2 point)
{
return Contains(point.X, point.Y);
}
/// <summary>
/// Checks if this circle intersects with another circle.
/// </summary>
public bool Intersects(Circle circle)
{
var dx = Position.X - circle.Position.X;
var dy = Position.Y - circle.Position.Y;
var sumR = Radius + circle.Radius;
return dx * dx + dy * dy < sumR * sumR;
}
/// <summary>
/// Checks if this circle intersects with a box.
/// </summary>
public bool Intersects(Box2 box)
{
// Construct the point in / on the box nearest to the center of the circle.
float closestX = MathHelper.Median(box.Left, box.Right, Position.X);
float closestY = MathHelper.Median(box.Bottom, box.Top, Position.Y);
// Check if the circle contains that point.
return Contains(closestX, closestY);
}
private bool Contains(float x, float y)
{
var dx = Position.X - x;
var dy = Position.Y - y;
var d2 = dx * dx + dy * dy;
var r2 = Radius * Radius;
// Instead of d2 <= r2, use FloatMath.CloseTo to allow for some tolerance.
return (d2 < r2) || FloatMath.CloseTo(d2, r2);
}
/// <inheritdoc />
public bool Equals(Circle other)
{
return Position.Equals(other.Position) && Radius.Equals(other.Radius);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Circle circle && Equals(circle);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (Position.GetHashCode() * 397) ^ Radius.GetHashCode();
}
}
public static bool operator ==(Circle a, Circle b)
{
return a.Equals(b);
}
public static bool operator !=(Circle a, Circle b)
{
return !(a == b);
}
public override string ToString()
{
return $"Circle ({Position.X}, {Position.Y}), {Radius} r";
}
}
}

1743
Robust.Shared.Maths/Color.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
using System;
namespace Robust.Shared.Maths
{
public enum Direction
{
East = 0,
NorthEast = 1,
North = 2,
NorthWest = 3,
West = 4,
SouthWest = 5,
South = 6,
SouthEast = 7
}
/// <summary>
/// Extension methods for Direction enum.
/// </summary>
public static class DirectionExtensions
{
private const double Segment = 2 * Math.PI / 8.0; // Cut the circle into 8 pieces
private const double Offset = Segment / 2.0; // offset the pieces by 1/2 their size
/// <summary>
/// Converts a direction vector to the closest Direction enum.
/// </summary>
/// <param name="vec"></param>
/// <returns></returns>
public static Direction GetDir(this Vector2 vec)
{
return vec.ToAngle().GetDir();
}
/// <summary>
/// Converts a direction to an angle, where angle is -PI to +PI.
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
public static Angle ToAngle(this Direction dir)
{
var ang = Segment * (int) dir;
if (ang > Math.PI) // convert 0 > 2PI to -PI > +PI
ang -= 2 * Math.PI;
return ang;
}
/// <summary>
/// Converts a Direction to a normalized Direction vector.
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
public static Vector2 ToVec(this Direction dir)
{
return dir.ToAngle().ToVec();
}
/// <summary>
/// Converts a direction vector to an angle, where angle is -PI to +PI.
/// </summary>
/// <param name="vec">Vector to get the angle from.</param>
/// <returns>Angle of the vector.</returns>
public static Angle ToAngle(this Vector2 vec)
{
return Math.Atan2(vec.Y, vec.X);
}
}
}

View File

@@ -0,0 +1,148 @@
using System;
namespace Robust.Shared.Maths
{
public static class FloatMath
{
private const int LookupSize = 1024 * 64; //has to be power of 2
private static readonly float[] getSin, getCos;
public const float RadToDeg = (float) (180.0 / Math.PI);
public const float DegToRad = (float) (Math.PI / 180.0);
static FloatMath()
{
getSin = new float[LookupSize];
getCos = new float[LookupSize];
for (var i = 0; i < LookupSize; i++)
{
getSin[i] = (float) Math.Sin(i * Math.PI / LookupSize * 2);
getCos[i] = (float) Math.Cos(i * Math.PI / LookupSize * 2);
}
}
/// <summary>
/// Fast inaccurate sinus
/// </summary>
public static float Sin(float degrees)
{
var rot = GetIndex(degrees);
return getSin[rot];
}
/// <summary>
/// Fast inaccurate cosinus
/// </summary>
public static float Cos(float degrees)
{
var rot = GetIndex(degrees);
return getCos[rot];
}
public static int GetIndex(float degrees)
{
return (int) (degrees * (LookupSize / 360f) + 0.5f) & (LookupSize - 1);
}
public static void SinCos(float degrees, out float sin, out float cos)
{
var rot = GetIndex(degrees);
sin = getSin[rot];
cos = getCos[rot];
}
public static float Min(float a, float b)
{
return Math.Min(a, b);
}
public static float Max(float a, float b)
{
return Math.Max(a, b);
}
/// <summary>
/// Returns the largest integer smaller to or equal to f.
/// </summary>
public static float Floor(float f) => (float)Math.Floor(f);
public const float Pi = (float) Math.PI;
public static float ToDegrees(float radians)
{
return radians / Pi * 180;
}
public static float ToRadians(float degrees)
{
return degrees / 180 * Pi;
}
public static T Clamp<T>(this T val, T min, T max)
where T : IComparable<T>
{
if (val.CompareTo(min) < 0) return min;
if (val.CompareTo(max) > 0) return max;
return val;
}
public static bool CloseTo(float a, float b, double tolerance = .00001)
{
var epsilon = Math.Max(Math.Max(Math.Abs(a), Math.Abs(b)) * tolerance, tolerance); // .001% of the smaller value for the epsilon check as per MSDN reference suggestion
return Math.Abs(a - b) <= epsilon;
}
public static bool CloseTo(float a, double b, double tolerance = .00001)
{
var epsilon = Math.Max(Math.Max(Math.Abs(a), Math.Abs(b)) * tolerance, tolerance); // .001% of the smaller value for the epsilon check as per MSDN reference suggestion
return Math.Abs(a - b) <= epsilon;
}
public static bool CloseTo(double a, float b, double tolerance = .00001)
{
var epsilon = Math.Max(Math.Max(Math.Abs(a), Math.Abs(b)) * tolerance, tolerance); // .001% of the smaller value for the epsilon check as per MSDN reference suggestion
return Math.Abs(a - b) <= epsilon;
}
public static bool CloseTo(double a, double b, double tolerance = .00001)
{
var epsilon = Math.Max(Math.Max(Math.Abs(a), Math.Abs(b)) * tolerance, tolerance); // .001% of the smaller value for the epsilon check as per MSDN reference suggestion
return Math.Abs(a - b) <= epsilon;
}
/// <summary>
/// <c>blend</c> 0 means <c>a</c>
/// </summary>
public static double Lerp(double a, double b, double blend)
{
return a + (b - a) * blend;
}
/// <summary>
/// <c>blend</c> 0 means <c>a</c>
/// </summary>
public static float Lerp(float a, float b, float blend)
{
return a + (b - a) * blend;
}
// Clamps value between 0 and 1 and returns value
public static float Clamp01(float value)
{
if (value < 0F)
return 0F;
if (value > 1F)
return 1F;
return value;
}
// Loops the value t, so that it is never larger than length and never smaller than 0.
public static float Repeat(float t, float length)
{
return Clamp(t - Floor(t / length) * length, 0.0f, length);
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Robust.Shared.Maths
{
/// <summary>
/// Approximate equality checking, to handle floating point errors.
/// </summary>
public interface IApproxEquatable<in T>
{
bool EqualsApprox(T other);
bool EqualsApprox(T other, double tolerance);
}
}

View File

@@ -0,0 +1,294 @@
#region --- License ---
/* Licensed under the MIT/X11 license.
* Copyright (c) 2006-2008 the OpenTK Team.
* This notice may not be removed from any source distribution.
* See license.txt for licensing detailed licensing details.
*
* Contributions by Andy Gill, James Talton and Georg Wächter.
*/
#endregion --- License ---
using System;
using System.Diagnostics;
namespace Robust.Shared.Maths
{
/// <summary>
/// Contains common mathematical functions and constants.
/// </summary>
public static class MathHelper
{
#region Fields
/// <summary>
/// Defines the value of Pi as a <see cref="System.Single"/>.
/// </summary>
public const float Pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930382f;
/// <summary>
/// Defines the value of Pi divided by two as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver2 = Pi / 2;
/// <summary>
/// Defines the value of Pi divided by three as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver3 = Pi / 3;
/// <summary>
/// Definesthe value of Pi divided by four as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver4 = Pi / 4;
/// <summary>
/// Defines the value of Pi divided by six as a <see cref="System.Single"/>.
/// </summary>
public const float PiOver6 = Pi / 6;
/// <summary>
/// Defines the value of Pi multiplied by two as a <see cref="System.Single"/>.
/// </summary>
public const float TwoPi = 2 * Pi;
/// <summary>
/// Defines the value of Pi multiplied by 3 and divided by two as a <see cref="System.Single"/>.
/// </summary>
public const float ThreePiOver2 = 3 * Pi / 2;
/// <summary>
/// Defines the value of E as a <see cref="System.Single"/>.
/// </summary>
public const float E = 2.71828182845904523536f;
/// <summary>
/// Defines the base-10 logarithm of E.
/// </summary>
public const float Log10E = 0.434294482f;
/// <summary>
/// Defines the base-2 logarithm of E.
/// </summary>
public const float Log2E = 1.442695041f;
#endregion Fields
#region Public Members
#region NextPowerOfTwo
/// <summary>
/// Returns the next power of two that is larger than the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
public static long NextPowerOfTwo(long n)
{
if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), "Must be positive.");
return (long)NextPowerOfTwo((double) n);
}
/// <summary>
/// Returns the next power of two that is larger than the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
public static int NextPowerOfTwo(int n)
{
if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), "Must be positive.");
return (int)NextPowerOfTwo((double) n);
}
/// <summary>
/// Returns the next power of two that is larger than the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
public static float NextPowerOfTwo(float n)
{
if (float.IsNaN(n) || float.IsInfinity(n)) throw new ArgumentOutOfRangeException(nameof(n), "Must be a number.");
if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), "Must be positive.");
return (float)NextPowerOfTwo((double) n);
}
/// <summary>
/// Returns the next power of two that is larger than the specified number.
/// </summary>
/// <param name="n">The specified number.</param>
/// <returns>The next power of two.</returns>
public static double NextPowerOfTwo(double n)
{
if (double.IsNaN(n) || double.IsInfinity(n)) throw new ArgumentOutOfRangeException(nameof(n), "Must be a number.");
if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), "Must be positive.");
// Don't return negative powers of two, that's nonsense.
if (n < 1) return 1.0;
return Math.Pow(2, Math.Floor(Math.Log(n, 2)) + 1);
}
#endregion NextPowerOfTwo
#region Factorial
/// <summary>Calculates the factorial of a given natural number.
/// </summary>
/// <param name="n">The number.</param>
/// <returns>n!</returns>
public static long Factorial(int n)
{
long result = 1;
for (; n > 1; n--)
result *= n;
return result;
}
#endregion Factorial
#region BinomialCoefficient
/// <summary>
/// Calculates the binomial coefficient <paramref name="n"/> above <paramref name="k"/>.
/// </summary>
/// <param name="n">The n.</param>
/// <param name="k">The k.</param>
/// <returns>n! / (k! * (n - k)!)</returns>
public static long BinomialCoefficient(int n, int k)
{
return Factorial(n) / (Factorial(k) * Factorial(n - k));
}
#endregion BinomialCoefficient
#region DegreesToRadians
/// <summary>
/// Convert degrees to radians
/// </summary>
/// <param name="degrees">An angle in degrees</param>
/// <returns>The angle expressed in radians</returns>
public static float DegreesToRadians(float degrees)
{
const float degToRad = (float) Math.PI / 180.0f;
return degrees * degToRad;
}
/// <summary>
/// Convert radians to degrees
/// </summary>
/// <param name="radians">An angle in radians</param>
/// <returns>The angle expressed in degrees</returns>
public static float RadiansToDegrees(float radians)
{
const float radToDeg = 180.0f / (float) Math.PI;
return radians * radToDeg;
}
/// <summary>
/// Convert degrees to radians
/// </summary>
/// <param name="degrees">An angle in degrees</param>
/// <returns>The angle expressed in radians</returns>
public static double DegreesToRadians(double degrees)
{
const double degToRad = Math.PI / 180.0;
return degrees * degToRad;
}
/// <summary>
/// Convert radians to degrees
/// </summary>
/// <param name="radians">An angle in radians</param>
/// <returns>The angle expressed in degrees</returns>
public static double RadiansToDegrees(double radians)
{
const double radToDeg = 180.0 / Math.PI;
return radians * radToDeg;
}
#endregion DegreesToRadians
#region Swap
/// <summary>
/// Swaps two double values.
/// </summary>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
public static void Swap(ref double a, ref double b)
{
var temp = a;
a = b;
b = temp;
}
/// <summary>
/// Swaps two float values.
/// </summary>
/// <param name="a">The first value.</param>
/// <param name="b">The second value.</param>
public static void Swap(ref float a, ref float b)
{
var temp = a;
a = b;
b = temp;
}
#endregion Swap
#region MinMax
public static float Min(float a, float b, float c, float d)
{
return Math.Min(a, Math.Min(b, Math.Min(c, d)));
}
public static float Max(float a, float b, float c, float d)
{
return Math.Max(a, Math.Max(b, Math.Max(c, d)));
}
/// <summary>
/// Returns the median value out of a, b and c.
/// </summary>
/// <returns>THe median.</returns>
public static float Median(float a, float b, float c)
{
return Math.Max(Math.Min(a, b), Math.Min(Math.Max(a, b), c));
}
#endregion MinMax
/// <summary>
/// This method provides floored modulus.
/// C-like languages use truncated modulus for their '%' operator.
/// </summary>
/// <param name="n">The dividend.</param>
/// <param name="d">The divisor.</param>
/// <returns>The remainder.</returns>
[DebuggerStepThrough]
public static double Mod(double n, double d)
{
return n - Math.Floor(n / d) * d;
}
/// <summary>
/// This method provides floored modulus.
/// C-like languages use truncated modulus for their '%' operator.
/// </summary>
/// <param name="n">The dividend.</param>
/// <param name="d">The divisor.</param>
/// <returns>The remainder.</returns>
[DebuggerStepThrough]
public static int Mod(int n, int d)
{
var r = n % d;
return r < 0 ? r + d : r;
}
#endregion Public Members
}
}

View File

@@ -0,0 +1,950 @@
#region --- License ---
/*
Copyright (c) 2006 - 2008 The Open Toolkit library.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion --- License ---
using System;
using System.Runtime.InteropServices;
namespace Robust.Shared.Maths
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Matrix3 : IEquatable<Matrix3>, IApproxEquatable<Matrix3>
{
#region Fields & Access
/// <summary>Row 0, Column 0</summary>
public float R0C0;
/// <summary>Row 0, Column 1</summary>
public float R0C1;
/// <summary>Row 0, Column 2</summary>
public float R0C2;
/// <summary>Row 1, Column 0</summary>
public float R1C0;
/// <summary>Row 1, Column 1</summary>
public float R1C1;
/// <summary>Row 1, Column 2</summary>
public float R1C2;
/// <summary>Row 2, Column 0</summary>
public float R2C0;
/// <summary>Row 2, Column 1</summary>
public float R2C1;
/// <summary>Row 2, Column 2</summary>
public float R2C2;
/// <summary>Gets the component at the given row and column in the matrix.</summary>
/// <param name="row">The row of the matrix.</param>
/// <param name="column">The column of the matrix.</param>
/// <returns>The component at the given row and column in the matrix.</returns>
public float this[int row, int column]
{
get
{
switch (row)
{
case 0:
switch (column)
{
case 0: return R0C0;
case 1: return R0C1;
case 2: return R0C2;
}
break;
case 1:
switch (column)
{
case 0: return R1C0;
case 1: return R1C1;
case 2: return R1C2;
}
break;
case 2:
switch (column)
{
case 0: return R2C0;
case 1: return R2C1;
case 2: return R2C2;
}
break;
}
throw new IndexOutOfRangeException();
}
set
{
switch (row)
{
case 0:
switch (column)
{
case 0:
R0C0 = value;
return;
case 1:
R0C1 = value;
return;
case 2:
R0C2 = value;
return;
}
break;
case 1:
switch (column)
{
case 0:
R1C0 = value;
return;
case 1:
R1C1 = value;
return;
case 2:
R1C2 = value;
return;
}
break;
case 2:
switch (column)
{
case 0:
R2C0 = value;
return;
case 1:
R2C1 = value;
return;
case 2:
R2C2 = value;
return;
}
break;
}
throw new IndexOutOfRangeException();
}
}
/// <summary>Gets the component at the index into the matrix.</summary>
/// <param name="index">The index into the components of the matrix.</param>
/// <returns>The component at the given index into the matrix.</returns>
public float this[int index]
{
get
{
switch (index)
{
case 0: return R0C0;
case 1: return R0C1;
case 2: return R0C2;
case 3: return R1C0;
case 4: return R1C1;
case 5: return R1C2;
case 6: return R2C0;
case 7: return R2C1;
case 8: return R2C2;
default: throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
R0C0 = value;
return;
case 1:
R0C1 = value;
return;
case 2:
R0C2 = value;
return;
case 3:
R1C0 = value;
return;
case 4:
R1C1 = value;
return;
case 5:
R1C2 = value;
return;
case 6:
R2C0 = value;
return;
case 7:
R2C1 = value;
return;
case 8:
R2C2 = value;
return;
default: throw new IndexOutOfRangeException();
}
}
}
/// <summary>Converts the matrix into an array of floats.</summary>
/// <param name="matrix">The matrix to convert.</param>
/// <returns>An array of floats for the matrix.</returns>
public static explicit operator float[](Matrix3 matrix)
{
return new[]
{
matrix.R0C0,
matrix.R0C1,
matrix.R0C2,
matrix.R1C0,
matrix.R1C1,
matrix.R1C2,
matrix.R2C0,
matrix.R2C1,
matrix.R2C2
};
}
#endregion Fields & Access
#region Constructors
/// <summary>Constructs left matrix with the same components as the given matrix.</summary>
/// <param name="matrix">The matrix whose components to copy.</param>
public Matrix3(ref Matrix3 matrix)
{
R0C0 = matrix.R0C0;
R0C1 = matrix.R0C1;
R0C2 = matrix.R0C2;
R1C0 = matrix.R1C0;
R1C1 = matrix.R1C1;
R1C2 = matrix.R1C2;
R2C0 = matrix.R2C0;
R2C1 = matrix.R2C1;
R2C2 = matrix.R2C2;
}
/// <summary>Constructs left matrix with the given values.</summary>
/// <param name="r0c0">The value for row 0 column 0.</param>
/// <param name="r0c1">The value for row 0 column 1.</param>
/// <param name="r0c2">The value for row 0 column 2.</param>
/// <param name="r1c0">The value for row 1 column 0.</param>
/// <param name="r1c1">The value for row 1 column 1.</param>
/// <param name="r1c2">The value for row 1 column 2.</param>
/// <param name="r2c0">The value for row 2 column 0.</param>
/// <param name="r2c1">The value for row 2 column 1.</param>
/// <param name="r2c2">The value for row 2 column 2.</param>
public Matrix3
(
float r0c0,
float r0c1,
float r0c2,
float r1c0,
float r1c1,
float r1c2,
float r2c0,
float r2c1,
float r2c2
)
{
R0C0 = r0c0;
R0C1 = r0c1;
R0C2 = r0c2;
R1C0 = r1c0;
R1C1 = r1c1;
R1C2 = r1c2;
R2C0 = r2c0;
R2C1 = r2c1;
R2C2 = r2c2;
}
/// <summary>Constructs left matrix from the given array of float-precision floating-point numbers.</summary>
/// <param name="floatArray">The array of floats for the components of the matrix.</param>
public Matrix3(float[] floatArray)
{
if (floatArray == null || floatArray.GetLength(0) < 9) throw new MissingFieldException();
R0C0 = floatArray[0];
R0C1 = floatArray[1];
R0C2 = floatArray[2];
R1C0 = floatArray[3];
R1C1 = floatArray[4];
R1C2 = floatArray[5];
R2C0 = floatArray[6];
R2C1 = floatArray[7];
R2C2 = floatArray[8];
}
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="matrix">A Matrix4 to take the upper-left 3x3 from.</param>
public Matrix3(Matrix4 matrix)
{
R0C0 = matrix.Row0.X;
R0C1 = matrix.Row0.Y;
R0C2 = matrix.Row0.Z;
R1C0 = matrix.Row1.X;
R1C1 = matrix.Row1.Y;
R1C2 = matrix.Row1.Z;
R2C0 = matrix.Row2.X;
R2C1 = matrix.Row2.Y;
R2C2 = matrix.Row2.Z;
}
public static Matrix3 CreateTranslation(float x, float y)
{
var result = Identity;
result.R0C2 = x;
result.R1C2 = y;
return result;
}
public static Matrix3 CreateTranslation(Vector2 vector)
{
return CreateTranslation(vector.X, vector.Y);
}
public static Matrix3 CreateRotation(float angle)
{
return CreateRotation(new Angle(angle));
}
public static Matrix3 CreateRotation(Angle angle)
{
var cos = (float) Math.Cos(angle);
var sin = (float) Math.Sin(angle);
var result = Identity;
result.R0C0 = cos;
result.R1C0 = sin;
result.R0C1 = -sin;
result.R1C1 = cos;
return result;
}
#endregion Constructors
#region Equality
/// <summary>Indicates whether the current matrix is equal to another matrix.</summary>
/// <param name="matrix">The Matrix3 structure to compare with.</param>
/// <returns>true if the current matrix is equal to the matrix parameter; otherwise, false.</returns>
public bool Equals(Matrix3 other)
{
return
R0C0 == other.R0C0 &&
R0C1 == other.R0C1 &&
R0C2 == other.R0C2 &&
R1C0 == other.R1C0 &&
R1C1 == other.R1C1 &&
R1C2 == other.R1C2 &&
R2C0 == other.R2C0 &&
R2C1 == other.R2C1 &&
R2C2 == other.R2C2;
}
/// <summary>Indicates whether the current matrix is equal to another matrix.</summary>
/// <param name="matrix">The Matrix3 structure to compare to.</param>
/// <returns>true if the current matrix is equal to the matrix parameter; otherwise, false.</returns>
public bool Equals(ref Matrix3 matrix)
{
return
R0C0 == matrix.R0C0 &&
R0C1 == matrix.R0C1 &&
R0C2 == matrix.R0C2 &&
R1C0 == matrix.R1C0 &&
R1C1 == matrix.R1C1 &&
R1C2 == matrix.R1C2 &&
R2C0 == matrix.R2C0 &&
R2C1 == matrix.R2C1 &&
R2C2 == matrix.R2C2;
}
/// <summary>Indicates whether the current matrix is equal to another matrix.</summary>
/// <param name="left">The left-hand operand.</param>
/// <param name="right">The right-hand operand.</param>
/// <returns>true if the current matrix is equal to the matrix parameter; otherwise, false.</returns>
public static bool Equals(ref Matrix3 left, ref Matrix3 right)
{
return
left.R0C0 == right.R0C0 &&
left.R0C1 == right.R0C1 &&
left.R0C2 == right.R0C2 &&
left.R1C0 == right.R1C0 &&
left.R1C1 == right.R1C1 &&
left.R1C2 == right.R1C2 &&
left.R2C0 == right.R2C0 &&
left.R2C1 == right.R2C1 &&
left.R2C2 == right.R2C2;
}
public bool EqualsApprox(Matrix3 other)
{
return EqualsApprox(ref other, 1.0E-6f);
}
public bool EqualsApprox(Matrix3 other, double tolerance)
{
return EqualsApprox(ref other, (float) tolerance);
}
/// <summary>Indicates whether the current matrix is approximately equal to another matrix.</summary>
/// <param name="matrix">The Matrix3 structure to compare with.</param>
/// <param name="tolerance">The limit below which the matrices are considered equal.</param>
/// <returns>true if the current matrix is approximately equal to the matrix parameter; otherwise, false.</returns>
public bool EqualsApprox(ref Matrix3 matrix, float tolerance)
{
return
Math.Abs(R0C0 - matrix.R0C0) <= tolerance &&
Math.Abs(R0C1 - matrix.R0C1) <= tolerance &&
Math.Abs(R0C2 - matrix.R0C2) <= tolerance &&
Math.Abs(R1C0 - matrix.R1C0) <= tolerance &&
Math.Abs(R1C1 - matrix.R1C1) <= tolerance &&
Math.Abs(R1C2 - matrix.R1C2) <= tolerance &&
Math.Abs(R2C0 - matrix.R2C0) <= tolerance &&
Math.Abs(R2C1 - matrix.R2C1) <= tolerance &&
Math.Abs(R2C2 - matrix.R2C2) <= tolerance;
}
/// <summary>Indicates whether the current matrix is approximately equal to another matrix.</summary>
/// <param name="left">The left-hand operand.</param>
/// <param name="right">The right-hand operand.</param>
/// <param name="tolerance">The limit below which the matrices are considered equal.</param>
/// <returns>true if the current matrix is approximately equal to the matrix parameter; otherwise, false.</returns>
public static bool EqualsApprox(ref Matrix3 left, ref Matrix3 right, float tolerance)
{
return
Math.Abs(left.R0C0 - right.R0C0) <= tolerance &&
Math.Abs(left.R0C1 - right.R0C1) <= tolerance &&
Math.Abs(left.R0C2 - right.R0C2) <= tolerance &&
Math.Abs(left.R1C0 - right.R1C0) <= tolerance &&
Math.Abs(left.R1C1 - right.R1C1) <= tolerance &&
Math.Abs(left.R1C2 - right.R1C2) <= tolerance &&
Math.Abs(left.R2C0 - right.R2C0) <= tolerance &&
Math.Abs(left.R2C1 - right.R2C1) <= tolerance &&
Math.Abs(left.R2C2 - right.R2C2) <= tolerance;
}
#endregion Equality
#region Arithmetic Operators
/// <summary>Add left matrix to this matrix.</summary>
/// <param name="matrix">The matrix to add.</param>
public void Add(ref Matrix3 matrix)
{
R0C0 = R0C0 + matrix.R0C0;
R0C1 = R0C1 + matrix.R0C1;
R0C2 = R0C2 + matrix.R0C2;
R1C0 = R1C0 + matrix.R1C0;
R1C1 = R1C1 + matrix.R1C1;
R1C2 = R1C2 + matrix.R1C2;
R2C0 = R2C0 + matrix.R2C0;
R2C1 = R2C1 + matrix.R2C1;
R2C2 = R2C2 + matrix.R2C2;
}
/// <summary>Add left matrix to this matrix.</summary>
/// <param name="matrix">The matrix to add.</param>
/// <param name="result">The resulting matrix of the addition.</param>
public void Add(ref Matrix3 matrix, out Matrix3 result)
{
result.R0C0 = R0C0 + matrix.R0C0;
result.R0C1 = R0C1 + matrix.R0C1;
result.R0C2 = R0C2 + matrix.R0C2;
result.R1C0 = R1C0 + matrix.R1C0;
result.R1C1 = R1C1 + matrix.R1C1;
result.R1C2 = R1C2 + matrix.R1C2;
result.R2C0 = R2C0 + matrix.R2C0;
result.R2C1 = R2C1 + matrix.R2C1;
result.R2C2 = R2C2 + matrix.R2C2;
}
/// <summary>Add left matrix to left matrix.</summary>
/// <param name="matrix">The matrix on the matrix side of the equation.</param>
/// <param name="right">The matrix on the right side of the equation</param>
/// <param name="result">The resulting matrix of the addition.</param>
public static void Add(ref Matrix3 left, ref Matrix3 right, out Matrix3 result)
{
result.R0C0 = left.R0C0 + right.R0C0;
result.R0C1 = left.R0C1 + right.R0C1;
result.R0C2 = left.R0C2 + right.R0C2;
result.R1C0 = left.R1C0 + right.R1C0;
result.R1C1 = left.R1C1 + right.R1C1;
result.R1C2 = left.R1C2 + right.R1C2;
result.R2C0 = left.R2C0 + right.R2C0;
result.R2C1 = left.R2C1 + right.R2C1;
result.R2C2 = left.R2C2 + right.R2C2;
}
/// <summary>Subtract matrix from this matrix.</summary>
/// <param name="matrix">The matrix to subtract.</param>
public void Subtract(ref Matrix3 matrix)
{
R0C0 = R0C0 - matrix.R0C0;
R0C1 = R0C1 - matrix.R0C1;
R0C2 = R0C2 - matrix.R0C2;
R1C0 = R1C0 - matrix.R1C0;
R1C1 = R1C1 - matrix.R1C1;
R1C2 = R1C2 - matrix.R1C2;
R2C0 = R2C0 - matrix.R2C0;
R2C1 = R2C1 - matrix.R2C1;
R2C2 = R2C2 - matrix.R2C2;
}
/// <summary>Subtract matrix from this matrix.</summary>
/// <param name="matrix">The matrix to subtract.</param>
/// <param name="result">The resulting matrix of the subtraction.</param>
public void Subtract(ref Matrix3 matrix, out Matrix3 result)
{
result.R0C0 = R0C0 - matrix.R0C0;
result.R0C1 = R0C1 - matrix.R0C1;
result.R0C2 = R0C2 - matrix.R0C2;
result.R1C0 = R1C0 - matrix.R1C0;
result.R1C1 = R1C1 - matrix.R1C1;
result.R1C2 = R1C2 - matrix.R1C2;
result.R2C0 = R2C0 - matrix.R2C0;
result.R2C1 = R2C1 - matrix.R2C1;
result.R2C2 = R2C2 - matrix.R2C2;
}
/// <summary>Subtract right matrix from left matrix.</summary>
/// <param name="left">The matrix on the matrix side of the equation.</param>
/// <param name="right">The matrix on the right side of the equation</param>
/// <param name="result">The resulting matrix of the subtraction.</param>
public static void Subtract(ref Matrix3 left, ref Matrix3 right, out Matrix3 result)
{
result.R0C0 = left.R0C0 - right.R0C0;
result.R0C1 = left.R0C1 - right.R0C1;
result.R0C2 = left.R0C2 - right.R0C2;
result.R1C0 = left.R1C0 - right.R1C0;
result.R1C1 = left.R1C1 - right.R1C1;
result.R1C2 = left.R1C2 - right.R1C2;
result.R2C0 = left.R2C0 - right.R2C0;
result.R2C1 = left.R2C1 - right.R2C1;
result.R2C2 = left.R2C2 - right.R2C2;
}
/// <summary>Multiply left matrix times this matrix.</summary>
/// <param name="matrix">The matrix to multiply.</param>
public void Multiply(ref Matrix3 matrix)
{
var r0c0 = matrix.R0C0 * R0C0 + matrix.R0C1 * R1C0 + matrix.R0C2 * R2C0;
var r0c1 = matrix.R0C0 * R0C1 + matrix.R0C1 * R1C1 + matrix.R0C2 * R2C1;
var r0c2 = matrix.R0C0 * R0C2 + matrix.R0C1 * R1C2 + matrix.R0C2 * R2C2;
var r1c0 = matrix.R1C0 * R0C0 + matrix.R1C1 * R1C0 + matrix.R1C2 * R2C0;
var r1c1 = matrix.R1C0 * R0C1 + matrix.R1C1 * R1C1 + matrix.R1C2 * R2C1;
var r1c2 = matrix.R1C0 * R0C2 + matrix.R1C1 * R1C2 + matrix.R1C2 * R2C2;
R2C0 = matrix.R2C0 * R0C0 + matrix.R2C1 * R1C0 + matrix.R2C2 * R2C0;
R2C1 = matrix.R2C0 * R0C1 + matrix.R2C1 * R1C1 + matrix.R2C2 * R2C1;
R2C2 = matrix.R2C0 * R0C2 + matrix.R2C1 * R1C2 + matrix.R2C2 * R2C2;
R0C0 = r0c0;
R0C1 = r0c1;
R0C2 = r0c2;
R1C0 = r1c0;
R1C1 = r1c1;
R1C2 = r1c2;
}
/// <summary>Multiply matrix times this matrix.</summary>
/// <param name="matrix">The matrix to multiply.</param>
/// <param name="result">The resulting matrix of the multiplication.</param>
public void Multiply(ref Matrix3 matrix, out Matrix3 result)
{
result.R0C0 = matrix.R0C0 * R0C0 + matrix.R0C1 * R1C0 + matrix.R0C2 * R2C0;
result.R0C1 = matrix.R0C0 * R0C1 + matrix.R0C1 * R1C1 + matrix.R0C2 * R2C1;
result.R0C2 = matrix.R0C0 * R0C2 + matrix.R0C1 * R1C2 + matrix.R0C2 * R2C2;
result.R1C0 = matrix.R1C0 * R0C0 + matrix.R1C1 * R1C0 + matrix.R1C2 * R2C0;
result.R1C1 = matrix.R1C0 * R0C1 + matrix.R1C1 * R1C1 + matrix.R1C2 * R2C1;
result.R1C2 = matrix.R1C0 * R0C2 + matrix.R1C1 * R1C2 + matrix.R1C2 * R2C2;
result.R2C0 = matrix.R2C0 * R0C0 + matrix.R2C1 * R1C0 + matrix.R2C2 * R2C0;
result.R2C1 = matrix.R2C0 * R0C1 + matrix.R2C1 * R1C1 + matrix.R2C2 * R2C1;
result.R2C2 = matrix.R2C0 * R0C2 + matrix.R2C1 * R1C2 + matrix.R2C2 * R2C2;
}
/// <summary>Multiply left matrix times left matrix.</summary>
/// <param name="left">The matrix on the matrix side of the equation.</param>
/// <param name="right">The matrix on the right side of the equation</param>
/// <param name="result">The resulting matrix of the multiplication.</param>
public static void Multiply(ref Matrix3 left, ref Matrix3 right, out Matrix3 result)
{
result.R0C0 = right.R0C0 * left.R0C0 + right.R0C1 * left.R1C0 + right.R0C2 * left.R2C0;
result.R0C1 = right.R0C0 * left.R0C1 + right.R0C1 * left.R1C1 + right.R0C2 * left.R2C1;
result.R0C2 = right.R0C0 * left.R0C2 + right.R0C1 * left.R1C2 + right.R0C2 * left.R2C2;
result.R1C0 = right.R1C0 * left.R0C0 + right.R1C1 * left.R1C0 + right.R1C2 * left.R2C0;
result.R1C1 = right.R1C0 * left.R0C1 + right.R1C1 * left.R1C1 + right.R1C2 * left.R2C1;
result.R1C2 = right.R1C0 * left.R0C2 + right.R1C1 * left.R1C2 + right.R1C2 * left.R2C2;
result.R2C0 = right.R2C0 * left.R0C0 + right.R2C1 * left.R1C0 + right.R2C2 * left.R2C0;
result.R2C1 = right.R2C0 * left.R0C1 + right.R2C1 * left.R1C1 + right.R2C2 * left.R2C1;
result.R2C2 = right.R2C0 * left.R0C2 + right.R2C1 * left.R1C2 + right.R2C2 * left.R2C2;
}
/// <summary>Multiply matrix times this scalar.</summary>
/// <param name="scalar">The scalar to multiply.</param>
public void Multiply(float scalar)
{
R0C0 = scalar * R0C0;
R0C1 = scalar * R0C1;
R0C2 = scalar * R0C2;
R1C0 = scalar * R1C0;
R1C1 = scalar * R1C1;
R1C2 = scalar * R1C2;
R2C0 = scalar * R2C0;
R2C1 = scalar * R2C1;
R2C2 = scalar * R2C2;
}
/// <summary>Multiply matrix times this matrix.</summary>
/// <param name="scalar">The scalar to multiply.</param>
/// <param name="result">The resulting matrix of the multiplication.</param>
public void Multiply(float scalar, out Matrix3 result)
{
result.R0C0 = scalar * R0C0;
result.R0C1 = scalar * R0C1;
result.R0C2 = scalar * R0C2;
result.R1C0 = scalar * R1C0;
result.R1C1 = scalar * R1C1;
result.R1C2 = scalar * R1C2;
result.R2C0 = scalar * R2C0;
result.R2C1 = scalar * R2C1;
result.R2C2 = scalar * R2C2;
}
/// <summary>Multiply left matrix times left matrix.</summary>
/// <param name="matrix">The matrix on the matrix side of the equation.</param>
/// <param name="scalar">The scalar on the right side of the equation</param>
/// <param name="result">The resulting matrix of the multiplication.</param>
public static void Multiply(ref Matrix3 matrix, float scalar, out Matrix3 result)
{
result.R0C0 = scalar * matrix.R0C0;
result.R0C1 = scalar * matrix.R0C1;
result.R0C2 = scalar * matrix.R0C2;
result.R1C0 = scalar * matrix.R1C0;
result.R1C1 = scalar * matrix.R1C1;
result.R1C2 = scalar * matrix.R1C2;
result.R2C0 = scalar * matrix.R2C0;
result.R2C1 = scalar * matrix.R2C1;
result.R2C2 = scalar * matrix.R2C2;
}
#endregion Arithmetic Operators
#region Functions
public float Determinant => R0C0 * R1C1 * R2C2 - R0C0 * R1C2 * R2C1 - R0C1 * R1C0 * R2C2 + R0C2 * R1C0 * R2C1 + R0C1 * R1C2 * R2C0 - R0C2 * R1C1 * R2C0;
public void Transpose()
{
MathHelper.Swap(ref R0C1, ref R1C0);
MathHelper.Swap(ref R0C2, ref R2C0);
MathHelper.Swap(ref R1C2, ref R2C1);
}
public void Transpose(out Matrix3 result)
{
result.R0C0 = R0C0;
result.R0C1 = R1C0;
result.R0C2 = R2C0;
result.R1C0 = R0C1;
result.R1C1 = R1C1;
result.R1C2 = R2C1;
result.R2C0 = R0C2;
result.R2C1 = R1C2;
result.R2C2 = R2C2;
}
public static void Transpose(ref Matrix3 matrix, out Matrix3 result)
{
result.R0C0 = matrix.R0C0;
result.R0C1 = matrix.R1C0;
result.R0C2 = matrix.R2C0;
result.R1C0 = matrix.R0C1;
result.R1C1 = matrix.R1C1;
result.R1C2 = matrix.R2C1;
result.R2C0 = matrix.R0C2;
result.R2C1 = matrix.R1C2;
result.R2C2 = matrix.R2C2;
}
/// <summary>
/// Calculate the inverse of the given matrix
/// </summary>
/// <param name="mat">The matrix to invert</param>
/// <returns>The inverse of the given matrix if it has one, or the input if it is singular</returns>
/// <exception cref="InvalidOperationException">Thrown if the Matrix4 is singular.</exception>
public static Matrix3 Invert(Matrix3 mat)
{
var result = new Matrix3();
mat.Invert(ref result);
return result;
}
public void Invert(ref Matrix3 minv)
{
//Credit: https://stackoverflow.com/a/18504573
var d = Determinant;
if (FloatMath.CloseTo(d, 0))
throw new InvalidOperationException("Matrix is singular and cannot be inverted.");
var m = this;
// computes the inverse of a matrix m
double det = m.R0C0 * (m.R1C1 * m.R2C2 - m.R2C1 * m.R1C2) -
m.R0C1 * (m.R1C0 * m.R2C2 - m.R1C2 * m.R2C0) +
m.R0C2 * (m.R1C0 * m.R2C1 - m.R1C1 * m.R2C0);
var invdet = 1 / det;
minv.R0C0 = (float) ((m.R1C1 * m.R2C2 - m.R2C1 * m.R1C2) * invdet);
minv.R0C1 = (float) ((m.R0C2 * m.R2C1 - m.R0C1 * m.R2C2) * invdet);
minv.R0C2 = (float) ((m.R0C1 * m.R1C2 - m.R0C2 * m.R1C1) * invdet);
minv.R1C0 = (float) ((m.R1C2 * m.R2C0 - m.R1C0 * m.R2C2) * invdet);
minv.R1C1 = (float) ((m.R0C0 * m.R2C2 - m.R0C2 * m.R2C0) * invdet);
minv.R1C2 = (float) ((m.R1C0 * m.R0C2 - m.R0C0 * m.R1C2) * invdet);
minv.R2C0 = (float) ((m.R1C0 * m.R2C1 - m.R2C0 * m.R1C1) * invdet);
minv.R2C1 = (float) ((m.R2C0 * m.R0C1 - m.R0C0 * m.R2C1) * invdet);
minv.R2C2 = (float) ((m.R0C0 * m.R1C1 - m.R1C0 * m.R0C1) * invdet);
}
#endregion Functions
#region Transformation Functions
public void Transform(ref Vector3 vector)
{
var x = R0C0 * vector.X + R0C1 * vector.Y + R0C2 * vector.Z;
var y = R1C0 * vector.X + R1C1 * vector.Y + R1C2 * vector.Z;
vector.Z = R2C0 * vector.X + R2C1 * vector.Y + R2C2 * vector.Z;
vector.X = x;
vector.Y = y;
}
public static void Transform(in Matrix3 matrix, ref Vector3 vector)
{
var x = matrix.R0C0 * vector.X + matrix.R0C1 * vector.Y + matrix.R0C2 * vector.Z;
var y = matrix.R1C0 * vector.X + matrix.R1C1 * vector.Y + matrix.R1C2 * vector.Z;
vector.Z = matrix.R2C0 * vector.X + matrix.R2C1 * vector.Y + matrix.R2C2 * vector.Z;
vector.X = x;
vector.Y = y;
}
public void Transform(ref Vector2 vector)
{
Transform(this, ref vector);
}
public static void Transform(in Matrix3 matrix, ref Vector2 vector)
{
var vec3 = new Vector3(vector.X, vector.Y, 1);
Transform(matrix, ref vec3);
vector = vec3.Xy;
}
public Vector2 Transform(Vector2 vector)
{
Transform(ref vector);
return vector;
}
public static Vector2 Transform(in Matrix3 matrix, Vector2 vector)
{
Transform(matrix, ref vector);
return vector;
}
public void Transform(ref Vector3 vector, out Vector3 result)
{
result.X = R0C0 * vector.X + R0C1 * vector.Y + R0C2 * vector.Z;
result.Y = R1C0 * vector.X + R1C1 * vector.Y + R1C2 * vector.Z;
result.Z = R2C0 * vector.X + R2C1 * vector.Y + R2C2 * vector.Z;
}
public static void Transform(ref Matrix3 matrix, ref Vector3 vector, out Vector3 result)
{
result.X = matrix.R0C0 * vector.X + matrix.R0C1 * vector.Y + matrix.R0C2 * vector.Z;
result.Y = matrix.R1C0 * vector.X + matrix.R1C1 * vector.Y + matrix.R1C2 * vector.Z;
result.Z = matrix.R2C0 * vector.X + matrix.R2C1 * vector.Y + matrix.R2C2 * vector.Z;
}
public void Rotate(float angle)
{
var angleRadians = MathHelper.DegreesToRadians(angle);
var sin = (float) Math.Sin(angleRadians);
var cos = (float) Math.Cos(angleRadians);
var r0c0 = cos * R0C0 + sin * R1C0;
var r0c1 = cos * R0C1 + sin * R1C1;
var r0c2 = cos * R0C2 + sin * R1C2;
R1C0 = cos * R1C0 - sin * R0C0;
R1C1 = cos * R1C1 - sin * R0C1;
R1C2 = cos * R1C2 - sin * R0C2;
R0C0 = r0c0;
R0C1 = r0c1;
R0C2 = r0c2;
}
public void Rotate(Angle angle)
{
var sin = (float) Math.Sin(angle);
var cos = (float) Math.Cos(angle);
var r0c0 = cos * R0C0 + sin * R1C0;
var r0c1 = cos * R0C1 + sin * R1C1;
var r0c2 = cos * R0C2 + sin * R1C2;
R1C0 = cos * R1C0 - sin * R0C0;
R1C1 = cos * R1C1 - sin * R0C1;
R1C2 = cos * R1C2 - sin * R0C2;
R0C0 = r0c0;
R0C1 = r0c1;
R0C2 = r0c2;
}
public void Rotate(float angle, out Matrix3 result)
{
var angleRadians = MathHelper.DegreesToRadians(angle);
var sin = (float) Math.Sin(angleRadians);
var cos = (float) Math.Cos(angleRadians);
result.R0C0 = cos * R0C0 + sin * R1C0;
result.R0C1 = cos * R0C1 + sin * R1C1;
result.R0C2 = cos * R0C2 + sin * R1C2;
result.R1C0 = cos * R1C0 - sin * R0C0;
result.R1C1 = cos * R1C1 - sin * R0C1;
result.R1C2 = cos * R1C2 - sin * R0C2;
result.R2C0 = R2C0;
result.R2C1 = R2C1;
result.R2C2 = R2C2;
}
public static void Rotate(ref Matrix3 matrix, float angle, out Matrix3 result)
{
var angleRadians = MathHelper.DegreesToRadians(angle);
var sin = (float) Math.Sin(angleRadians);
var cos = (float) Math.Cos(angleRadians);
result.R0C0 = cos * matrix.R0C0 + sin * matrix.R1C0;
result.R0C1 = cos * matrix.R0C1 + sin * matrix.R1C1;
result.R0C2 = cos * matrix.R0C2 + sin * matrix.R1C2;
result.R1C0 = cos * matrix.R1C0 - sin * matrix.R0C0;
result.R1C1 = cos * matrix.R1C1 - sin * matrix.R0C1;
result.R1C2 = cos * matrix.R1C2 - sin * matrix.R0C2;
result.R2C0 = matrix.R2C0;
result.R2C1 = matrix.R2C1;
result.R2C2 = matrix.R2C2;
}
public static void RotateMatrix(float angle, out Matrix3 result)
{
var angleRadians = MathHelper.DegreesToRadians(angle);
var sin = (float) Math.Sin(angleRadians);
var cos = (float) Math.Cos(angleRadians);
result.R0C0 = cos;
result.R0C1 = sin;
result.R0C2 = 0;
result.R1C0 = -sin;
result.R1C1 = cos;
result.R1C2 = 0;
result.R2C0 = 0;
result.R2C1 = 0;
result.R2C2 = 1;
}
#endregion Transformation Functions
#region Constants
/// <summary>The identity matrix.</summary>
public static readonly Matrix3 Identity = new Matrix3
(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
/// <summary>A matrix of all zeros.</summary>
public static readonly Matrix3 Zero = new Matrix3
(
0, 0, 0,
0, 0, 0,
0, 0, 0
);
#endregion Constants
#region HashCode
/// <summary>Returns the hash code for this instance.</summary>
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
public override int GetHashCode()
{
return
R0C0.GetHashCode() ^ R0C1.GetHashCode() ^ R0C2.GetHashCode() ^
R1C0.GetHashCode() ^ R1C1.GetHashCode() ^ R1C2.GetHashCode() ^
R2C0.GetHashCode() ^ R2C1.GetHashCode() ^ R2C2.GetHashCode();
}
#endregion HashCode
#region String
/// <summary>Returns the fully qualified type name of this instance.</summary>
/// <returns>A System.String containing left fully qualified type name.</returns>
public override string ToString()
{
return $"|{R0C0}, {R0C1}, {R0C2}|\n"
+ $"|{R1C0}, {R1C1}, {R1C2}|\n"
+ $"|{R2C0}, {R2C1}, {R2C2}|\n";
}
#endregion String
}
#pragma warning restore 3019
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Robust.Shared.Math")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Robust.Shared.Math")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("93f23a82-00c5-4572-964e-e7c9457726d4")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,866 @@
#region --- License ---
/*
Copyright (c) 2006 - 2008 The Open Toolkit library.
Copyright 2013 Xamarin Inc
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion --- License ---
using System;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
namespace Robust.Shared.Maths
{
/// <summary>
/// Represents a Quaternion.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Quaternion : IEquatable<Quaternion>
{
#region Fields
private Vector3 xyz;
private float w;
#endregion Fields
#region Constructors
/// <summary>
/// Construct a new Quaternion from vector and w components
/// </summary>
/// <param name="v">The vector part</param>
/// <param name="w">The w part</param>
public Quaternion(Vector3 v, float w)
{
xyz = v;
this.w = w;
}
/// <summary>
/// Construct a new Quaternion
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
/// <param name="z">The z component</param>
/// <param name="w">The w component</param>
public Quaternion(float x, float y, float z, float w)
: this(new Vector3(x, y, z), w) { }
public Quaternion(ref Matrix3 matrix)
{
var scale = Math.Pow(matrix.Determinant, 1.0d / 3.0d);
float x, y, z;
w = (float) (Math.Sqrt(Math.Max(0, scale + matrix[0, 0] + matrix[1, 1] + matrix[2, 2])) / 2);
x = (float) (Math.Sqrt(Math.Max(0, scale + matrix[0, 0] - matrix[1, 1] - matrix[2, 2])) / 2);
y = (float) (Math.Sqrt(Math.Max(0, scale - matrix[0, 0] + matrix[1, 1] - matrix[2, 2])) / 2);
z = (float) (Math.Sqrt(Math.Max(0, scale - matrix[0, 0] - matrix[1, 1] + matrix[2, 2])) / 2);
xyz = new Vector3(x, y, z);
if (matrix[2, 1] - matrix[1, 2] < 0) X = -X;
if (matrix[0, 2] - matrix[2, 0] < 0) Y = -Y;
if (matrix[1, 0] - matrix[0, 1] < 0) Z = -Z;
}
#endregion Constructors
#region Public Members
#region Properties
/// <summary>
/// Gets or sets an OpenTK.Vector3 with the X, Y and Z components of this instance.
/// </summary>
public Vector3 Xyz
{
get => xyz;
set => xyz = value;
}
/// <summary>
/// Gets or sets the X component of this instance.
/// </summary>
[XmlIgnore]
public float X
{
get => xyz.X;
set => xyz.X = value;
}
/// <summary>
/// Gets or sets the Y component of this instance.
/// </summary>
[XmlIgnore]
public float Y
{
get => xyz.Y;
set => xyz.Y = value;
}
/// <summary>
/// Gets or sets the Z component of this instance.
/// </summary>
[XmlIgnore]
public float Z
{
get => xyz.Z;
set => xyz.Z = value;
}
/// <summary>
/// Gets or sets the W component of this instance.
/// </summary>
public float W
{
get => w;
set => w = value;
}
public float x
{
get => xyz.X;
set => xyz.X = value;
}
public float y
{
get => xyz.Y;
set => xyz.Y = value;
}
public float z
{
get => xyz.Z;
set => xyz.Z = value;
}
#endregion Properties
#region Instance
#region ToAxisAngle
/// <summary>
/// Convert the current quaternion to axis angle representation
/// </summary>
/// <param name="axis">The resultant axis</param>
/// <param name="angle">The resultant angle</param>
public void ToAxisAngle(out Vector3 axis, out float angle)
{
var result = ToAxisAngle();
axis = result.Xyz;
angle = result.W;
}
/// <summary>
/// Convert this instance to an axis-angle representation.
/// </summary>
/// <returns>A Vector4 that is the axis-angle representation of this quaternion.</returns>
public Vector4 ToAxisAngle()
{
var q = this;
if (Math.Abs(q.W) > 1.0f)
q.Normalize();
var result = new Vector4();
result.W = 2.0f * (float) Math.Acos(q.W); // angle
var den = (float) Math.Sqrt(1.0 - q.W * q.W);
if (den > 0.0001f)
{
result.Xyz = q.Xyz / den;
}
else
{
// This occurs when the angle is zero.
// Not a problem: just set an arbitrary normalized axis.
result.Xyz = Vector3.UnitX;
}
return result;
}
#endregion ToAxisAngle
#region public float Length
/// <summary>
/// Gets the length (magnitude) of the quaternion.
/// </summary>
/// <seealso cref="LengthSquared"/>
public float Length => (float) Math.Sqrt(W * W + Xyz.LengthSquared);
#endregion public float Length
#region public float LengthSquared
/// <summary>
/// Gets the square of the quaternion length (magnitude).
/// </summary>
public float LengthSquared => W * W + Xyz.LengthSquared;
#endregion public float LengthSquared
#region public void Normalize()
/// <summary>
/// Scales the Quaternion to unit length.
/// </summary>
public void Normalize()
{
var scale = 1.0f / Length;
Xyz *= scale;
W *= scale;
}
#endregion public void Normalize()
#region public void Conjugate()
/// <summary>
/// Convert this quaternion to its conjugate
/// </summary>
public void Conjugate()
{
Xyz = -Xyz;
}
#endregion public void Conjugate()
#endregion Instance
#region Static
#region Fields
private const float RadToDeg = (float) (180.0 / Math.PI);
private const float DegToRad = (float) (Math.PI / 180.0);
/// <summary>
/// Defines the identity quaternion.
/// </summary>
public static readonly Quaternion Identity = new Quaternion(0, 0, 0, 1);
#endregion Fields
#region Add
/// <summary>
/// Add two quaternions
/// </summary>
/// <param name="left">The first operand</param>
/// <param name="right">The second operand</param>
/// <returns>The result of the addition</returns>
public static Quaternion Add(Quaternion left, Quaternion right)
{
return new Quaternion(
left.Xyz + right.Xyz,
left.W + right.W);
}
/// <summary>
/// Add two quaternions
/// </summary>
/// <param name="left">The first operand</param>
/// <param name="right">The second operand</param>
/// <param name="result">The result of the addition</param>
public static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result)
{
result = new Quaternion(
left.Xyz + right.Xyz,
left.W + right.W);
}
#endregion Add
#region Sub
/// <summary>
/// Subtracts two instances.
/// </summary>
/// <param name="left">The left instance.</param>
/// <param name="right">The right instance.</param>
/// <returns>The result of the operation.</returns>
public static Quaternion Sub(Quaternion left, Quaternion right)
{
return new Quaternion(
left.Xyz - right.Xyz,
left.W - right.W);
}
/// <summary>
/// Subtracts two instances.
/// </summary>
/// <param name="left">The left instance.</param>
/// <param name="right">The right instance.</param>
/// <param name="result">The result of the operation.</param>
public static void Sub(ref Quaternion left, ref Quaternion right, out Quaternion result)
{
result = new Quaternion(
left.Xyz - right.Xyz,
left.W - right.W);
}
#endregion Sub
#region Mult
/// <summary>
/// Multiplies two instances.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>A new instance containing the result of the calculation.</returns>
public static Quaternion Multiply(Quaternion left, Quaternion right)
{
Multiply(ref left, ref right, out var result);
return result;
}
/// <summary>
/// Multiplies two instances.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <param name="result">A new instance containing the result of the calculation.</param>
public static void Multiply(ref Quaternion left, ref Quaternion right, out Quaternion result)
{
result = new Quaternion(
right.W * left.Xyz + left.W * right.Xyz + Vector3.Cross(left.Xyz, right.Xyz),
left.W * right.W - Vector3.Dot(left.Xyz, right.Xyz));
}
/// <summary>
/// Multiplies an instance by a scalar.
/// </summary>
/// <param name="quaternion">The instance.</param>
/// <param name="scale">The scalar.</param>
/// <param name="result">A new instance containing the result of the calculation.</param>
public static void Multiply(ref Quaternion quaternion, float scale, out Quaternion result)
{
result = new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
}
/// <summary>
/// Multiplies an instance by a scalar.
/// </summary>
/// <param name="quaternion">The instance.</param>
/// <param name="scale">The scalar.</param>
/// <returns>A new instance containing the result of the calculation.</returns>
public static Quaternion Multiply(Quaternion quaternion, float scale)
{
return new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
}
#endregion Mult
#region Dot
/// <summary>
/// Calculates the dot product between two Quaternions.
/// </summary>
public static float Dot(Quaternion a, Quaternion b)
{
return a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;
}
#endregion Dot
#region Conjugate
/// <summary>
/// Get the conjugate of the given quaternion
/// </summary>
/// <param name="q">The quaternion</param>
/// <returns>The conjugate of the given quaternion</returns>
public static Quaternion Conjugate(Quaternion q)
{
return new Quaternion(-q.Xyz, q.W);
}
/// <summary>
/// Get the conjugate of the given quaternion
/// </summary>
/// <param name="q">The quaternion</param>
/// <param name="result">The conjugate of the given quaternion</param>
public static void Conjugate(ref Quaternion q, out Quaternion result)
{
result = new Quaternion(-q.Xyz, q.W);
}
#endregion Conjugate
#region Invert
/// <summary>
/// Get the inverse of the given quaternion
/// </summary>
/// <param name="q">The quaternion to invert</param>
/// <returns>The inverse of the given quaternion</returns>
public static Quaternion Invert(Quaternion q)
{
Invert(ref q, out var result);
return result;
}
/// <summary>
/// Get the inverse of the given quaternion
/// </summary>
/// <param name="q">The quaternion to invert</param>
/// <param name="result">The inverse of the given quaternion</param>
public static void Invert(ref Quaternion q, out Quaternion result)
{
var lengthSq = q.LengthSquared;
if (lengthSq != 0.0)
{
var i = 1.0f / lengthSq;
result = new Quaternion(q.Xyz * -i, q.W * i);
}
else
{
result = q;
}
}
#endregion Invert
#region Normalize
/// <summary>
/// Scale the given quaternion to unit length
/// </summary>
/// <param name="q">The quaternion to normalize</param>
/// <returns>The normalized quaternion</returns>
public static Quaternion Normalize(Quaternion q)
{
Normalize(ref q, out var result);
return result;
}
/// <summary>
/// Scale the given quaternion to unit length
/// </summary>
/// <param name="q">The quaternion to normalize</param>
/// <param name="result">The normalized quaternion</param>
public static void Normalize(ref Quaternion q, out Quaternion result)
{
var scale = 1.0f / q.Length;
result = new Quaternion(q.Xyz * scale, q.W * scale);
}
#endregion Normalize
#region FromAxisAngle
/// <summary>
/// Build a quaternion from the given axis and angle
/// </summary>
/// <param name="axis">The axis to rotate about</param>
/// <param name="angle">The rotation angle in radians</param>
/// <returns></returns>
public static Quaternion FromAxisAngle(Vector3 axis, float angle)
{
if (axis.LengthSquared == 0.0f)
return Identity;
var result = Identity;
angle *= 0.5f;
axis.Normalize();
result.Xyz = axis * (float) Math.Sin(angle);
result.W = (float) Math.Cos(angle);
return Normalize(result);
}
#endregion FromAxisAngle
#region Slerp
/// <summary>
/// Do Spherical linear interpolation between two quaternions
/// </summary>
/// <param name="q1">The first quaternion</param>
/// <param name="q2">The second quaternion</param>
/// <param name="blend">The blend factor</param>
/// <returns>A smooth blend between the given quaternions</returns>
public static Quaternion Slerp(Quaternion q1, Quaternion q2, float blend)
{
// if either input is zero, return the other.
if (q1.LengthSquared == 0.0f)
{
if (q2.LengthSquared == 0.0f)
{
return Identity;
}
return q2;
}
if (q2.LengthSquared == 0.0f)
{
return q1;
}
var cosHalfAngle = q1.W * q2.W + Vector3.Dot(q1.Xyz, q2.Xyz);
if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f)
{
// angle = 0.0f, so just return one input.
return q1;
}
if (cosHalfAngle < 0.0f)
{
q2.Xyz = -q2.Xyz;
q2.W = -q2.W;
cosHalfAngle = -cosHalfAngle;
}
float blendA;
float blendB;
if (cosHalfAngle < 0.99f)
{
// do proper slerp for big angles
var halfAngle = (float) Math.Acos(cosHalfAngle);
var sinHalfAngle = (float) Math.Sin(halfAngle);
var oneOverSinHalfAngle = 1.0f / sinHalfAngle;
blendA = (float) Math.Sin(halfAngle * (1.0f - blend)) * oneOverSinHalfAngle;
blendB = (float) Math.Sin(halfAngle * blend) * oneOverSinHalfAngle;
}
else
{
// do lerp if angle is really small.
blendA = 1.0f - blend;
blendB = blend;
}
var result = new Quaternion(blendA * q1.Xyz + blendB * q2.Xyz, blendA * q1.W + blendB * q2.W);
if (result.LengthSquared > 0.0f)
return Normalize(result);
return Identity;
}
#endregion Slerp
#region RotateTowards
public static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta)
{
var num = Angle(from, to);
if (num == 0f)
{
return to;
}
var t = Math.Min(1f, maxDegreesDelta / num);
return Slerp(from, to, t);
}
#endregion RotateTowards
#region Angle
public static float Angle(Quaternion a, Quaternion b)
{
var f = Dot(a, b);
return (float) (Math.Acos(Math.Min(Math.Abs(f), 1f)) * 2f * RadToDeg);
}
#endregion Angle
#region LookRotation
// from http://answers.unity3d.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html
public static Quaternion LookRotation(ref Vector3 forward, ref Vector3 up)
{
forward = Vector3.Normalize(forward);
var right = Vector3.Normalize(Vector3.Cross(up, forward));
up = Vector3.Cross(forward, right);
var m00 = right.X;
var m01 = right.Y;
var m02 = right.Z;
var m10 = up.X;
var m11 = up.Y;
var m12 = up.Z;
var m20 = forward.X;
var m21 = forward.Y;
var m22 = forward.Z;
var num8 = m00 + m11 + m22;
var quaternion = new Quaternion();
if (num8 > 0f)
{
var num = (float) Math.Sqrt(num8 + 1f);
quaternion.w = num * 0.5f;
num = 0.5f / num;
quaternion.X = (m12 - m21) * num;
quaternion.Y = (m20 - m02) * num;
quaternion.Z = (m01 - m10) * num;
return quaternion;
}
if (m00 >= m11 && m00 >= m22)
{
var num7 = (float) Math.Sqrt(1f + m00 - m11 - m22);
var num4 = 0.5f / num7;
quaternion.X = 0.5f * num7;
quaternion.Y = (m01 + m10) * num4;
quaternion.Z = (m02 + m20) * num4;
quaternion.W = (m12 - m21) * num4;
return quaternion;
}
if (m11 > m22)
{
var num6 = (float) Math.Sqrt(1f + m11 - m00 - m22);
var num3 = 0.5f / num6;
quaternion.X = (m10 + m01) * num3;
quaternion.Y = 0.5f * num6;
quaternion.Z = (m21 + m12) * num3;
quaternion.W = (m20 - m02) * num3;
return quaternion;
}
var num5 = (float) Math.Sqrt(1f + m22 - m00 - m11);
var num2 = 0.5f / num5;
quaternion.X = (m20 + m02) * num2;
quaternion.Y = (m21 + m12) * num2;
quaternion.Z = 0.5f * num5;
quaternion.W = (m01 - m10) * num2;
return quaternion;
}
#endregion LookRotation
#region Euler Angles
// from http://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
public static Vector3 ToEulerRad(Quaternion rotation)
{
var sqw = rotation.w * rotation.w;
var sqx = rotation.x * rotation.x;
var sqy = rotation.y * rotation.y;
var sqz = rotation.z * rotation.z;
var unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
var test = rotation.x * rotation.w - rotation.y * rotation.z;
Vector3 v;
if (test > 0.4995f * unit)
{
// singularity at north pole
v.Y = (float) (2f * Math.Atan2(rotation.y, rotation.x));
v.X = (float) (Math.PI / 2);
v.Z = 0;
return NormalizeAngles(v * RadToDeg);
}
if (test < -0.4995f * unit)
{
// singularity at south pole
v.Y = (float) (-2f * Math.Atan2(rotation.y, rotation.x));
v.X = (float) (-Math.PI / 2);
v.Z = 0;
return NormalizeAngles(v * RadToDeg);
}
var q = new Quaternion(rotation.w, rotation.z, rotation.x, rotation.y);
v.Y = (float) Math.Atan2(2f * q.x * q.w + 2f * q.y * q.z, 1 - 2f * (q.z * q.z + q.w * q.w)); // Yaw
v.X = (float) Math.Asin(2f * (q.x * q.z - q.w * q.y)); // Pitch
v.Z = (float) Math.Atan2(2f * q.x * q.y + 2f * q.z * q.w, 1 - 2f * (q.y * q.y + q.z * q.z)); // Roll
return NormalizeAngles(v * RadToDeg);
}
private static Vector3 NormalizeAngles(Vector3 angles)
{
angles.X = NormalizeAngle(angles.X);
angles.Y = NormalizeAngle(angles.Y);
angles.Z = NormalizeAngle(angles.Z);
return angles;
}
private static float NormalizeAngle(float angle)
{
while (angle > 360)
angle -= 360;
while (angle < 0)
angle += 360;
return angle;
}
#endregion Euler Angles
#endregion Static
#region Operators
/// <summary>
/// Adds two instances.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>The result of the calculation.</returns>
public static Quaternion operator +(Quaternion left, Quaternion right)
{
left.Xyz += right.Xyz;
left.W += right.W;
return left;
}
/// <summary>
/// Subtracts two instances.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>The result of the calculation.</returns>
public static Quaternion operator -(Quaternion left, Quaternion right)
{
left.Xyz -= right.Xyz;
left.W -= right.W;
return left;
}
/// <summary>
/// Multiplies two instances.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>The result of the calculation.</returns>
public static Quaternion operator *(Quaternion left, Quaternion right)
{
Multiply(ref left, ref right, out left);
return left;
}
/// <summary>
/// Multiplies an instance by a scalar.
/// </summary>
/// <param name="quaternion">The instance.</param>
/// <param name="scale">The scalar.</param>
/// <returns>A new instance containing the result of the calculation.</returns>
public static Quaternion operator *(Quaternion quaternion, float scale)
{
Multiply(ref quaternion, scale, out quaternion);
return quaternion;
}
/// <summary>
/// Multiplies an instance by a scalar.
/// </summary>
/// <param name="quaternion">The instance.</param>
/// <param name="scale">The scalar.</param>
/// <returns>A new instance containing the result of the calculation.</returns>
public static Quaternion operator *(float scale, Quaternion quaternion)
{
return new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
}
/// <summary>
/// Compares two instances for equality.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>True, if left equals right; false otherwise.</returns>
public static bool operator ==(Quaternion left, Quaternion right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two instances for inequality.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>True, if left does not equal right; false otherwise.</returns>
public static bool operator !=(Quaternion left, Quaternion right)
{
return !left.Equals(right);
}
#endregion Operators
#region Overrides
#region public override string ToString()
/// <summary>
/// Returns a System.String that represents the current Quaternion.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"V: {Xyz}, W: {W}";
}
#endregion public override string ToString()
#region public override bool Equals (object o)
/// <summary>
/// Compares this object instance to another object for equality.
/// </summary>
/// <param name="other">The other object to be used in the comparison.</param>
/// <returns>True if both objects are Quaternions of equal value. Otherwise it returns false.</returns>
public override bool Equals(object obj)
{
if (obj is Quaternion == false) return false;
return this == (Quaternion) obj;
}
#endregion public override bool Equals (object o)
#region public override int GetHashCode ()
/// <summary>
/// Provides the hash code for this object.
/// </summary>
/// <returns>A hash code formed from the bitwise XOR of this objects members.</returns>
public override int GetHashCode()
{
return Xyz.GetHashCode() ^ W.GetHashCode();
}
#endregion public override int GetHashCode ()
#endregion Overrides
#endregion Public Members
#region IEquatable<Quaternion> Members
/// <summary>
/// Compares this Quaternion instance to another Quaternion for equality.
/// </summary>
/// <param name="other">The other Quaternion to be used in the comparison.</param>
/// <returns>True if both instances are equal; false otherwise.</returns>
public bool Equals(Quaternion other)
{
return Xyz == other.Xyz && W == other.W;
}
#endregion IEquatable<Quaternion> Members
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
namespace Robust.Shared.Maths
{
public static class RandomExtensions
{
/// <summary>
/// Generate a random number from a normal (gaussian) distribution.
/// </summary>
/// <param name="random">The random object to generate the number from.</param>
/// <param name="μ">The average or "center" of the normal distribution.</param>
/// <param name="σ">The standard deviation of the normal distribution.</param>
public static double NextGaussian(this Random random, double μ = 0, double σ = 1)
{
// https://stackoverflow.com/a/218600
var α = random.NextDouble();
var β = random.NextDouble();
var randStdNormal = Math.Sqrt(-2.0 * Math.Log(α)) * Math.Sin(2.0 * Math.PI * β);
return μ + σ * randStdNormal;
}
public static T Pick<T>(this Random random, IReadOnlyList<T> list)
{
var index = random.Next(list.Count);
return list[index];
}
public static float NextFloat(this Random random)
{
return (float)random.NextDouble();
}
}
}

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\MSBuild\Robust.Properties.targets" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{93F23A82-00C5-4572-964E-E7C9457726D4}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Robust.Shared.Maths</RootNamespace>
<AssemblyName>Robust.Shared.Maths</AssemblyName>
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<LangVersion>7.2</LangVersion>
<CodeAnalysisRuleSet Condition="'$(ActualOS)' == 'Windows'">MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType Condition="'$(AppVeyor)' != 'yes'">portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\bin\Shared.Maths\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\bin\Shared.Maths\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\bin\Shared.Maths\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType Condition="'$(AppVeyor)' != 'yes'">portable</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>..\bin\Shared.Maths\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<DebugType Condition="'$(AppVeyor)' != 'yes'">portable</DebugType>
<OutputPath>..\bin\Shared.Maths\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>..\bin\Shared.Maths\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2018.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Angle.cs" />
<Compile Include="Box2.cs" />
<Compile Include="Box2i.cs" />
<Compile Include="RandomExtensions.cs" />
<Compile Include="UIBox2.cs" />
<Compile Include="UIBox2i.cs" />
<Compile Include="Circle.cs" />
<Compile Include="Color.cs" />
<Compile Include="Direction.cs" />
<Compile Include="FloatMath.cs" />
<Compile Include="IApproxEquatable.cs" />
<Compile Include="MathHelper.cs" />
<Compile Include="Matrix3.cs" />
<Compile Include="Matrix4.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Quaternion.cs" />
<Compile Include="Vector2.cs" />
<Compile Include="Vector2d.cs" />
<Compile Include="Vector2i.cs" />
<Compile Include="Vector2u.cs" />
<Compile Include="Vector3.cs" />
<Compile Include="Vector3d.cs" />
<Compile Include="Vector4.cs" />
<Compile Include="Vector4d.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,168 @@
using System;
namespace Robust.Shared.Maths
{
/// <summary>
/// Axis Aligned rectangular box in screen coordinates.
/// Uses a left-handed coordinate system. This means that X+ is to the right and Y+ down.
/// </summary>
[Serializable]
public readonly struct UIBox2 : IEquatable<UIBox2>
{
/// <summary>
/// The X coordinate of the left edge of the box.
/// </summary>
public readonly float Left;
/// <summary>
/// The X coordinate of the right edge of the box.
/// </summary>
public readonly float Right;
/// <summary>
/// The Y coordinate of the top edge of the box.
/// </summary>
public readonly float Top;
/// <summary>
/// The Y coordinate of the bottom of the box.
/// </summary>
public readonly float Bottom;
public Vector2 BottomRight => new Vector2(Right, Bottom);
public Vector2 TopLeft => new Vector2(Left, Top);
public Vector2 TopRight => new Vector2(Right, Top);
public Vector2 BottomLeft => new Vector2(Left, Bottom);
public float Width => Math.Abs(Right - Left);
public float Height => Math.Abs(Top - Bottom);
public Vector2 Size => new Vector2(Width, Height);
public Vector2 Center => TopLeft + Size / 2;
public UIBox2(Vector2 leftTop, Vector2 rightBottom) : this(leftTop.X, leftTop.Y, rightBottom.X, rightBottom.Y)
{
}
public UIBox2(float left, float top, float right, float bottom)
{
Left = left;
Right = right;
Top = top;
Bottom = bottom;
}
public static UIBox2 FromDimensions(float left, float top, float width, float height)
{
return new UIBox2(left, top, left + width, top + height);
}
public static UIBox2 FromDimensions(Vector2 leftTopPosition, Vector2 size)
{
return FromDimensions(leftTopPosition.X, leftTopPosition.Y, size.X, size.Y);
}
public bool Intersects(UIBox2 other)
{
return other.Bottom >= this.Top && other.Top <= this.Bottom && other.Right >= this.Left &&
other.Left <= this.Right;
}
public bool IsEmpty()
{
return FloatMath.CloseTo(Width, 0.0f) && FloatMath.CloseTo(Height, 0.0f);
}
public bool Encloses(UIBox2 inner)
{
return this.Left < inner.Left && this.Bottom > inner.Bottom && this.Right > inner.Right &&
this.Top < inner.Top;
}
public bool Contains(float x, float y)
{
return Contains(new Vector2(x, y));
}
public bool Contains(Vector2 point, bool closedRegion = true)
{
var xOk = closedRegion
? point.X >= Left ^ point.X > Right
: point.X > Left ^ point.X >= Right;
var yOk = closedRegion
? point.Y >= Top ^ point.Y > Bottom
: point.Y > Top ^ point.Y >= Bottom;
return xOk && yOk;
}
/// <summary>
/// Uniformly scales the box by a given scalar.
/// This scaling is done such that the center of the resulting box is the same as this box.
/// i.e. it scales around the center of the box, just changing width/height.
/// </summary>
/// <param name="scalar">Value to scale the box by.</param>
/// <returns>Scaled box.</returns>
public UIBox2 Scale(float scalar)
{
if (scalar < 0)
{
throw new ArgumentOutOfRangeException(nameof(scalar), scalar, "Scalar cannot be negative.");
}
var center = Center;
var halfSize = Size / 2 * scalar;
return new UIBox2(
center - halfSize,
center + halfSize);
}
/// <summary>Returns a UIBox2 translated by the given amount.</summary>
public UIBox2 Translated(Vector2 point)
{
return new UIBox2(Left + point.X, Top + point.Y, Right + point.X, Bottom + point.Y);
}
public bool Equals(UIBox2 other)
{
return Left.Equals(other.Left) && Right.Equals(other.Right) && Top.Equals(other.Top) &&
Bottom.Equals(other.Bottom);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
return obj is UIBox2 box2 && Equals(box2);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Left.GetHashCode();
hashCode = (hashCode * 397) ^ Right.GetHashCode();
hashCode = (hashCode * 397) ^ Top.GetHashCode();
hashCode = (hashCode * 397) ^ Bottom.GetHashCode();
return hashCode;
}
}
/// <summary>
/// Compares two objects for equality by value.
/// </summary>
public static bool operator ==(UIBox2 a, UIBox2 b)
{
return FloatMath.CloseTo(a.Bottom, b.Bottom) &&
FloatMath.CloseTo(a.Right, b.Right) &&
FloatMath.CloseTo(a.Top, b.Top) &&
FloatMath.CloseTo(a.Left, b.Left);
}
public static bool operator !=(UIBox2 a, UIBox2 b)
{
return !(a == b);
}
public override string ToString()
{
return $"({Left}, {Top}, {Right}, {Bottom})";
}
}
}

View File

@@ -0,0 +1,131 @@
using System;
namespace Robust.Shared.Maths
{
[Serializable]
public readonly struct UIBox2i : IEquatable<UIBox2i>
{
public readonly int Left;
public readonly int Right;
public readonly int Top;
public readonly int Bottom;
public Vector2i BottomRight => new Vector2i(Right, Bottom);
public Vector2i TopLeft => new Vector2i(Left, Top);
public Vector2i TopRight => new Vector2i(Right, Top);
public Vector2i BottomLeft => new Vector2i(Left, Bottom);
public int Width => Math.Abs(Right - Left);
public int Height => Math.Abs(Top - Bottom);
public Vector2i Size => new Vector2i(Width, Height);
public UIBox2i(Vector2i topLeft, Vector2i bottomRight) : this(topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y)
{
}
public UIBox2i(int left, int top, int right, int bottom)
{
Left = left;
Right = right;
Top = top;
Bottom = bottom;
}
public static UIBox2i FromDimensions(int left, int top, int width, int height)
{
return new UIBox2i(left, top, left + width, top + height);
}
public static UIBox2i FromDimensions(Vector2i position, Vector2i size)
{
return FromDimensions(position.X, position.Y, size.X, size.Y);
}
public bool Contains(int x, int y)
{
return Contains(new Vector2i(x, y));
}
public bool Contains(Vector2i point, bool closedRegion = true)
{
var xOk = closedRegion
? point.X >= Left ^ point.X > Right
: point.X > Left ^ point.X >= Right;
var yOk = closedRegion
? point.Y >= Top ^ point.Y > Bottom
: point.Y > Top ^ point.Y >= Bottom;
return xOk && yOk;
}
/// <summary>Returns a UIBox2 translated by the given amount.</summary>
public UIBox2i Translated(Vector2i point)
{
return new UIBox2i(Left + point.X, Top + point.Y, Right + point.X, Bottom + point.Y);
}
/// <summary>
/// Calculates the "intersection" of this and another box.
/// Basically, the smallest region that fits in both boxes.
/// </summary>
/// <param name="other">The box to calculate the intersection with.</param>
/// <returns>
/// <c>null</c> if there is no intersection, otherwise the smallest region that fits in both boxes.
/// </returns>
public UIBox2i? Intersection(in UIBox2i other)
{
if (!Intersects(other))
{
return null;
}
return new UIBox2i(
Vector2i.ComponentMax(TopLeft, other.TopLeft),
Vector2i.ComponentMin(BottomRight, other.BottomRight));
}
public bool Intersects(in UIBox2i other)
{
return other.Bottom >= this.Top && other.Top <= this.Bottom && other.Right >= this.Left &&
other.Left <= this.Right;
}
// override object.Equals
public override bool Equals(object obj)
{
if (obj is UIBox2i box)
{
return Equals(box);
}
return false;
}
public bool Equals(UIBox2i other)
{
return other.Left == Left && other.Right == Right && other.Bottom == Bottom && other.Top == Top;
}
// override object.GetHashCode
public override int GetHashCode()
{
var code = Left.GetHashCode();
code = (code * 929) ^ Right.GetHashCode();
code = (code * 929) ^ Top.GetHashCode();
code = (code * 929) ^ Bottom.GetHashCode();
return code;
}
public static explicit operator UIBox2i(UIBox2 box)
{
return new UIBox2i((int) box.Left, (int) box.Top, (int) box.Right, (int) box.Bottom);
}
public static implicit operator UIBox2(UIBox2i box)
{
return new UIBox2(box.Left, box.Top, box.Right, box.Bottom);
}
public override string ToString()
{
return $"({Left}, {Top}, {Right}, {Bottom})";
}
}
}

View File

@@ -0,0 +1,321 @@
using System;
using System.Runtime.InteropServices;
namespace Robust.Shared.Maths
{
/// <summary>
/// Represents a float vector with two components (x, y).
/// </summary>
[StructLayout(LayoutKind.Sequential)]
[Serializable]
public readonly struct Vector2 : IEquatable<Vector2>, IApproxEquatable<Vector2>
{
/// <summary>
/// The X component of the vector.
/// </summary>
public readonly float X;
/// <summary>
/// The Y component of the vector.
/// </summary>
public readonly float Y;
/// <summary>
/// A zero length vector.
/// </summary>
public static readonly Vector2 Zero = new Vector2(0, 0);
/// <summary>
/// A vector with all components set to 1.
/// </summary>
public static readonly Vector2 One = new Vector2(1, 1);
/// <summary>
/// A unit vector pointing in the +X direction.
/// </summary>
public static readonly Vector2 UnitX = new Vector2(1, 0);
/// <summary>
/// A unit vector pointing in the +Y direction.
/// </summary>
public static readonly Vector2 UnitY = new Vector2(0, 1);
/// <summary>
/// Construct a vector from its coordinates.
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y coordinate</param>
public Vector2(float x, float y)
{
X = x;
Y = y;
}
/// <summary>
/// Gets the length (magnitude) of the vector.
/// </summary>
public float Length => (float) Math.Sqrt(LengthSquared);
/// <summary>
/// Gets the squared length of the vector.
/// </summary>
public float LengthSquared => X * X + Y * Y;
/// <summary>
/// Returns a new, normalized, vector.
/// </summary>
/// <returns></returns>
public Vector2 Normalized
{
get
{
var length = Length;
return new Vector2(X / length, Y / length);
}
}
public Vector2 Rounded()
{
return new Vector2((float) Math.Round(X), (float) Math.Round(Y));
}
/// <summary>
/// Subtracts a vector from another, returning a new vector.
/// </summary>
/// <param name="a">Vector to subtract from.</param>
/// <param name="b">Vector to subtract with.</param>
public static Vector2 operator -(Vector2 a, Vector2 b)
{
return new Vector2(a.X - b.X, a.Y - b.Y);
}
/// <summary>
/// Subtracts a scalar with each component of a vector, returning a new vecotr..
/// </summary>
/// <param name="a">Vector to subtract from.</param>
/// <param name="b">Scalar to subtract with.</param>
public static Vector2 operator -(Vector2 a, float b)
{
return new Vector2(a.X - b, a.Y - b);
}
/// <summary>
/// Negates a vector.
/// </summary>
public static Vector2 operator -(Vector2 vec)
{
return new Vector2(-vec.X, -vec.Y);
}
/// <summary>
/// Adds two vectors together, returning a new vector with the components of each added together.
/// </summary>
public static Vector2 operator +(Vector2 a, Vector2 b)
{
return new Vector2(a.X + b.X, a.Y + b.Y);
}
/// <summary>
/// Adds a scalar to each component of a vector, returning a new vector.
/// </summary>
public static Vector2 operator +(Vector2 a, float b)
{
return new Vector2(a.X + b, a.Y + b);
}
/// <summary>
/// Multiply a vector by a scale by multiplying the individual components.
/// </summary>
/// <param name="vec">The vector to multiply.</param>
/// <param name="scale">The scale to multiply with.</param>
/// <returns>A new vector.</returns>
public static Vector2 operator *(Vector2 vec, float scale)
{
return new Vector2(vec.X * scale, vec.Y * scale);
}
/// <summary>
/// Multiplies a vector's components corresponding to a vector scale.
/// </summary>
public static Vector2 operator *(Vector2 vec, Vector2 scale)
{
return new Vector2(vec.X * scale.X, vec.Y * scale.Y);
}
/// <summary>
/// Divide a vector by a scale by dividing the individual components.
/// </summary>
/// <param name="vec">The vector to divide.</param>
/// <param name="scale">The scale to divide by.</param>
/// <returns>A new vector.</returns>
public static Vector2 operator /(Vector2 vec, float scale)
{
return new Vector2(vec.X / scale, vec.Y / scale);
}
/// <summary>
/// Divides a vector's components corresponding to a vector scale.
/// </summary>
public static Vector2 operator /(Vector2 vec, Vector2 scale)
{
return new Vector2(vec.X / scale.X, vec.Y / scale.Y);
}
/// <summary>
/// Return a vector made up of the smallest components of the provided vectors.
/// </summary>
public static Vector2 ComponentMin(Vector2 a, Vector2 b)
{
return new Vector2(
a.X < b.X ? a.X : b.X,
a.Y < b.Y ? a.Y : b.Y
);
}
/// <summary>
/// Return a vector made up of the largest components of the provided vectors.
/// </summary>
public static Vector2 ComponentMax(Vector2 a, Vector2 b)
{
return new Vector2(
a.X > b.X ? a.X : b.X,
a.Y > b.Y ? a.Y : b.Y
);
}
/// <summary>
/// Returns the vector with the smallest magnitude. If both have equal magnitude, <paramref name="b" /> is selected.
/// </summary>
public static Vector2 MagnitudeMin(Vector2 a, Vector2 b)
{
return a.LengthSquared < b.LengthSquared ? a : b;
}
/// <summary>
/// Returns the vector with the largest magnitude. If both have equal magnitude, <paramref name="a" /> is selected.
/// </summary>
public static Vector2 MagnitudeMax(Vector2 a, Vector2 b)
{
return a.LengthSquared >= b.LengthSquared ? a : b;
}
/// <summary>
/// Clamps the components of a vector to minimum and maximum vectors.
/// </summary>
/// <param name="vector">The vector to clamp.</param>
/// <param name="min">The lower bound vector.</param>
/// <param name="max">The upper bound vector.</param>
public static Vector2 Clamp(Vector2 vector, Vector2 min, Vector2 max)
{
return new Vector2(
vector.X.Clamp(min.X, max.X),
vector.Y.Clamp(min.Y, max.Y)
);
}
/// <summary>
/// Calculates the dot product of two vectors.
/// </summary>
public static float Dot(Vector2 a, Vector2 b)
{
return a.X * b.X + a.Y * b.Y;
}
/// <summary>
/// Linearly interpolates two vectors so make a mix based on a factor.
/// </summary>
/// <returns>
/// a when factor=0, b when factor=1, a linear interpolation between the two otherwise.
/// </returns>
public static Vector2 Lerp(Vector2 a, Vector2 b, float factor)
{
return new Vector2(
factor * (b.X - a.X) + a.X,
factor * (b.Y - a.Y) + a.Y
);
}
public static Vector2 LerpClamped(in Vector2 a, in Vector2 b, float factor)
{
if (factor <= 0)
return a;
if (factor >= 1)
return b;
return Lerp(a, b, factor);
}
public void Deconstruct(out float x, out float y)
{
x = X;
y = Y;
}
public static implicit operator Vector2((float x, float y) tuple)
{
var (x, y) = tuple;
return new Vector2(x, y);
}
/// <summary>
/// Returns a string that represents the current Vector2.
/// </summary>
public override string ToString()
{
return $"({X}, {Y})";
}
public static bool operator ==(Vector2 a, Vector2 b)
{
return a.Equals(b);
}
public static bool operator !=(Vector2 a, Vector2 b)
{
return !a.Equals(b);
}
/// <summary>
/// Compare a vector to another vector and check if they are equal.
/// </summary>
/// <param name="other">Other vector to check.</param>
/// <returns>True if the two vectors are equal.</returns>
public bool Equals(Vector2 other)
{
return X == other.X && Y == other.Y;
}
/// <summary>
/// Compare a vector to an object and check if they are equal.
/// </summary>
/// <param name="obj">Other object to check.</param>
/// <returns>True if Object and vector are equal.</returns>
public override bool Equals(object obj)
{
return obj is Vector2 vec && Equals(vec);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A unique hash code for this instance.</returns>
public override int GetHashCode()
{
unchecked
{
return (X.GetHashCode() * 397) ^ Y.GetHashCode();
}
}
public bool EqualsApprox(Vector2 other)
{
return FloatMath.CloseTo(X, other.X) && FloatMath.CloseTo(Y, other.Y);
}
public bool EqualsApprox(Vector2 other, double tolerance)
{
return FloatMath.CloseTo(X, other.X, tolerance) && FloatMath.CloseTo(Y, other.Y, tolerance);
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Runtime.InteropServices;
namespace Robust.Shared.Maths
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public readonly struct Vector2d
{
public readonly double X;
public readonly double Y;
public Vector2d(double x, double y)
{
X = x;
Y = y;
}
public void Deconstruct(out double x, out double y)
{
x = X;
y = Y;
}
public static implicit operator Vector2d(Vector2 vector)
{
return new Vector2d(vector.X, vector.Y);
}
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.Runtime.InteropServices;
namespace Robust.Shared.Maths
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public readonly struct Vector2i : IEquatable<Vector2i>
{
public static readonly Vector2i Zero = new Vector2i(0, 0);
/// <summary>
/// The X component of the Vector2i.
/// </summary>
public readonly int X;
/// <summary>
/// The Y component of the Vector2i.
/// </summary>
public readonly int Y;
/// <summary>
/// Construct a vector from its coordinates.
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y coordinate</param>
public Vector2i(int x, int y)
{
X = x;
Y = y;
}
public static Vector2i ComponentMax(Vector2i a, Vector2i b)
{
return new Vector2i(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
}
public static Vector2i ComponentMin(Vector2i a, Vector2i b)
{
return new Vector2i(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y));
}
/// <summary>
/// Compare a vector to another vector and check if they are equal.
/// </summary>
/// <param name="other">Other vector to check.</param>
/// <returns>True if the two vectors are equal.</returns>
public bool Equals(Vector2i other)
{
return X == other.X && Y == other.Y;
}
/// <summary>
/// Compare a vector to an object and check if they are equal.
/// </summary>
/// <param name="obj">Other object to check.</param>
/// <returns>True if Object and vector are equal.</returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Vector2i && Equals((Vector2i) obj);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A unique hash code for this instance.</returns>
public override int GetHashCode()
{
unchecked
{
return (X * 397) ^ Y;
}
}
public static Vector2i operator -(Vector2i a, Vector2i b)
{
return new Vector2i(a.X - b.X, a.Y - b.Y);
}
public static Vector2i operator -(Vector2i a, int b)
{
return new Vector2i(a.X - b, a.Y - b);
}
public static Vector2i operator +(Vector2i a, Vector2i b)
{
return new Vector2i(a.X + b.X, a.Y + b.Y);
}
public static Vector2i operator +(Vector2i a, int b)
{
return new Vector2i(a.X + b, a.Y + b);
}
public static Vector2i operator *(Vector2i a, Vector2i b)
{
return new Vector2i(a.X * b.X, a.Y * b.Y);
}
public static Vector2i operator *(Vector2i a, int scale)
{
return new Vector2i(a.X * scale, a.Y * scale);
}
public static Vector2 operator *(Vector2i a, float scale)
{
return new Vector2(a.X * scale, a.Y * scale);
}
public static Vector2i operator /(Vector2i a, Vector2i b)
{
return new Vector2i(a.X / b.X, a.Y / b.Y);
}
public static Vector2i operator /(Vector2i a, int scale)
{
return new Vector2i(a.X / scale, a.Y / scale);
}
public static Vector2 operator /(Vector2i a, float scale)
{
return new Vector2(a.X / scale, a.Y / scale);
}
public void Deconstruct(out int x, out int y)
{
x = X;
y = Y;
}
public static implicit operator Vector2(Vector2i vector)
{
return new Vector2(vector.X, vector.Y);
}
public static explicit operator Vector2i(Vector2 vector)
{
return new Vector2i((int) vector.X, (int) vector.Y);
}
/// <summary>
/// Returns a string that represents the current Vector2i.
/// </summary>
public override string ToString()
{
return $"({X}, {Y})";
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
namespace Robust.Shared.Maths
{
[JsonObject(MemberSerialization.Fields)]
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public readonly struct Vector2u : IEquatable<Vector2u>
{
/// <summary>
/// The X component of the Vector2i.
/// </summary>
public readonly uint X;
/// <summary>
/// The Y component of the Vector2i.
/// </summary>
public readonly uint Y;
/// <summary>
/// Construct a vector from its coordinates.
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y coordinate</param>
public Vector2u(uint x, uint y)
{
X = x;
Y = y;
}
public void Deconstruct(out uint x, out uint y)
{
x = X;
y = Y;
}
/// <summary>
/// Compare a vector to another vector and check if they are equal.
/// </summary>
/// <param name="other">Other vector to check.</param>
/// <returns>True if the two vectors are equal.</returns>
public bool Equals(Vector2u other)
{
return X == other.X && Y == other.Y;
}
/// <summary>
/// Compare a vector to an object and check if they are equal.
/// </summary>
/// <param name="obj">Other object to check.</param>
/// <returns>True if Object and vector are equal.</returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Vector2u vec && Equals(vec);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A unique hash code for this instance.</returns>
public override int GetHashCode()
{
unchecked
{
return ((int) X * 397) ^ (int) Y;
}
}
public static Vector2u operator /(Vector2u vector, uint divider)
{
return new Vector2u(vector.X / divider, vector.Y / divider);
}
public static implicit operator Vector2(Vector2u vector)
{
return new Vector2(vector.X, vector.Y);
}
public static explicit operator Vector2u(Vector2 vector)
{
return new Vector2u((uint) vector.X, (uint) vector.Y);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
using System;
using System.Runtime.InteropServices;
namespace Robust.Shared.Maths
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public readonly struct Vector3d
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public Vector3d(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public void Deconstruct(out double x, out double y, out double z)
{
x = X;
y = Y;
z = Z;
}
public static implicit operator Vector3d(Vector3 vector)
{
return new Vector3d(vector.X, vector.Y, vector.Z);
}
}
}

View File

@@ -0,0 +1,898 @@
#region --- License ---
/*
Copyright (c) 2006 - 2008 The Open Toolkit library.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion --- License ---
using System;
using System.Runtime.InteropServices;
using System.Xml.Serialization;
namespace Robust.Shared.Maths
{
/// <summary>Represents a 4D vector using four single-precision floating-point numbers.</summary>
/// <remarks>
/// The Vector4 structure is suitable for interoperation with unmanaged code requiring four consecutive floats.
/// </remarks>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Vector4 : IEquatable<Vector4>
{
#region Fields
/// <summary>
/// The X component of the Vector4.
/// </summary>
public float X;
/// <summary>
/// The Y component of the Vector4.
/// </summary>
public float Y;
/// <summary>
/// The Z component of the Vector4.
/// </summary>
public float Z;
/// <summary>
/// The W component of the Vector4.
/// </summary>
public float W;
/// <summary>
/// Defines a unit-length Vector4 that points towards the X-axis.
/// </summary>
public static readonly Vector4 UnitX = new Vector4(1, 0, 0, 0);
/// <summary>
/// Defines a unit-length Vector4 that points towards the Y-axis.
/// </summary>
public static readonly Vector4 UnitY = new Vector4(0, 1, 0, 0);
/// <summary>
/// Defines a unit-length Vector4 that points towards the Z-axis.
/// </summary>
public static readonly Vector4 UnitZ = new Vector4(0, 0, 1, 0);
/// <summary>
/// Defines a unit-length Vector4 that points towards the W-axis.
/// </summary>
public static readonly Vector4 UnitW = new Vector4(0, 0, 0, 1);
/// <summary>
/// Defines a zero-length Vector4.
/// </summary>
public static readonly Vector4 Zero = new Vector4(0, 0, 0, 0);
/// <summary>
/// Defines an instance with all components set to 1.
/// </summary>
public static readonly Vector4 One = new Vector4(1, 1, 1, 1);
/// <summary>
/// Defines the size of the Vector4 struct in bytes.
/// </summary>
public static readonly int SizeInBytes = Marshal.SizeOf(new Vector4());
#endregion Fields
#region Constructors
/// <summary>
/// Constructs a new instance.
/// </summary>
/// <param name="value">The value that will initialize this instance.</param>
public Vector4(float value)
{
X = value;
Y = value;
Z = value;
W = value;
}
/// <summary>
/// Constructs a new Vector4.
/// </summary>
/// <param name="x">The x component of the Vector4.</param>
/// <param name="y">The y component of the Vector4.</param>
/// <param name="z">The z component of the Vector4.</param>
/// <param name="w">The w component of the Vector4.</param>
public Vector4(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
/// <summary>
/// Constructs a new Vector4 from the given Vector2.
/// </summary>
/// <param name="v">The Vector2 to copy components from.</param>
public Vector4(Vector2 v)
{
X = v.X;
Y = v.Y;
Z = 0.0f;
W = 0.0f;
}
/// <summary>
/// Constructs a new Vector4 from the given Vector3.
/// The w component is initialized to 0.
/// </summary>
/// <param name="v">The Vector3 to copy components from.</param>
/// <remarks><seealso cref="Vector4(Vector3, float)"/></remarks>
public Vector4(Vector3 v)
{
X = v.X;
Y = v.Y;
Z = v.Z;
W = 0.0f;
}
/// <summary>
/// Constructs a new Vector4 from the specified Vector3 and w component.
/// </summary>
/// <param name="v">The Vector3 to copy components from.</param>
/// <param name="w">The w component of the new Vector4.</param>
public Vector4(Vector3 v, float w)
{
X = v.X;
Y = v.Y;
Z = v.Z;
W = w;
}
/// <summary>
/// Constructs a new Vector4 from the given Vector4.
/// </summary>
/// <param name="v">The Vector4 to copy components from.</param>
public Vector4(Vector4 v)
{
X = v.X;
Y = v.Y;
Z = v.Z;
W = v.W;
}
public void Deconstruct(out float x, out float y, out float z, out float w)
{
x = X;
y = Y;
z = Z;
w = W;
}
#endregion Constructors
#region Public Members
#region Instance
#region public float Length
/// <summary>
/// Gets the length (magnitude) of the vector.
/// </summary>
/// <seealso cref="LengthSquared"/>
public float Length => (float) Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
#endregion public float Length
#region public float LengthSquared
/// <summary>
/// Gets the square of the vector length (magnitude).
/// </summary>
/// <remarks>
/// This property avoids the costly square root operation required by the Length property. This makes it more suitable
/// for comparisons.
/// </remarks>
/// <see cref="Length"/>
public float LengthSquared => X * X + Y * Y + Z * Z + W * W;
#endregion public float LengthSquared
#region public void Normalize()
/// <summary>
/// Scales the Vector4 to unit length.
/// </summary>
public void Normalize()
{
var scale = 1.0f / Length;
X *= scale;
Y *= scale;
Z *= scale;
W *= scale;
}
#endregion public void Normalize()
#endregion Instance
#region Static
#region Add
/// <summary>
/// Adds two vectors.
/// </summary>
/// <param name="a">Left operand.</param>
/// <param name="b">Right operand.</param>
/// <returns>Result of operation.</returns>
public static Vector4 Add(Vector4 a, Vector4 b)
{
Add(ref a, ref b, out a);
return a;
}
/// <summary>
/// Adds two vectors.
/// </summary>
/// <param name="a">Left operand.</param>
/// <param name="b">Right operand.</param>
/// <param name="result">Result of operation.</param>
public static void Add(ref Vector4 a, ref Vector4 b, out Vector4 result)
{
result = new Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W);
}
#endregion Add
#region Subtract
/// <summary>
/// Subtract one Vector from another
/// </summary>
/// <param name="a">First operand</param>
/// <param name="b">Second operand</param>
/// <returns>Result of subtraction</returns>
public static Vector4 Subtract(Vector4 a, Vector4 b)
{
Subtract(ref a, ref b, out a);
return a;
}
/// <summary>
/// Subtract one Vector from another
/// </summary>
/// <param name="a">First operand</param>
/// <param name="b">Second operand</param>
/// <param name="result">Result of subtraction</param>
public static void Subtract(ref Vector4 a, ref Vector4 b, out Vector4 result)
{
result = new Vector4(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W);
}
#endregion Subtract
#region Multiply
/// <summary>
/// Multiplies a vector by a scalar.
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <returns>Result of the operation.</returns>
public static Vector4 Multiply(Vector4 vector, float scale)
{
Multiply(ref vector, scale, out vector);
return vector;
}
/// <summary>
/// Multiplies a vector by a scalar.
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <param name="result">Result of the operation.</param>
public static void Multiply(ref Vector4 vector, float scale, out Vector4 result)
{
result = new Vector4(vector.X * scale, vector.Y * scale, vector.Z * scale, vector.W * scale);
}
/// <summary>
/// Multiplies a vector by the components a vector (scale).
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <returns>Result of the operation.</returns>
public static Vector4 Multiply(Vector4 vector, Vector4 scale)
{
Multiply(ref vector, ref scale, out vector);
return vector;
}
/// <summary>
/// Multiplies a vector by the components of a vector (scale).
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <param name="result">Result of the operation.</param>
public static void Multiply(ref Vector4 vector, ref Vector4 scale, out Vector4 result)
{
result = new Vector4(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z, vector.W * scale.W);
}
#endregion Multiply
#region Divide
/// <summary>
/// Divides a vector by a scalar.
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <returns>Result of the operation.</returns>
public static Vector4 Divide(Vector4 vector, float scale)
{
Divide(ref vector, scale, out vector);
return vector;
}
/// <summary>
/// Divides a vector by a scalar.
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <param name="result">Result of the operation.</param>
public static void Divide(ref Vector4 vector, float scale, out Vector4 result)
{
Multiply(ref vector, 1 / scale, out result);
}
/// <summary>
/// Divides a vector by the components of a vector (scale).
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <returns>Result of the operation.</returns>
public static Vector4 Divide(Vector4 vector, Vector4 scale)
{
Divide(ref vector, ref scale, out vector);
return vector;
}
/// <summary>
/// Divide a vector by the components of a vector (scale).
/// </summary>
/// <param name="vector">Left operand.</param>
/// <param name="scale">Right operand.</param>
/// <param name="result">Result of the operation.</param>
public static void Divide(ref Vector4 vector, ref Vector4 scale, out Vector4 result)
{
result = new Vector4(vector.X / scale.X, vector.Y / scale.Y, vector.Z / scale.Z, vector.W / scale.W);
}
#endregion Divide
#region Min
/// <summary>
/// Calculate the component-wise minimum of two vectors
/// </summary>
/// <param name="a">First operand</param>
/// <param name="b">Second operand</param>
/// <returns>The component-wise minimum</returns>
public static Vector4 Min(Vector4 a, Vector4 b)
{
a.X = a.X < b.X ? a.X : b.X;
a.Y = a.Y < b.Y ? a.Y : b.Y;
a.Z = a.Z < b.Z ? a.Z : b.Z;
a.W = a.W < b.W ? a.W : b.W;
return a;
}
/// <summary>
/// Calculate the component-wise minimum of two vectors
/// </summary>
/// <param name="a">First operand</param>
/// <param name="b">Second operand</param>
/// <param name="result">The component-wise minimum</param>
public static void Min(ref Vector4 a, ref Vector4 b, out Vector4 result)
{
result.X = a.X < b.X ? a.X : b.X;
result.Y = a.Y < b.Y ? a.Y : b.Y;
result.Z = a.Z < b.Z ? a.Z : b.Z;
result.W = a.W < b.W ? a.W : b.W;
}
#endregion Min
#region Max
/// <summary>
/// Calculate the component-wise maximum of two vectors
/// </summary>
/// <param name="a">First operand</param>
/// <param name="b">Second operand</param>
/// <returns>The component-wise maximum</returns>
public static Vector4 Max(Vector4 a, Vector4 b)
{
a.X = a.X > b.X ? a.X : b.X;
a.Y = a.Y > b.Y ? a.Y : b.Y;
a.Z = a.Z > b.Z ? a.Z : b.Z;
a.W = a.W > b.W ? a.W : b.W;
return a;
}
/// <summary>
/// Calculate the component-wise maximum of two vectors
/// </summary>
/// <param name="a">First operand</param>
/// <param name="b">Second operand</param>
/// <param name="result">The component-wise maximum</param>
public static void Max(ref Vector4 a, ref Vector4 b, out Vector4 result)
{
result.X = a.X > b.X ? a.X : b.X;
result.Y = a.Y > b.Y ? a.Y : b.Y;
result.Z = a.Z > b.Z ? a.Z : b.Z;
result.W = a.W > b.W ? a.W : b.W;
}
#endregion Max
#region Clamp
/// <summary>
/// Clamp a vector to the given minimum and maximum vectors
/// </summary>
/// <param name="vec">Input vector</param>
/// <param name="min">Minimum vector</param>
/// <param name="max">Maximum vector</param>
/// <returns>The clamped vector</returns>
public static Vector4 Clamp(Vector4 vec, Vector4 min, Vector4 max)
{
vec.X = vec.X < min.X ? min.X : vec.X > max.X ? max.X : vec.X;
vec.Y = vec.Y < min.Y ? min.Y : vec.Y > max.Y ? max.Y : vec.Y;
vec.Z = vec.X < min.Z ? min.Z : vec.Z > max.Z ? max.Z : vec.Z;
vec.W = vec.Y < min.W ? min.W : vec.W > max.W ? max.W : vec.W;
return vec;
}
/// <summary>
/// Clamp a vector to the given minimum and maximum vectors
/// </summary>
/// <param name="vec">Input vector</param>
/// <param name="min">Minimum vector</param>
/// <param name="max">Maximum vector</param>
/// <param name="result">The clamped vector</param>
public static void Clamp(ref Vector4 vec, ref Vector4 min, ref Vector4 max, out Vector4 result)
{
result.X = vec.X < min.X ? min.X : vec.X > max.X ? max.X : vec.X;
result.Y = vec.Y < min.Y ? min.Y : vec.Y > max.Y ? max.Y : vec.Y;
result.Z = vec.X < min.Z ? min.Z : vec.Z > max.Z ? max.Z : vec.Z;
result.W = vec.Y < min.W ? min.W : vec.W > max.W ? max.W : vec.W;
}
#endregion Clamp
#region Normalize
/// <summary>
/// Scale a vector to unit length
/// </summary>
/// <param name="vec">The input vector</param>
/// <returns>The normalized vector</returns>
public static Vector4 Normalize(Vector4 vec)
{
var scale = 1.0f / vec.Length;
vec.X *= scale;
vec.Y *= scale;
vec.Z *= scale;
vec.W *= scale;
return vec;
}
/// <summary>
/// Scale a vector to unit length
/// </summary>
/// <param name="vec">The input vector</param>
/// <param name="result">The normalized vector</param>
public static void Normalize(ref Vector4 vec, out Vector4 result)
{
var scale = 1.0f / vec.Length;
result.X = vec.X * scale;
result.Y = vec.Y * scale;
result.Z = vec.Z * scale;
result.W = vec.W * scale;
}
#endregion Normalize
#region Dot
/// <summary>
/// Calculate the dot product of two vectors
/// </summary>
/// <param name="left">First operand</param>
/// <param name="right">Second operand</param>
/// <returns>The dot product of the two inputs</returns>
public static float Dot(Vector4 left, Vector4 right)
{
return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
}
/// <summary>
/// Calculate the dot product of two vectors
/// </summary>
/// <param name="left">First operand</param>
/// <param name="right">Second operand</param>
/// <param name="result">The dot product of the two inputs</param>
public static void Dot(ref Vector4 left, ref Vector4 right, out float result)
{
result = left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
}
#endregion Dot
#region Lerp
/// <summary>
/// Returns a new Vector that is the linear blend of the 2 given Vectors
/// </summary>
/// <param name="a">First input vector</param>
/// <param name="b">Second input vector</param>
/// <param name="blend">The blend factor. a when blend=0, b when blend=1.</param>
/// <returns>a when blend=0, b when blend=1, and a linear combination otherwise</returns>
public static Vector4 Lerp(Vector4 a, Vector4 b, float blend)
{
a.X = blend * (b.X - a.X) + a.X;
a.Y = blend * (b.Y - a.Y) + a.Y;
a.Z = blend * (b.Z - a.Z) + a.Z;
a.W = blend * (b.W - a.W) + a.W;
return a;
}
/// <summary>
/// Returns a new Vector that is the linear blend of the 2 given Vectors
/// </summary>
/// <param name="a">First input vector</param>
/// <param name="b">Second input vector</param>
/// <param name="blend">The blend factor. a when blend=0, b when blend=1.</param>
/// <param name="result">a when blend=0, b when blend=1, and a linear combination otherwise</param>
public static void Lerp(ref Vector4 a, ref Vector4 b, float blend, out Vector4 result)
{
result.X = blend * (b.X - a.X) + a.X;
result.Y = blend * (b.Y - a.Y) + a.Y;
result.Z = blend * (b.Z - a.Z) + a.Z;
result.W = blend * (b.W - a.W) + a.W;
}
#endregion Lerp
#region Barycentric
/// <summary>
/// Interpolate 3 Vectors using Barycentric coordinates
/// </summary>
/// <param name="a">First input Vector</param>
/// <param name="b">Second input Vector</param>
/// <param name="c">Third input Vector</param>
/// <param name="u">First Barycentric Coordinate</param>
/// <param name="v">Second Barycentric Coordinate</param>
/// <returns>a when u=v=0, b when u=1,v=0, c when u=0,v=1, and a linear combination of a,b,c otherwise</returns>
public static Vector4 BaryCentric(Vector4 a, Vector4 b, Vector4 c, float u, float v)
{
return a + u * (b - a) + v * (c - a);
}
/// <summary>Interpolate 3 Vectors using Barycentric coordinates</summary>
/// <param name="a">First input Vector.</param>
/// <param name="b">Second input Vector.</param>
/// <param name="c">Third input Vector.</param>
/// <param name="u">First Barycentric Coordinate.</param>
/// <param name="v">Second Barycentric Coordinate.</param>
/// <param name="result">Output Vector. a when u=v=0, b when u=1,v=0, c when u=0,v=1, and a linear combination of a,b,c otherwise</param>
public static void BaryCentric(ref Vector4 a, ref Vector4 b, ref Vector4 c, float u, float v, out Vector4 result)
{
result = a; // copy
var temp = b; // copy
Subtract(ref temp, ref a, out temp);
Multiply(ref temp, u, out temp);
Add(ref result, ref temp, out result);
temp = c; // copy
Subtract(ref temp, ref a, out temp);
Multiply(ref temp, v, out temp);
Add(ref result, ref temp, out result);
}
#endregion Barycentric
#region Transform
/// <summary>Transform a Vector by the given Matrix</summary>
/// <param name="vec">The vector to transform</param>
/// <param name="mat">The desired transformation</param>
/// <returns>The transformed vector</returns>
public static Vector4 Transform(Vector4 vec, Matrix4 mat)
{
Transform(ref vec, ref mat, out var result);
return result;
}
/// <summary>Transform a Vector by the given Matrix</summary>
/// <param name="vec">The vector to transform</param>
/// <param name="mat">The desired transformation</param>
/// <param name="result">The transformed vector</param>
public static void Transform(ref Vector4 vec, ref Matrix4 mat, out Vector4 result)
{
result = new Vector4(
vec.X * mat.Row0.X + vec.Y * mat.Row1.X + vec.Z * mat.Row2.X + vec.W * mat.Row3.X,
vec.X * mat.Row0.Y + vec.Y * mat.Row1.Y + vec.Z * mat.Row2.Y + vec.W * mat.Row3.Y,
vec.X * mat.Row0.Z + vec.Y * mat.Row1.Z + vec.Z * mat.Row2.Z + vec.W * mat.Row3.Z,
vec.X * mat.Row0.W + vec.Y * mat.Row1.W + vec.Z * mat.Row2.W + vec.W * mat.Row3.W);
}
/// <summary>
/// Transforms a vector by a quaternion rotation.
/// </summary>
/// <param name="vec">The vector to transform.</param>
/// <param name="quat">The quaternion to rotate the vector by.</param>
/// <returns>The result of the operation.</returns>
public static Vector4 Transform(Vector4 vec, Quaternion quat)
{
Transform(ref vec, ref quat, out var result);
return result;
}
/// <summary>
/// Transforms a vector by a quaternion rotation.
/// </summary>
/// <param name="vec">The vector to transform.</param>
/// <param name="quat">The quaternion to rotate the vector by.</param>
/// <param name="result">The result of the operation.</param>
public static void Transform(ref Vector4 vec, ref Quaternion quat, out Vector4 result)
{
var v = new Quaternion(vec.X, vec.Y, vec.Z, vec.W);
Quaternion.Invert(ref quat, out var i);
Quaternion.Multiply(ref quat, ref v, out var t);
Quaternion.Multiply(ref t, ref i, out v);
result = new Vector4(v.X, v.Y, v.Z, v.W);
}
#endregion Transform
#endregion Static
#region Swizzle
/// <summary>
/// Gets or sets an OpenTK.Vector2 with the X and Y components of this instance.
/// </summary>
[XmlIgnore]
public Vector2 Xy
{
get => new Vector2(X, Y);
set
{
X = value.X;
Y = value.Y;
}
}
/// <summary>
/// Gets or sets an OpenTK.Vector3 with the X, Y and Z components of this instance.
/// </summary>
[XmlIgnore]
public Vector3 Xyz
{
get => new Vector3(X, Y, Z);
set
{
X = value.X;
Y = value.Y;
Z = value.Z;
}
}
#endregion Swizzle
#region Operators
/// <summary>
/// Adds two instances.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>The result of the calculation.</returns>
public static Vector4 operator +(Vector4 left, Vector4 right)
{
left.X += right.X;
left.Y += right.Y;
left.Z += right.Z;
left.W += right.W;
return left;
}
/// <summary>
/// Subtracts two instances.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>The result of the calculation.</returns>
public static Vector4 operator -(Vector4 left, Vector4 right)
{
left.X -= right.X;
left.Y -= right.Y;
left.Z -= right.Z;
left.W -= right.W;
return left;
}
/// <summary>
/// Negates an instance.
/// </summary>
/// <param name="vec">The instance.</param>
/// <returns>The result of the calculation.</returns>
public static Vector4 operator -(Vector4 vec)
{
vec.X = -vec.X;
vec.Y = -vec.Y;
vec.Z = -vec.Z;
vec.W = -vec.W;
return vec;
}
/// <summary>
/// Multiplies an instance by a scalar.
/// </summary>
/// <param name="vec">The instance.</param>
/// <param name="scale">The scalar.</param>
/// <returns>The result of the calculation.</returns>
public static Vector4 operator *(Vector4 vec, float scale)
{
vec.X *= scale;
vec.Y *= scale;
vec.Z *= scale;
vec.W *= scale;
return vec;
}
/// <summary>
/// Multiplies an instance by a scalar.
/// </summary>
/// <param name="scale">The scalar.</param>
/// <param name="vec">The instance.</param>
/// <returns>The result of the calculation.</returns>
public static Vector4 operator *(float scale, Vector4 vec)
{
vec.X *= scale;
vec.Y *= scale;
vec.Z *= scale;
vec.W *= scale;
return vec;
}
/// <summary>
/// Divides an instance by a scalar.
/// </summary>
/// <param name="vec">The instance.</param>
/// <param name="scale">The scalar.</param>
/// <returns>The result of the calculation.</returns>
public static Vector4 operator /(Vector4 vec, float scale)
{
var mult = 1.0f / scale;
vec.X *= mult;
vec.Y *= mult;
vec.Z *= mult;
vec.W *= mult;
return vec;
}
/// <summary>
/// Compares two instances for equality.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>True, if left equals right; false otherwise.</returns>
public static bool operator ==(Vector4 left, Vector4 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two instances for inequality.
/// </summary>
/// <param name="left">The first instance.</param>
/// <param name="right">The second instance.</param>
/// <returns>True, if left does not equal right; false otherwise.</returns>
public static bool operator !=(Vector4 left, Vector4 right)
{
return !left.Equals(right);
}
#endregion Operators
#region Overrides
#region public override string ToString()
/// <summary>
/// Returns a System.String that represents the current Vector4.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"({X}, {Y}, {Z}, {W})";
}
#endregion public override string ToString()
#region public override int GetHashCode()
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A System.Int32 containing the unique hashcode for this instance.</returns>
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode();
}
#endregion public override int GetHashCode()
#region public override bool Equals(object obj)
/// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>
/// <param name="obj">The object to compare to.</param>
/// <returns>True if the instances are equal; false otherwise.</returns>
public override bool Equals(object obj)
{
if (!(obj is Vector4))
return false;
return Equals((Vector4) obj);
}
#endregion public override bool Equals(object obj)
#endregion Overrides
#endregion Public Members
#region IEquatable<Vector4> Members
/// <summary>Indicates whether the current vector is equal to another vector.</summary>
/// <param name="other">A vector to compare with this vector.</param>
/// <returns>true if the current vector is equal to the vector parameter; otherwise, false.</returns>
public bool Equals(Vector4 other)
{
return
X == other.X &&
Y == other.Y &&
Z == other.Z &&
W == other.W;
}
#endregion IEquatable<Vector4> Members
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Runtime.InteropServices;
namespace Robust.Shared.Maths
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public readonly struct Vector4d
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public readonly double W;
public Vector4d(double x, double y, double z, double w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public void Deconstruct(out double x, out double y, out double z, out double w)
{
x = X;
y = Y;
z = Z;
w = W;
}
public static implicit operator Vector4d(Vector4 vector)
{
return new Vector4d(vector.X, vector.Y, vector.Z, vector.W);
}
}
}