mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
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:
228
Robust.Shared.Maths/Angle.cs
Normal file
228
Robust.Shared.Maths/Angle.cs
Normal 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
168
Robust.Shared.Maths/Box2.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Robust.Shared.Maths/Box2i.cs
Normal file
106
Robust.Shared.Maths/Box2i.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
112
Robust.Shared.Maths/Circle.cs
Normal file
112
Robust.Shared.Maths/Circle.cs
Normal 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
1743
Robust.Shared.Maths/Color.cs
Normal file
File diff suppressed because it is too large
Load Diff
70
Robust.Shared.Maths/Direction.cs
Normal file
70
Robust.Shared.Maths/Direction.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
148
Robust.Shared.Maths/FloatMath.cs
Normal file
148
Robust.Shared.Maths/FloatMath.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Robust.Shared.Maths/IApproxEquatable.cs
Normal file
11
Robust.Shared.Maths/IApproxEquatable.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
294
Robust.Shared.Maths/MathHelper.cs
Normal file
294
Robust.Shared.Maths/MathHelper.cs
Normal 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
|
||||
}
|
||||
}
|
||||
950
Robust.Shared.Maths/Matrix3.cs
Normal file
950
Robust.Shared.Maths/Matrix3.cs
Normal 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
|
||||
}
|
||||
1127
Robust.Shared.Maths/Matrix4.cs
Normal file
1127
Robust.Shared.Maths/Matrix4.cs
Normal file
File diff suppressed because it is too large
Load Diff
35
Robust.Shared.Maths/Properties/AssemblyInfo.cs
Normal file
35
Robust.Shared.Maths/Properties/AssemblyInfo.cs
Normal 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")]
|
||||
866
Robust.Shared.Maths/Quaternion.cs
Normal file
866
Robust.Shared.Maths/Quaternion.cs
Normal 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
|
||||
}
|
||||
}
|
||||
36
Robust.Shared.Maths/RandomExtensions.cs
Normal file
36
Robust.Shared.Maths/RandomExtensions.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Robust.Shared.Maths/Robust.Shared.Maths.csproj
Normal file
107
Robust.Shared.Maths/Robust.Shared.Maths.csproj
Normal 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>
|
||||
168
Robust.Shared.Maths/UIBox2.cs
Normal file
168
Robust.Shared.Maths/UIBox2.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
131
Robust.Shared.Maths/UIBox2i.cs
Normal file
131
Robust.Shared.Maths/UIBox2i.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
321
Robust.Shared.Maths/Vector2.cs
Normal file
321
Robust.Shared.Maths/Vector2.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Robust.Shared.Maths/Vector2d.cs
Normal file
30
Robust.Shared.Maths/Vector2d.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Robust.Shared.Maths/Vector2i.cs
Normal file
150
Robust.Shared.Maths/Vector2i.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Robust.Shared.Maths/Vector2u.cs
Normal file
87
Robust.Shared.Maths/Vector2u.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
1108
Robust.Shared.Maths/Vector3.cs
Normal file
1108
Robust.Shared.Maths/Vector3.cs
Normal file
File diff suppressed because it is too large
Load Diff
33
Robust.Shared.Maths/Vector3d.cs
Normal file
33
Robust.Shared.Maths/Vector3d.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
898
Robust.Shared.Maths/Vector4.cs
Normal file
898
Robust.Shared.Maths/Vector4.cs
Normal 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
|
||||
}
|
||||
}
|
||||
36
Robust.Shared.Maths/Vector4d.cs
Normal file
36
Robust.Shared.Maths/Vector4d.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user