diff --git a/Config.cs b/Config.cs index 38d7181..958764e 100644 --- a/Config.cs +++ b/Config.cs @@ -6,6 +6,18 @@ using System.Reflection; namespace lib { +public class DescAttribute : Attribute +{ + public string Desc { get; private set; } + + public DescAttribute( string desc ) + { + Desc = desc; + } +} + + + [Serializable] public class ResRefConfig : res.Ref where T: Config { diff --git a/SharpLib.csproj b/SharpLib.csproj index 3d768cc..3b3457f 100644 --- a/SharpLib.csproj +++ b/SharpLib.csproj @@ -66,6 +66,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/math/AngleSingle.cs b/math/AngleSingle.cs new file mode 100644 index 0000000..213fa3b --- /dev/null +++ b/math/AngleSingle.cs @@ -0,0 +1,779 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a unit independant angle using a single-precision floating-point + /// internal representation. + /// + [DataStyle(DataStyle.Compact)] + [DataContract] + public struct AngleSingle : IComparable, IComparable, IEquatable, IFormattable + { + /// + /// A value that specifies the size of a single degree. + /// + public const float Degree = 0.002777777777777778f; + + /// + /// A value that specifies the size of a single minute. + /// + public const float Minute = 0.000046296296296296f; + + /// + /// A value that specifies the size of a single second. + /// + public const float Second = 0.000000771604938272f; + + /// + /// A value that specifies the size of a single radian. + /// + public const float Radian = 0.159154943091895336f; + + /// + /// A value that specifies the size of a single milliradian. + /// + public const float Milliradian = 0.0001591549431f; + + /// + /// A value that specifies the size of a single gradian. + /// + public const float Gradian = 0.0025f; + + /// + /// The internal representation of the angle. + /// + private float radians; + + /// + /// Initializes a new instance of the struct with the + /// given unit dependant angle and unit type. + /// + /// A unit dependant measure of the angle. + /// The type of unit the angle argument is. + public AngleSingle(float angle, AngleType type) + { + switch (type) + { + case AngleType.Revolution: + radians = MathUtil.RevolutionsToRadians(angle); + break; + + case AngleType.Degree: + radians = MathUtil.DegreesToRadians(angle); + break; + + case AngleType.Radian: + radians = angle; + break; + + case AngleType.Gradian: + radians = MathUtil.GradiansToRadians(angle); + break; + + default: + radians = 0.0f; + break; + } + } + + /// + /// Initializes a new instance of the struct using the + /// arc length formula (θ = s/r). + /// + /// The measure of the arc. + /// The radius of the circle. + public AngleSingle(float arcLength, float radius) + { + radians = arcLength / radius; + } + + /// + /// Wraps this math.AngleSingle to be in the range [π, -π]. + /// + public void Wrap() + { + float newangle = (float)Math.IEEERemainder(radians, MathUtil.TwoPi); + + if (newangle <= -MathUtil.Pi) + newangle += MathUtil.TwoPi; + else if (newangle > MathUtil.Pi) + newangle -= MathUtil.TwoPi; + + radians = newangle; + } + + /// + /// Wraps this math.AngleSingle to be in the range [0, 2π). + /// + public void WrapPositive() + { + float newangle = radians % MathUtil.TwoPi; + + if (newangle < 0.0) + newangle += MathUtil.TwoPi; + + radians = newangle; + } + + /// + /// Gets or sets the total number of revolutions this math.AngleSingle represents. + /// + [DataMemberIgnore] + public float Revolutions + { + get { return MathUtil.RadiansToRevolutions(radians); } + set { radians = MathUtil.RevolutionsToRadians(value); } + } + + /// + /// Gets or sets the total number of degrees this math.AngleSingle represents. + /// + [DataMemberIgnore] + public float Degrees + { + get { return MathUtil.RadiansToDegrees(radians); } + set { radians = MathUtil.DegreesToRadians(value); } + } + + /// + /// Gets or sets the minutes component of the degrees this math.AngleSingle represents. + /// When setting the minutes, if the value is in the range (-60, 60) the whole degrees are + /// not changed; otherwise, the whole degrees may be changed. Fractional values may set + /// the seconds component. + /// + [DataMemberIgnore] + public float Minutes + { + get + { + float degrees = MathUtil.RadiansToDegrees(radians); + + if (degrees < 0) + { + float degreesfloor = (float)Math.Ceiling(degrees); + return (degrees - degreesfloor) * 60.0f; + } + else + { + float degreesfloor = (float)Math.Floor(degrees); + return (degrees - degreesfloor) * 60.0f; + } + } + set + { + float degrees = MathUtil.RadiansToDegrees(radians); + float degreesfloor = (float)Math.Floor(degrees); + + degreesfloor += value / 60.0f; + radians = MathUtil.DegreesToRadians(degreesfloor); + } + } + + /// + /// Gets or sets the seconds of the degrees this math.AngleSingle represents. + /// When setting te seconds, if the value is in the range (-60, 60) the whole minutes + /// or whole degrees are not changed; otherwise, the whole minutes or whole degrees + /// may be changed. + /// + [DataMemberIgnore] + public float Seconds + { + get + { + float degrees = MathUtil.RadiansToDegrees(radians); + + if (degrees < 0) + { + float degreesfloor = (float)Math.Ceiling(degrees); + + float minutes = (degrees - degreesfloor) * 60.0f; + float minutesfloor = (float)Math.Ceiling(minutes); + + return (minutes - minutesfloor) * 60.0f; + } + else + { + float degreesfloor = (float)Math.Floor(degrees); + + float minutes = (degrees - degreesfloor) * 60.0f; + float minutesfloor = (float)Math.Floor(minutes); + + return (minutes - minutesfloor) * 60.0f; + } + } + set + { + float degrees = MathUtil.RadiansToDegrees(radians); + float degreesfloor = (float)Math.Floor(degrees); + + float minutes = (degrees - degreesfloor) * 60.0f; + float minutesfloor = (float)Math.Floor(minutes); + + minutesfloor += value / 60.0f; + degreesfloor += minutesfloor / 60.0f; + radians = MathUtil.DegreesToRadians(degreesfloor); + } + } + + /// + /// Gets or sets the total number of radians this math.AngleSingle represents. + /// + public float Radians + { + get { return radians; } + set { radians = value; } + } + + /// + /// Gets or sets the total number of milliradians this math.AngleSingle represents. + /// One milliradian is equal to 1/(2000π). + /// + [DataMemberIgnore] + public float Milliradians + { + get { return radians / (Milliradian * MathUtil.TwoPi); } + set { radians = value * (Milliradian * MathUtil.TwoPi); } + } + + /// + /// Gets or sets the total number of gradians this math.AngleSingle represents. + /// + [DataMemberIgnore] + public float Gradians + { + get { return MathUtil.RadiansToGradians(radians); } + set { radians = MathUtil.GradiansToRadians(value); } + } + + /// + /// Gets a System.Boolean that determines whether this math.Angle + /// is a right angle (i.e. 90° or π/2). + /// + [DataMemberIgnore] + public bool IsRight + { + get { return radians == MathUtil.PiOverTwo; } + } + + /// + /// Gets a System.Boolean that determines whether this math.Angle + /// is a straight angle (i.e. 180° or π). + /// + [DataMemberIgnore] + public bool IsStraight + { + get { return radians == MathUtil.Pi; } + } + + /// + /// Gets a System.Boolean that determines whether this math.Angle + /// is a full rotation angle (i.e. 360° or 2π). + /// + [DataMemberIgnore] + public bool IsFullRotation + { + get { return radians == MathUtil.TwoPi; } + } + + /// + /// Gets a System.Boolean that determines whether this math.Angle + /// is an oblique angle (i.e. is not 90° or a multiple of 90°). + /// + [DataMemberIgnore] + public bool IsOblique + { + get { return WrapPositive(this).radians != MathUtil.PiOverTwo; } + } + + /// + /// Gets a System.Boolean that determines whether this math.Angle + /// is an acute angle (i.e. less than 90° but greater than 0°). + /// + [DataMemberIgnore] + public bool IsAcute + { + get { return radians > 0.0 && radians < MathUtil.PiOverTwo; } + } + + /// + /// Gets a System.Boolean that determines whether this math.Angle + /// is an obtuse angle (i.e. greater than 90° but less than 180°). + /// + [DataMemberIgnore] + public bool IsObtuse + { + get { return radians > MathUtil.PiOverTwo && radians < MathUtil.Pi; } + } + + /// + /// Gets a System.Boolean that determines whether this math.Angle + /// is a reflex angle (i.e. greater than 180° but less than 360°). + /// + [DataMemberIgnore] + public bool IsReflex + { + get { return radians > MathUtil.Pi && radians < MathUtil.TwoPi; } + } + + /// + /// Gets a math.AngleSingle instance that complements this angle (i.e. the two angles add to 90°). + /// + [DataMemberIgnore] + public AngleSingle Complement + { + get { return new AngleSingle(MathUtil.PiOverTwo - radians, AngleType.Radian); } + } + + /// + /// Gets a math.AngleSingle instance that supplements this angle (i.e. the two angles add to 180°). + /// + [DataMemberIgnore] + public AngleSingle Supplement + { + get { return new AngleSingle(MathUtil.Pi - radians, AngleType.Radian); } + } + + /// + /// Wraps the math.AngleSingle given in the value argument to be in the range [π, -π]. + /// + /// A math.AngleSingle to wrap. + /// The math.AngleSingle that is wrapped. + public static AngleSingle Wrap(AngleSingle value) + { + value.Wrap(); + return value; + } + + /// + /// Wraps the math.AngleSingle given in the value argument to be in the range [0, 2π). + /// + /// A math.AngleSingle to wrap. + /// The math.AngleSingle that is wrapped. + public static AngleSingle WrapPositive(AngleSingle value) + { + value.WrapPositive(); + return value; + } + + /// + /// Compares two math.AngleSingle instances and returns the smaller angle. + /// + /// The first math.AngleSingle instance to compare. + /// The second math.AngleSingle instance to compare. + /// The smaller of the two given math.AngleSingle instances. + public static AngleSingle Min(AngleSingle left, AngleSingle right) + { + if (left.radians < right.radians) + return left; + + return right; + } + + /// + /// Compares two math.AngleSingle instances and returns the greater angle. + /// + /// The first math.AngleSingle instance to compare. + /// The second math.AngleSingle instance to compare. + /// The greater of the two given math.AngleSingle instances. + public static AngleSingle Max(AngleSingle left, AngleSingle right) + { + if (left.radians > right.radians) + return left; + + return right; + } + + /// + /// Adds two math.AngleSingle objects and returns the result. + /// + /// The first object to add. + /// The second object to add. + /// The value of the two objects added together. + public static AngleSingle Add(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians + right.radians, AngleType.Radian); + } + + /// + /// Subtracts two math.AngleSingle objects and returns the result. + /// + /// The first object to subtract. + /// The second object to subtract. + /// The value of the two objects subtracted. + public static AngleSingle Subtract(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians - right.radians, AngleType.Radian); + } + + /// + /// Multiplies two math.AngleSingle objects and returns the result. + /// + /// The first object to multiply. + /// The second object to multiply. + /// The value of the two objects multiplied together. + public static AngleSingle Multiply(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians * right.radians, AngleType.Radian); + } + + /// + /// Divides two math.AngleSingle objects and returns the result. + /// + /// The numerator object. + /// The denominator object. + /// The value of the two objects divided. + public static AngleSingle Divide(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians / right.radians, AngleType.Radian); + } + + /// + /// Gets a new math.AngleSingle instance that represents the zero angle (i.e. 0°). + /// + public static AngleSingle ZeroAngle + { + get { return new AngleSingle(0.0f, AngleType.Radian); } + } + + /// + /// Gets a new math.AngleSingle instance that represents the right angle (i.e. 90° or π/2). + /// + public static AngleSingle RightAngle + { + get { return new AngleSingle(MathUtil.PiOverTwo, AngleType.Radian); } + } + + /// + /// Gets a new math.AngleSingle instance that represents the straight angle (i.e. 180° or π). + /// + public static AngleSingle StraightAngle + { + get { return new AngleSingle(MathUtil.Pi, AngleType.Radian); } + } + + /// + /// Gets a new math.AngleSingle instance that represents the full rotation angle (i.e. 360° or 2π). + /// + public static AngleSingle FullRotationAngle + { + get { return new AngleSingle(MathUtil.TwoPi, AngleType.Radian); } + } + + /// + /// Returns a System.Boolean that indicates whether the values of two math.Angle + /// objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the left and right parameters have the same value; otherwise, false. + public static bool operator ==(AngleSingle left, AngleSingle right) + { + return left.radians == right.radians; + } + + /// + /// Returns a System.Boolean that indicates whether the values of two math.Angle + /// objects are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the left and right parameters do not have the same value; otherwise, false. + public static bool operator !=(AngleSingle left, AngleSingle right) + { + return left.radians != right.radians; + } + + /// + /// Returns a System.Boolean that indicates whether a math.Angle + /// object is less than another math.AngleSingle object. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left is less than right; otherwise, false. + public static bool operator <(AngleSingle left, AngleSingle right) + { + return left.radians < right.radians; + } + + /// + /// Returns a System.Boolean that indicates whether a math.Angle + /// object is greater than another math.AngleSingle object. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left is greater than right; otherwise, false. + public static bool operator >(AngleSingle left, AngleSingle right) + { + return left.radians > right.radians; + } + + /// + /// Returns a System.Boolean that indicates whether a math.Angle + /// object is less than or equal to another math.AngleSingle object. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left is less than or equal to right; otherwise, false. + public static bool operator <=(AngleSingle left, AngleSingle right) + { + return left.radians <= right.radians; + } + + /// + /// Returns a System.Boolean that indicates whether a math.Angle + /// object is greater than or equal to another math.AngleSingle object. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left is greater than or equal to right; otherwise, false. + public static bool operator >=(AngleSingle left, AngleSingle right) + { + return left.radians >= right.radians; + } + + /// + /// Returns the value of the math.AngleSingle operand. (The sign of + /// the operand is unchanged.) + /// + /// A math.AngleSingle object. + /// The value of the value parameter. + public static AngleSingle operator +(AngleSingle value) + { + return value; + } + + /// + /// Returns the the negated value of the math.AngleSingle operand. + /// + /// A math.AngleSingle object. + /// The negated value of the value parameter. + public static AngleSingle operator -(AngleSingle value) + { + return new AngleSingle(-value.radians, AngleType.Radian); + } + + /// + /// Adds two math.AngleSingle objects and returns the result. + /// + /// The first object to add. + /// The second object to add. + /// The value of the two objects added together. + public static AngleSingle operator +(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians + right.radians, AngleType.Radian); + } + + /// + /// Subtracts two math.AngleSingle objects and returns the result. + /// + /// The first object to subtract + /// The second object to subtract. + /// The value of the two objects subtracted. + public static AngleSingle operator -(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians - right.radians, AngleType.Radian); + } + + /// + /// Multiplies two math.AngleSingle objects and returns the result. + /// + /// The first object to multiply. + /// The second object to multiply. + /// The value of the two objects multiplied together. + public static AngleSingle operator *(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians * right.radians, AngleType.Radian); + } + + /// + /// Divides two math.AngleSingle objects and returns the result. + /// + /// The numerator object. + /// The denominator object. + /// The value of the two objects divided. + public static AngleSingle operator /(AngleSingle left, AngleSingle right) + { + return new AngleSingle(left.radians / right.radians, AngleType.Radian); + } + + /// + /// Compares this instance to a specified object and returns an integer that + /// indicates whether the value of this instance is less than, equal to, or greater + /// than the value of the specified object. + /// + /// The object to compare. + /// + /// A signed integer that indicates the relationship of the current instance + /// to the obj parameter. If the value is less than zero, the current instance + /// is less than the other. If the value is zero, the current instance is equal + /// to the other. If the value is greater than zero, the current instance is + /// greater than the other. + /// + public int CompareTo(object other) + { + if (other == null) + return 1; + + if (!(other is AngleSingle)) + throw new ArgumentException("Argument must be of type Angle.", "other"); + + float radians = ((AngleSingle)other).radians; + + if (this.radians > radians) + return 1; + + if (this.radians < radians) + return -1; + + return 0; + } + + /// + /// Compares this instance to a second math.AngleSingle and returns + /// an integer that indicates whether the value of this instance is less than, + /// equal to, or greater than the value of the specified object. + /// + /// The object to compare. + /// + /// A signed integer that indicates the relationship of the current instance + /// to the obj parameter. If the value is less than zero, the current instance + /// is less than the other. If the value is zero, the current instance is equal + /// to the other. If the value is greater than zero, the current instance is + /// greater than the other. + /// + public int CompareTo(AngleSingle other) + { + if (this.radians > other.radians) + return 1; + + if (this.radians < other.radians) + return -1; + + return 0; + } + + /// + /// Returns a value that indicates whether the current instance and a specified + /// math.AngleSingle object have the same value. + /// + /// The object to compare. + /// + /// Returns true if this math.AngleSingle object and another have the same value; + /// otherwise, false. + /// + public bool Equals(AngleSingle other) + { + return this == other; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, MathUtil.RadiansToDegrees(radians).ToString("0.##°")); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "{0}°", MathUtil.RadiansToDegrees(radians).ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, MathUtil.RadiansToDegrees(radians).ToString("0.##°")); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "{0}°", MathUtil.RadiansToDegrees(radians).ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a hash code for this math.AngleSingle instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return (int)(BitConverter.DoubleToInt64Bits(radians) % int.MaxValue); + } + + /// + /// Returns a value that indicates whether the current instance and a specified + /// object have the same value. + /// + /// The object to compare. + /// + /// Returns true if the obj parameter is a math.AngleSingle object or a type + /// capable of implicit conversion to a math.AngleSingle value, and + /// its value is equal to the value of the current math.Angle + /// object; otherwise, false. + /// + public override bool Equals(object obj) + { + return (obj is AngleSingle) && (this == (AngleSingle)obj); + } + } +} diff --git a/math/AngleType.cs b/math/AngleType.cs new file mode 100644 index 0000000..7c04fc6 --- /dev/null +++ b/math/AngleType.cs @@ -0,0 +1,56 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +namespace math +{ + /// + /// Describes the type of angle. + /// + public enum AngleType + { + /// + /// Specifies an angle measurement in revolutions. + /// + Revolution, + + /// + /// Specifies an angle measurement in degrees. + /// + Degree, + + /// + /// Specifies an angle measurement in radians. + /// + Radian, + + /// + /// Specifies an angle measurement in gradians. + /// + Gradian, + } +} diff --git a/math/BoundingBox.cs b/math/BoundingBox.cs new file mode 100644 index 0000000..e24b32c --- /dev/null +++ b/math/BoundingBox.cs @@ -0,0 +1,466 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents an axis-aligned bounding box in three dimensional space. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct BoundingBox : IEquatable, IFormattable + { + /// + /// A which represents an empty space. + /// + public static readonly BoundingBox Empty = new BoundingBox(new Vector3(float.MaxValue), new Vector3(float.MinValue)); + + /// + /// The minimum point of the box. + /// + public Vector3 Minimum; + + /// + /// The maximum point of the box. + /// + public Vector3 Maximum; + + /// + /// Initializes a new instance of the struct. + /// + /// The minimum vertex of the bounding box. + /// The maximum vertex of the bounding box. + public BoundingBox(Vector3 minimum, Vector3 maximum) + { + this.Minimum = minimum; + this.Maximum = maximum; + } + + /// + /// Gets the center of this bouding box. + /// + public Vector3 Center + { + get { return (Minimum + Maximum) / 2; } + } + + /// + /// Gets the extent of this bouding box. + /// + public Vector3 Extent + { + get { return (Maximum - Minimum) / 2; } + } + + /// + /// Retrieves the eight corners of the bounding box. + /// + /// An array of points representing the eight corners of the bounding box. + public Vector3[] GetCorners() + { + Vector3[] results = new Vector3[8]; + results[0] = new Vector3(Minimum.X, Maximum.Y, Maximum.Z); + results[1] = new Vector3(Maximum.X, Maximum.Y, Maximum.Z); + results[2] = new Vector3(Maximum.X, Minimum.Y, Maximum.Z); + results[3] = new Vector3(Minimum.X, Minimum.Y, Maximum.Z); + results[4] = new Vector3(Minimum.X, Maximum.Y, Minimum.Z); + results[5] = new Vector3(Maximum.X, Maximum.Y, Minimum.Z); + results[6] = new Vector3(Maximum.X, Minimum.Y, Minimum.Z); + results[7] = new Vector3(Minimum.X, Minimum.Y, Minimum.Z); + return results; + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray) + { + float distance; + return CollisionHelper.RayIntersectsBox(ref ray, ref this, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray, out float distance) + { + return CollisionHelper.RayIntersectsBox(ref ray, ref this, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray, out Vector3 point) + { + return CollisionHelper.RayIntersectsBox(ref ray, ref this, out point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The plane to test. + /// Whether the two objects intersected. + public PlaneIntersectionType Intersects(ref Plane plane) + { + return CollisionHelper.PlaneIntersectsBox(ref plane, ref this); + } + + /* This implentation is wrong + /// + /// Determines if there is an intersection between the current object and a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// Whether the two objects intersected. + public bool Intersects(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + return Collision.BoxIntersectsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3); + } + */ + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The box to test. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingBox box) + { + return CollisionHelper.BoxIntersectsBox(ref this, ref box); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The sphere to test. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingSphere sphere) + { + return CollisionHelper.BoxIntersectsSphere(ref this, ref sphere); + } + + /// + /// Determines whether the current objects contains a point. + /// + /// The point to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref Vector3 point) + { + return CollisionHelper.BoxContainsPoint(ref this, ref point); + } + + /* This implentation is wrong + /// + /// Determines whether the current objects contains a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + return Collision.BoxContainsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3); + } + */ + + /// + /// Determines whether the current objects contains a . + /// + /// The box to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref BoundingBox box) + { + return CollisionHelper.BoxContainsBox(ref this, ref box); + } + + /// + /// Determines whether the current objects contains a . + /// + /// The sphere to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref BoundingSphere sphere) + { + return CollisionHelper.BoxContainsSphere(ref this, ref sphere); + } + + /// + /// Constructs a that fully contains the given points. + /// + /// The points that will be contained by the box. + /// When the method completes, contains the newly constructed bounding box. + /// Thrown when is null. + public static void FromPoints(Vector3[] points, out BoundingBox result) + { + if (points == null) + throw new ArgumentNullException("points"); + + Vector3 min = new Vector3(float.MaxValue); + Vector3 max = new Vector3(float.MinValue); + + for (int i = 0; i < points.Length; ++i) + { + Vector3.Min(ref min, ref points[i], out min); + Vector3.Max(ref max, ref points[i], out max); + } + + result = new BoundingBox(min, max); + } + + /// + /// Constructs a that fully contains the given points. + /// + /// The points that will be contained by the box. + /// The newly constructed bounding box. + /// Thrown when is null. + public static BoundingBox FromPoints(Vector3[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + Vector3 min = new Vector3(float.MaxValue); + Vector3 max = new Vector3(float.MinValue); + + for (int i = 0; i < points.Length; ++i) + { + Vector3.Min(ref min, ref points[i], out min); + Vector3.Max(ref max, ref points[i], out max); + } + + return new BoundingBox(min, max); + } + + /// + /// Constructs a from a given sphere. + /// + /// The sphere that will designate the extents of the box. + /// When the method completes, contains the newly constructed bounding box. + public static void FromSphere(ref BoundingSphere sphere, out BoundingBox result) + { + result.Minimum = new Vector3(sphere.Center.X - sphere.Radius, sphere.Center.Y - sphere.Radius, sphere.Center.Z - sphere.Radius); + result.Maximum = new Vector3(sphere.Center.X + sphere.Radius, sphere.Center.Y + sphere.Radius, sphere.Center.Z + sphere.Radius); + } + + /// + /// Constructs a from a given sphere. + /// + /// The sphere that will designate the extents of the box. + /// The newly constructed bounding box. + public static BoundingBox FromSphere(BoundingSphere sphere) + { + BoundingBox box; + box.Minimum = new Vector3(sphere.Center.X - sphere.Radius, sphere.Center.Y - sphere.Radius, sphere.Center.Z - sphere.Radius); + box.Maximum = new Vector3(sphere.Center.X + sphere.Radius, sphere.Center.Y + sphere.Radius, sphere.Center.Z + sphere.Radius); + return box; + } + + /// + /// Transform a bounding box. + /// + /// The original bounding box. + /// The transform to apply to the bounding box. + /// The transformed bounding box. + public static void Transform(ref BoundingBox value, ref Matrix transform, out BoundingBox result) + { + var boundingBox = new BoundingBoxExt(value); + boundingBox.Transform(transform); + result = (BoundingBox)boundingBox; + } + + /// + /// Constructs a that is as large enough to contains the bounding box and the given point. + /// + /// The box to merge. + /// The point to merge. + /// When the method completes, contains the newly constructed bounding box. + public static void Merge(ref BoundingBox value1, ref Vector3 value2, out BoundingBox result) + { + Vector3.Min(ref value1.Minimum, ref value2, out result.Minimum); + Vector3.Max(ref value1.Maximum, ref value2, out result.Maximum); + } + + /// + /// Constructs a that is as large as the total combined area of the two specified boxes. + /// + /// The first box to merge. + /// The second box to merge. + /// When the method completes, contains the newly constructed bounding box. + public static void Merge(ref BoundingBox value1, ref BoundingBox value2, out BoundingBox result) + { + Vector3.Min(ref value1.Minimum, ref value2.Minimum, out result.Minimum); + Vector3.Max(ref value1.Maximum, ref value2.Maximum, out result.Maximum); + } + + /// + /// Constructs a that is as large as the total combined area of the two specified boxes. + /// + /// The first box to merge. + /// The second box to merge. + /// The newly constructed bounding box. + public static BoundingBox Merge(BoundingBox value1, BoundingBox value2) + { + BoundingBox box; + Vector3.Min(ref value1.Minimum, ref value2.Minimum, out box.Minimum); + Vector3.Max(ref value1.Maximum, ref value2.Maximum, out box.Maximum); + return box; + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(BoundingBox left, BoundingBox right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(BoundingBox left, BoundingBox right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "Minimum:{0} Maximum:{1}", Minimum.ToString(), Maximum.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "Minimum:{0} Maximum:{1}", Minimum.ToString(format, CultureInfo.CurrentCulture), + Maximum.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "Minimum:{0} Maximum:{1}", Minimum.ToString(), Maximum.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "Minimum:{0} Maximum:{1}", Minimum.ToString(format, formatProvider), + Maximum.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return Minimum.GetHashCode() + Maximum.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(BoundingBox value) + { + return Minimum == value.Minimum && Maximum == value.Maximum; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((BoundingBox)value); + } + } +} diff --git a/math/BoundingBoxExt.cs b/math/BoundingBoxExt.cs new file mode 100644 index 0000000..cf41e4b --- /dev/null +++ b/math/BoundingBoxExt.cs @@ -0,0 +1,186 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents an axis-aligned bounding box in three dimensional space that store only the Center and Extent. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct BoundingBoxExt : IEquatable + { + /// + /// A which represents an empty space. + /// + public static readonly BoundingBoxExt Empty = new BoundingBoxExt(BoundingBox.Empty); + + /// + /// The center of this bounding box. + /// + public Vector3 Center; + + /// + /// The extent of this bounding box. + /// + public Vector3 Extent; + + /// + /// Initializes a new instance of the struct. + /// + /// The box. + public BoundingBoxExt(BoundingBox box) + { + this.Center = box.Center; + this.Extent = box.Extent; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The minimum vertex of the bounding box. + /// The maximum vertex of the bounding box. + public BoundingBoxExt(Vector3 minimum, Vector3 maximum) + { + this.Center = (minimum + maximum) / 2; + this.Extent = (maximum - minimum) / 2; + } + + /// + /// Gets the minimum. + /// + /// The minimum. + public Vector3 Minimum + { + get + { + return Center - Extent; + } + } + + /// + /// Gets the maximum. + /// + /// The maximum. + public Vector3 Maximum + { + get + { + return Center + Extent; + } + } + + /// + /// Transform this Bounding box + /// + /// The transform to apply to the bounding box. + public void Transform(Matrix world) + { + // http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/ + // Compute transformed AABB (by world) + var center = Center; + var extent = Extent; + + Vector3.TransformCoordinate(ref center, ref world, out Center); + + // Update world matrix into absolute form + unsafe + { + // Perform an abs on the matrix + var matrixData = (float*)&world; + for (int j = 0; j < 16; ++j) + { + //*matrixData &= 0x7FFFFFFF; + *matrixData = Math.Abs(*matrixData); + ++matrixData; + } + } + + Vector3.TransformNormal(ref extent, ref world, out Extent); + } + + /// + /// Constructs a that is as large as the total combined area of the two specified boxes. + /// + /// The first box to merge. + /// The second box to merge. + /// When the method completes, contains the newly constructed bounding box. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Merge(ref BoundingBoxExt value1, ref BoundingBoxExt value2, out BoundingBoxExt result) + { + var maximum = Vector3.Max(value1.Maximum, value2.Maximum); + var minimum = Vector3.Min(value1.Minimum, value2.Minimum); + + result.Center = (minimum + maximum) / 2; + result.Extent = (maximum - minimum) / 2; + } + + /// + public bool Equals(BoundingBoxExt other) + { + return Center.Equals(other.Center) && Extent.Equals(other.Extent); + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is BoundingBoxExt && Equals((BoundingBoxExt)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + return (Center.GetHashCode() * 397) ^ Extent.GetHashCode(); + } + } + + /// + /// Implements the ==. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator ==(BoundingBoxExt left, BoundingBoxExt right) + { + return left.Equals(right); + } + + /// + /// Implements the !=. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator !=(BoundingBoxExt left, BoundingBoxExt right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The bb ext. + /// The result of the conversion. + public static explicit operator BoundingBox(BoundingBoxExt bbExt) + { + return new BoundingBox(bbExt.Minimum, bbExt.Maximum); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The bounding box. + /// The result of the conversion. + public static explicit operator BoundingBoxExt(BoundingBox boundingBox) + { + return new BoundingBoxExt(boundingBox); + } + } +} diff --git a/math/BoundingFrustum.cs b/math/BoundingFrustum.cs new file mode 100644 index 0000000..506c957 --- /dev/null +++ b/math/BoundingFrustum.cs @@ -0,0 +1,103 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System.Runtime.CompilerServices; + +namespace math +{ + /// + /// A bounding frustum. + /// + public struct BoundingFrustum + { + /// + /// The left plane of this frustum. + /// + public Plane LeftPlane; + + /// + /// The right plane of this frustum. + /// + public Plane RightPlane; + + /// + /// The top plane of this frustum. + /// + public Plane TopPlane; + + /// + /// The bottom plane of this frustum. + /// + public Plane BottomPlane; + + /// + /// The near plane of this frustum. + /// + public Plane NearPlane; + + /// + /// The far plane of this frustum. + /// + public Plane FarPlane; + + /// + /// Initializes a new instance of the struct from a matrix view-projection. + /// + /// The matrix view projection. + public BoundingFrustum(ref Matrix matrix) + { + // Left + LeftPlane = Plane.Normalize(new Plane( + matrix.M14 + matrix.M11, + matrix.M24 + matrix.M21, + matrix.M34 + matrix.M31, + matrix.M44 + matrix.M41)); + + // Right + RightPlane = Plane.Normalize(new Plane( + matrix.M14 - matrix.M11, + matrix.M24 - matrix.M21, + matrix.M34 - matrix.M31, + matrix.M44 - matrix.M41)); + + // Top + TopPlane = Plane.Normalize(new Plane( + matrix.M14 - matrix.M12, + matrix.M24 - matrix.M22, + matrix.M34 - matrix.M32, + matrix.M44 - matrix.M42)); + + // Bottom + BottomPlane = Plane.Normalize(new Plane( + matrix.M14 + matrix.M12, + matrix.M24 + matrix.M22, + matrix.M34 + matrix.M32, + matrix.M44 + matrix.M42)); + + // Near + NearPlane = Plane.Normalize(new Plane( + matrix.M13, + matrix.M23, + matrix.M33, + matrix.M43)); + + // Far + FarPlane = Plane.Normalize(new Plane( + matrix.M14 - matrix.M13, + matrix.M24 - matrix.M23, + matrix.M34 - matrix.M33, + matrix.M44 - matrix.M43)); + } + + /// + /// Check whether this frustum contains the specified . + /// + /// The bounding box. + /// true if this frustum contains the specified bounding box. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(ref BoundingBoxExt boundingBoxExt) + { + return CollisionHelper.FrustumContainsBox(ref this, ref boundingBoxExt); + } + } +} diff --git a/math/BoundingSphere.cs b/math/BoundingSphere.cs new file mode 100644 index 0000000..36496a8 --- /dev/null +++ b/math/BoundingSphere.cs @@ -0,0 +1,539 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a bounding sphere in three dimensional space. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct BoundingSphere : IEquatable, IFormattable + { + /// + /// An empty bounding sphere (Center = 0 and Radius = 0). + /// + public static readonly BoundingSphere Empty = new BoundingSphere(); + + /// + /// The center of the sphere in three dimensional space. + /// + public Vector3 Center; + + /// + /// The radious of the sphere. + /// + public float Radius; + + /// + /// Initializes a new instance of the struct. + /// + /// The center of the sphere in three dimensional space. + /// The radius of the sphere. + public BoundingSphere(Vector3 center, float radius) + { + this.Center = center; + this.Radius = radius; + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray) + { + float distance; + return CollisionHelper.RayIntersectsSphere(ref ray, ref this, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray, out float distance) + { + return CollisionHelper.RayIntersectsSphere(ref ray, ref this, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray, out Vector3 point) + { + return CollisionHelper.RayIntersectsSphere(ref ray, ref this, out point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The plane to test. + /// Whether the two objects intersected. + public PlaneIntersectionType Intersects(ref Plane plane) + { + return CollisionHelper.PlaneIntersectsSphere(ref plane, ref this); + } + + /// + /// Determines if there is an intersection between the current object and a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// Whether the two objects intersected. + public bool Intersects(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + return CollisionHelper.SphereIntersectsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The box to test. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingBox box) + { + return CollisionHelper.BoxIntersectsSphere(ref box, ref this); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The sphere to test. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingSphere sphere) + { + return CollisionHelper.SphereIntersectsSphere(ref this, ref sphere); + } + + /// + /// Determines whether the current objects contains a point. + /// + /// The point to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref Vector3 point) + { + return CollisionHelper.SphereContainsPoint(ref this, ref point); + } + + /// + /// Determines whether the current objects contains a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + return CollisionHelper.SphereContainsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3); + } + + /// + /// Determines whether the current objects contains a . + /// + /// The box to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref BoundingBox box) + { + return CollisionHelper.SphereContainsBox(ref this, ref box); + } + + /// + /// Determines whether the current objects contains a . + /// + /// The sphere to test. + /// The type of containment the two objects have. + public ContainmentType Contains(ref BoundingSphere sphere) + { + return CollisionHelper.SphereContainsSphere(ref this, ref sphere); + } + + /// + /// Constructs a that fully contains the given points. + /// + /// The points that will be contained by the sphere. + /// When the method completes, contains the newly constructed bounding sphere. + public static unsafe void FromPoints(Vector3[] points, out BoundingSphere result) + { + if (points == null) throw new ArgumentNullException("points"); + fixed (void* pointsPtr = points) + { + FromPoints((IntPtr)pointsPtr, 0, points.Length, Utilities.SizeOf(), out result); + } + } + + /// + /// Constructs a that fully contains the given unmanaged points. + /// + /// A pointer to of vertices containing points. + /// The point offset in bytes starting from the vertex structure. + /// The verterx vertexCount. + /// The vertex stride (size of vertex). + /// When the method completes, contains the newly constructed bounding sphere. + public static unsafe void FromPoints(IntPtr vertexBufferPtr, int vertexPositionOffsetInBytes, int vertexCount, int vertexStride, out BoundingSphere result) + { + if (vertexBufferPtr == IntPtr.Zero) + { + throw new ArgumentNullException("vertexBufferPtr"); + } + + var startPoint = (byte*)vertexBufferPtr + vertexPositionOffsetInBytes; + + //Find the center of all points. + Vector3 center = Vector3.Zero; + var nextPoint = startPoint; + for (int i = 0; i < vertexCount; ++i) + { + Vector3.Add(ref *(Vector3*)nextPoint, ref center, out center); + nextPoint += vertexStride; + } + + //This is the center of our sphere. + center /= (float)vertexCount; + + //Find the radius of the sphere + float radius = 0f; + nextPoint = startPoint; + for (int i = 0; i < vertexCount; ++i) + { + //We are doing a relative distance comparasin to find the maximum distance + //from the center of our sphere. + float distance; + Vector3.DistanceSquared(ref center, ref *(Vector3*)nextPoint, out distance); + + if (distance > radius) + radius = distance; + nextPoint += vertexStride; + } + + //Find the real distance from the DistanceSquared. + radius = (float)Math.Sqrt(radius); + + //Construct the sphere. + result.Center = center; + result.Radius = radius; + } + + /// + /// Constructs a that fully contains the given points. + /// + /// The points that will be contained by the sphere. + /// The newly constructed bounding sphere. + public static BoundingSphere FromPoints(Vector3[] points) + { + BoundingSphere result; + FromPoints(points, out result); + return result; + } + + /// + /// Constructs a from a given box. + /// + /// The box that will designate the extents of the sphere. + /// When the method completes, the newly constructed bounding sphere. + public static void FromBox(ref BoundingBox box, out BoundingSphere result) + { + Vector3.Lerp(ref box.Minimum, ref box.Maximum, 0.5f, out result.Center); + + float x = box.Minimum.X - box.Maximum.X; + float y = box.Minimum.Y - box.Maximum.Y; + float z = box.Minimum.Z - box.Maximum.Z; + + float distance = (float)(Math.Sqrt((x * x) + (y * y) + (z * z))); + result.Radius = distance * 0.5f; + } + + /// + /// Constructs a from a given box. + /// + /// The box that will designate the extents of the sphere. + /// The newly constructed bounding sphere. + public static BoundingSphere FromBox(BoundingBox box) + { + BoundingSphere result; + FromBox(ref box, out result); + return result; + } + + /// + /// Transforms a bounding bounding sphere, yielding the bounding sphere of all points contained by the original one, transformed by the specified transform. + /// + /// The original bounding sphere. + /// The transform to apply to the bounding sphere. + /// The transformed bounding sphere. + public static void Transform(ref BoundingSphere value, ref Matrix transform, out BoundingSphere result) + { + Vector3.TransformCoordinate(ref value.Center, ref transform, out result.Center); + + var majorAxisLengthSquared = Math.Max( + (transform.M11 * transform.M11) + (transform.M12 * transform.M12) + (transform.M13 * transform.M13), Math.Max( + (transform.M21 * transform.M21) + (transform.M22 * transform.M22) + (transform.M23 * transform.M23), + (transform.M31 * transform.M31) + (transform.M32 * transform.M32) + (transform.M33 * transform.M33))); + + result.Radius = value.Radius * (float)Math.Sqrt(majorAxisLengthSquared); + } + + /// + /// Constructs a that is the as large as the total combined area of the two specified spheres. + /// + /// The first sphere to merge. + /// The second sphere to merge. + /// When the method completes, contains the newly constructed bounding sphere. + public static void Merge(ref BoundingSphere value1, ref BoundingSphere value2, out BoundingSphere result) + { + // Pre-exit if one of the bounding sphere by assuming that a merge with an empty sphere is equivalent at taking the non-empty sphere + if (value1 == Empty) + { + result = value2; + return; + } + + if (value2 == Empty) + { + result = value1; + return; + } + + Vector3 difference = value2.Center - value1.Center; + + float length = difference.Length(); + float radius = value1.Radius; + float radius2 = value2.Radius; + + if (radius + radius2 >= length) + { + if (radius - radius2 >= length) + { + result = value1; + return; + } + + if (radius2 - radius >= length) + { + result = value2; + return; + } + } + + Vector3 vector = difference * (1.0f / length); + float min = Math.Min(-radius, length - radius2); + float max = (Math.Max(radius, length + radius2) - min) * 0.5f; + + result.Center = value1.Center + vector * (max + min); + result.Radius = max; + } + + /// + /// Constructs a that is the as large as the total combined area of the two specified spheres. + /// + /// The first sphere to merge. + /// The second sphere to merge. + /// The newly constructed bounding sphere. + public static BoundingSphere Merge(BoundingSphere value1, BoundingSphere value2) + { + BoundingSphere result; + Merge(ref value1, ref value2, out result); + return result; + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(BoundingSphere left, BoundingSphere right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(BoundingSphere left, BoundingSphere right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "Center:{0} Radius:{1}", Center.ToString(), Radius.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "Center:{0} Radius:{1}", Center.ToString(format, CultureInfo.CurrentCulture), + Radius.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "Center:{0} Radius:{1}", Center.ToString(), Radius.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "Center:{0} Radius:{1}", Center.ToString(format, formatProvider), + Radius.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return Center.GetHashCode() + Radius.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(BoundingSphere value) + { + return Center == value.Center && Radius == value.Radius; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((BoundingSphere)value); + } + +#if SlimDX1xInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator SlimDX.BoundingSphere(BoundingSphere value) + { + return new SlimDX.BoundingSphere(value.Center, value.Radius); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator BoundingSphere(SlimDX.BoundingSphere value) + { + return new BoundingSphere(value.Center, value.Radius); + } +#endif + +#if SlimDX1xInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.BoundingSphere(BoundingSphere value) + { + return new Microsoft.Xna.Framework.BoundingSphere(value.Center, value.Radius); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator BoundingSphere(Microsoft.Xna.Framework.BoundingSphere value) + { + return new BoundingSphere(value.Center, value.Radius); + } +#endif + } +} diff --git a/math/CollisionHelper.cs b/math/CollisionHelper.cs new file mode 100644 index 0000000..92742cf --- /dev/null +++ b/math/CollisionHelper.cs @@ -0,0 +1,1551 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; + +namespace math +{ + /* + * This class is organized so that the least complex objects come first so that the least + * complex objects will have the most methods in most cases. Note that not all shapes exist + * at this time and not all shapes have a corresponding struct. Only the objects that have + * a corresponding struct should come first in naming and in parameter order. The order of + * complexity is as follows: + * + * 1. Point + * 2. Ray + * 3. Segment + * 4. Plane + * 5. Triangle + * 6. Polygon + * 7. Box + * 8. Sphere + * 9. Ellipsoid + * 10. Cylinder + * 11. Cone + * 12. Capsule + * 13. Torus + * 14. Polyhedron + * 15. Frustum + */ + + /// + /// Contains static methods to help in determining intersections, containment, etc. + /// + public static class CollisionHelper + { + /// + /// Determines the closest point between a point and a triangle. + /// + /// The point to test. + /// The first vertex to test. + /// The second vertex to test. + /// The third vertex to test. + /// When the method completes, contains the closest point between the two objects. + public static void ClosestPointPointTriangle(ref Vector3 point, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out Vector3 result) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 136 + + //Check if P in vertex region outside A + Vector3 ab = vertex2 - vertex1; + Vector3 ac = vertex3 - vertex1; + Vector3 ap = point - vertex1; + + float d1 = Vector3.Dot(ab, ap); + float d2 = Vector3.Dot(ac, ap); + if (d1 <= 0.0f && d2 <= 0.0f) + { + result = vertex1; //Barycentric coordinates (1,0,0) + return; + } + + //Check if P in vertex region outside B + Vector3 bp = point - vertex2; + float d3 = Vector3.Dot(ab, bp); + float d4 = Vector3.Dot(ac, bp); + if (d3 >= 0.0f && d4 <= d3) + { + result = vertex2; // barycentric coordinates (0,1,0) + return; + } + + //Check if P in edge region of AB, if so return projection of P onto AB + float vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) + { + float v = d1 / (d1 - d3); + result = vertex1 + v * ab; //Barycentric coordinates (1-v,v,0) + return; + } + + //Check if P in vertex region outside C + Vector3 cp = point - vertex3; + float d5 = Vector3.Dot(ab, cp); + float d6 = Vector3.Dot(ac, cp); + if (d6 >= 0.0f && d5 <= d6) + { + result = vertex3; //Barycentric coordinates (0,0,1) + return; + } + + //Check if P in edge region of AC, if so return projection of P onto AC + float vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) + { + float w = d2 / (d2 - d6); + result = vertex1 + w * ac; //Barycentric coordinates (1-w,0,w) + return; + } + + //Check if P in edge region of BC, if so return projection of P onto BC + float va = d3 * d6 - d5 * d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) + { + float w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + result = vertex2 + w * (vertex3 - vertex2); //Barycentric coordinates (0,1-w,w) + return; + } + + //P inside face region. Compute Q through its barycentric coordinates (u,v,w) + float denom = 1.0f / (va + vb + vc); + float v2 = vb * denom; + float w2 = vc * denom; + result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w + } + + /// + /// Determines the closest point between a and a point. + /// + /// The plane to test. + /// The point to test. + /// When the method completes, contains the closest point between the two objects. + public static void ClosestPointPlanePoint(ref Plane plane, ref Vector3 point, out Vector3 result) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 126 + + float dot; + Vector3.Dot(ref plane.Normal, ref point, out dot); + float t = dot - plane.D; + + result = point - (t * plane.Normal); + } + + /// + /// Determines the closest point between a and a point. + /// + /// The box to test. + /// The point to test. + /// When the method completes, contains the closest point between the two objects. + public static void ClosestPointBoxPoint(ref BoundingBox box, ref Vector3 point, out Vector3 result) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 130 + + Vector3 temp; + Vector3.Max(ref point, ref box.Minimum, out temp); + Vector3.Min(ref temp, ref box.Maximum, out result); + } + + /// + /// Determines the closest point between a and a point. + /// + /// The bounding sphere. + /// The point to test. + /// When the method completes, contains the closest point between the two objects; + /// or, if the point is directly in the center of the sphere, contains . + public static void ClosestPointSpherePoint(ref BoundingSphere sphere, ref Vector3 point, out Vector3 result) + { + //Source: Jorgy343 + //Reference: None + + //Get the unit direction from the sphere's center to the point. + Vector3.Subtract(ref point, ref sphere.Center, out result); + result.Normalize(); + + //Multiply the unit direction by the sphere's radius to get a vector + //the length of the sphere. + result *= sphere.Radius; + + //Add the sphere's center to the direction to get a point on the sphere. + result += sphere.Center; + } + + /// + /// Determines the closest point between a and a . + /// + /// The first sphere to test. + /// The second sphere to test. + /// When the method completes, contains the closest point between the two objects; + /// or, if the point is directly in the center of the sphere, contains . + /// + /// If the two spheres are overlapping, but not directly ontop of each other, the closest point + /// is the 'closest' point of intersection. This can also be considered is the deepest point of + /// intersection. + /// + public static void ClosestPointSphereSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2, out Vector3 result) + { + //Source: Jorgy343 + //Reference: None + + //Get the unit direction from the first sphere's center to the second sphere's center. + Vector3.Subtract(ref sphere2.Center, ref sphere1.Center, out result); + result.Normalize(); + + //Multiply the unit direction by the first sphere's radius to get a vector + //the length of the first sphere. + result *= sphere1.Radius; + + //Add the first sphere's center to the direction to get a point on the first sphere. + result += sphere1.Center; + } + + /// + /// Determines the distance between a and a point. + /// + /// The plane to test. + /// The point to test. + /// The distance between the two objects. + public static float DistancePlanePoint(ref Plane plane, ref Vector3 point) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 127 + + float dot; + Vector3.Dot(ref plane.Normal, ref point, out dot); + return dot - plane.D; + } + + /// + /// Determines the distance between a and a point. + /// + /// The box to test. + /// The point to test. + /// The distance between the two objects. + public static float DistanceBoxPoint(ref BoundingBox box, ref Vector3 point) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 131 + + float distance = 0f; + + if (point.X < box.Minimum.X) + distance += (box.Minimum.X - point.X) * (box.Minimum.X - point.X); + if (point.X > box.Maximum.X) + distance += (point.X - box.Maximum.X) * (point.X - box.Maximum.X); + + if (point.Y < box.Minimum.Y) + distance += (box.Minimum.Y - point.Y) * (box.Minimum.Y - point.Y); + if (point.Y > box.Maximum.Y) + distance += (point.Y - box.Maximum.Y) * (point.Y - box.Maximum.Y); + + if (point.Z < box.Minimum.Z) + distance += (box.Minimum.Z - point.Z) * (box.Minimum.Z - point.Z); + if (point.Z > box.Maximum.Z) + distance += (point.Z - box.Maximum.Z) * (point.Z - box.Maximum.Z); + + return (float)Math.Sqrt(distance); + } + + /// + /// Determines the distance between a and a . + /// + /// The first box to test. + /// The second box to test. + /// The distance between the two objects. + public static float DistanceBoxBox(ref BoundingBox box1, ref BoundingBox box2) + { + //Source: + //Reference: + + float distance = 0f; + + //Distance for X. + if (box1.Minimum.X > box2.Maximum.X) + { + float delta = box2.Maximum.X - box1.Minimum.X; + distance += delta * delta; + } + else if (box2.Minimum.X > box1.Maximum.X) + { + float delta = box1.Maximum.X - box2.Minimum.X; + distance += delta * delta; + } + + //Distance for Y. + if (box1.Minimum.Y > box2.Maximum.Y) + { + float delta = box2.Maximum.Y - box1.Minimum.Y; + distance += delta * delta; + } + else if (box2.Minimum.Y > box1.Maximum.Y) + { + float delta = box1.Maximum.Y - box2.Minimum.Y; + distance += delta * delta; + } + + //Distance for Z. + if (box1.Minimum.Z > box2.Maximum.Z) + { + float delta = box2.Maximum.Z - box1.Minimum.Z; + distance += delta * delta; + } + else if (box2.Minimum.Z > box1.Maximum.Z) + { + float delta = box1.Maximum.Z - box2.Minimum.Z; + distance += delta * delta; + } + + return (float)Math.Sqrt(distance); + } + + /// + /// Determines the distance between a and a point. + /// + /// The sphere to test. + /// The point to test. + /// The distance between the two objects. + public static float DistanceSpherePoint(ref BoundingSphere sphere, ref Vector3 point) + { + //Source: Jorgy343 + //Reference: None + + float distance; + Vector3.Distance(ref sphere.Center, ref point, out distance); + distance -= sphere.Radius; + + return Math.Max(distance, 0f); + } + + /// + /// Determines the distance between a and a . + /// + /// The first sphere to test. + /// The second sphere to test. + /// The distance between the two objects. + public static float DistanceSphereSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2) + { + //Source: Jorgy343 + //Reference: None + + float distance; + Vector3.Distance(ref sphere1.Center, ref sphere2.Center, out distance); + distance -= sphere1.Radius + sphere2.Radius; + + return Math.Max(distance, 0f); + } + + /// + /// Determines whether there is an intersection between a and a point. + /// + /// The ray to test. + /// The point to test. + /// Whether the two objects intersect. + public static bool RayIntersectsPoint(ref Ray ray, ref Vector3 point) + { + //Source: RayIntersectsSphere + //Reference: None + + Vector3 m; + Vector3.Subtract(ref ray.Position, ref point, out m); + + //Same thing as RayIntersectsSphere except that the radius of the sphere (point) + //is the epsilon for zero. + float b = Vector3.Dot(m, ray.Direction); + float c = Vector3.Dot(m, m) - MathUtil.ZeroTolerance; + + if (c > 0f && b > 0f) + return false; + + float discriminant = b * b - c; + + if (discriminant < 0f) + return false; + + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The first ray to test. + /// The second ray to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersect. + /// + /// This method performs a ray vs ray intersection test based on the following formula + /// from Goldman. + /// s = det([o_2 - o_1, d_2, d_1 x d_2]) / ||d_1 x d_2||^2 + /// t = det([o_2 - o_1, d_1, d_1 x d_2]) / ||d_1 x d_2||^2 + /// Where o_1 is the position of the first ray, o_2 is the position of the second ray, + /// d_1 is the normalized direction of the first ray, d_2 is the normalized direction + /// of the second ray, det denotes the determinant of a matrix, x denotes the cross + /// product, [ ] denotes a matrix, and || || denotes the length or magnitude of a vector. + /// + public static bool RayIntersectsRay(ref Ray ray1, ref Ray ray2, out Vector3 point) + { + //Source: Real-Time Rendering, Third Edition + //Reference: Page 780 + + Vector3 cross; + + Vector3.Cross(ref ray1.Direction, ref ray2.Direction, out cross); + float denominator = cross.Length(); + + //Lines are parallel. + if (Math.Abs(denominator) < MathUtil.ZeroTolerance) + { + //Lines are parallel and on top of each other. + if (Math.Abs(ray2.Position.X - ray1.Position.X) < MathUtil.ZeroTolerance && + Math.Abs(ray2.Position.Y - ray1.Position.Y) < MathUtil.ZeroTolerance && + Math.Abs(ray2.Position.Z - ray1.Position.Z) < MathUtil.ZeroTolerance) + { + point = Vector3.Zero; + return true; + } + } + + denominator = denominator * denominator; + + //3x3 matrix for the first ray. + float m11 = ray2.Position.X - ray1.Position.X; + float m12 = ray2.Position.Y - ray1.Position.Y; + float m13 = ray2.Position.Z - ray1.Position.Z; + float m21 = ray2.Direction.X; + float m22 = ray2.Direction.Y; + float m23 = ray2.Direction.Z; + float m31 = cross.X; + float m32 = cross.Y; + float m33 = cross.Z; + + //Determinant of first matrix. + float dets = + m11 * m22 * m33 + + m12 * m23 * m31 + + m13 * m21 * m32 - + m11 * m23 * m32 - + m12 * m21 * m33 - + m13 * m22 * m31; + + //3x3 matrix for the second ray. + m21 = ray1.Direction.X; + m22 = ray1.Direction.Y; + m23 = ray1.Direction.Z; + + //Determinant of the second matrix. + float dett = + m11 * m22 * m33 + + m12 * m23 * m31 + + m13 * m21 * m32 - + m11 * m23 * m32 - + m12 * m21 * m33 - + m13 * m22 * m31; + + //t values of the point of intersection. + float s = dets / denominator; + float t = dett / denominator; + + //The points of intersection. + Vector3 point1 = ray1.Position + (s * ray1.Direction); + Vector3 point2 = ray2.Position + (t * ray2.Direction); + + //If the points are not equal, no intersection has occurred. + if (Math.Abs(point2.X - point1.X) > MathUtil.ZeroTolerance || + Math.Abs(point2.Y - point1.Y) > MathUtil.ZeroTolerance || + Math.Abs(point2.Z - point1.Z) > MathUtil.ZeroTolerance) + { + point = Vector3.Zero; + return false; + } + + point = point1; + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The ray to test. + /// The plane to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersect. + public static bool RayIntersectsPlane(ref Ray ray, ref Plane plane, out float distance) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 175 + + float direction; + Vector3.Dot(ref plane.Normal, ref ray.Direction, out direction); + + if (Math.Abs(direction) < MathUtil.ZeroTolerance) + { + distance = 0f; + return false; + } + + float position; + Vector3.Dot(ref plane.Normal, ref ray.Position, out position); + distance = (-plane.D - position) / direction; + + if (distance < 0f) + { + if (distance < -MathUtil.ZeroTolerance) + { + distance = 0; + return false; + } + + distance = 0f; + } + + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The ray to test. + /// The plane to test + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public static bool RayIntersectsPlane(ref Ray ray, ref Plane plane, out Vector3 point) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 175 + + float distance; + if (!RayIntersectsPlane(ref ray, ref plane, out distance)) + { + point = Vector3.Zero; + return false; + } + + point = ray.Position + (ray.Direction * distance); + return true; + } + + /// + /// Determines whether there is an intersection between a and a triangle. + /// + /// The ray to test. + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + /// + /// This method tests if the ray intersects either the front or back of the triangle. + /// If the ray is parallel to the triangle's plane, no intersection is assumed to have + /// happened. If the intersection of the ray and the triangle is behind the origin of + /// the ray, no intersection is assumed to have happened. In both cases of assumptions, + /// this method returns false. + /// + public static bool RayIntersectsTriangle(ref Ray ray, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out float distance) + { + //Source: Fast Minimum Storage Ray / Triangle Intersection + //Reference: http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf + + //Compute vectors along two edges of the triangle. + Vector3 edge1, edge2; + + //Edge 1 + edge1.X = vertex2.X - vertex1.X; + edge1.Y = vertex2.Y - vertex1.Y; + edge1.Z = vertex2.Z - vertex1.Z; + + //Edge2 + edge2.X = vertex3.X - vertex1.X; + edge2.Y = vertex3.Y - vertex1.Y; + edge2.Z = vertex3.Z - vertex1.Z; + + //Cross product of ray direction and edge2 - first part of determinant. + Vector3 directioncrossedge2; + directioncrossedge2.X = (ray.Direction.Y * edge2.Z) - (ray.Direction.Z * edge2.Y); + directioncrossedge2.Y = (ray.Direction.Z * edge2.X) - (ray.Direction.X * edge2.Z); + directioncrossedge2.Z = (ray.Direction.X * edge2.Y) - (ray.Direction.Y * edge2.X); + + //Compute the determinant. + float determinant; + //Dot product of edge1 and the first part of determinant. + determinant = (edge1.X * directioncrossedge2.X) + (edge1.Y * directioncrossedge2.Y) + (edge1.Z * directioncrossedge2.Z); + + //If the ray is parallel to the triangle plane, there is no collision. + //This also means that we are not culling, the ray may hit both the + //back and the front of the triangle. + if (determinant > -MathUtil.ZeroTolerance && determinant < MathUtil.ZeroTolerance) + { + distance = 0f; + return false; + } + + float inversedeterminant = 1.0f / determinant; + + //Calculate the U parameter of the intersection point. + Vector3 distanceVector; + distanceVector.X = ray.Position.X - vertex1.X; + distanceVector.Y = ray.Position.Y - vertex1.Y; + distanceVector.Z = ray.Position.Z - vertex1.Z; + + float triangleU; + triangleU = (distanceVector.X * directioncrossedge2.X) + (distanceVector.Y * directioncrossedge2.Y) + (distanceVector.Z * directioncrossedge2.Z); + triangleU *= inversedeterminant; + + //Make sure it is inside the triangle. + if (triangleU < 0f || triangleU > 1f) + { + distance = 0f; + return false; + } + + //Calculate the V parameter of the intersection point. + Vector3 distancecrossedge1; + distancecrossedge1.X = (distanceVector.Y * edge1.Z) - (distanceVector.Z * edge1.Y); + distancecrossedge1.Y = (distanceVector.Z * edge1.X) - (distanceVector.X * edge1.Z); + distancecrossedge1.Z = (distanceVector.X * edge1.Y) - (distanceVector.Y * edge1.X); + + float triangleV; + triangleV = ((ray.Direction.X * distancecrossedge1.X) + (ray.Direction.Y * distancecrossedge1.Y)) + (ray.Direction.Z * distancecrossedge1.Z); + triangleV *= inversedeterminant; + + //Make sure it is inside the triangle. + if (triangleV < 0f || triangleU + triangleV > 1f) + { + distance = 0f; + return false; + } + + //Compute the distance along the ray to the triangle. + float raydistance; + raydistance = (edge2.X * distancecrossedge1.X) + (edge2.Y * distancecrossedge1.Y) + (edge2.Z * distancecrossedge1.Z); + raydistance *= inversedeterminant; + + //Is the triangle behind the ray origin? + if (raydistance < 0f) + { + distance = 0f; + return false; + } + + distance = raydistance; + return true; + } + + /// + /// Determines whether there is an intersection between a and a triangle. + /// + /// The ray to test. + /// The first vertex of the triangle to test. + /// The second vertex of the triangle to test. + /// The third vertex of the triangle to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public static bool RayIntersectsTriangle(ref Ray ray, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out Vector3 point) + { + float distance; + if (!RayIntersectsTriangle(ref ray, ref vertex1, ref vertex2, ref vertex3, out distance)) + { + point = Vector3.Zero; + return false; + } + + point = ray.Position + (ray.Direction * distance); + return true; + } + + /// + /// Determines whether there is an intersection between a and a rectangle (2D). + /// + /// The ray to test + /// The world matrix applied on the rectangle + /// The size of the rectangle in 3D + /// The index of axis defining the normal of the rectangle in the world. This value should be 0, 1 or 2 + /// The position of the intersection point in the world + /// true if the ray and rectangle intersects. + public static bool RayIntersectsRectangle(ref Ray ray, ref Matrix rectangleWorldMatrix, ref Vector3 rectangleSize, int normalAxis, out Vector3 intersectionPoint) + { + bool intersects; + + int testAxis1; + int testAxis2; + switch (normalAxis) + { + case 0: + testAxis1 = 1; + testAxis2 = 2; + break; + case 1: + testAxis1 = 2; + testAxis2 = 0; + break; + case 2: + testAxis1 = 0; + testAxis2 = 1; + break; + default: + throw new ArgumentOutOfRangeException("normalAxis"); + } + + var rectanglePosition = new Vector3(rectangleWorldMatrix.M41, rectangleWorldMatrix.M42, rectangleWorldMatrix.M43); + + var normalRowStart = normalAxis << 2; + var plane = new Plane(rectanglePosition, new Vector3(rectangleWorldMatrix[normalRowStart], rectangleWorldMatrix[normalRowStart + 1], rectangleWorldMatrix[normalRowStart + 2])); + + // early exist the planes were parallels + if (!plane.Intersects(ref ray, out intersectionPoint)) + return false; + + // the position of the intersection point with respect to the rectangle center + var intersectionInRectangle = intersectionPoint - rectanglePosition; + + // optimization for the simple but very frequent case where the element is not rotated + if (rectangleWorldMatrix.M12 == 0 && rectangleWorldMatrix.M13 == 0 && + rectangleWorldMatrix.M21 == 0 && rectangleWorldMatrix.M23 == 0 && + rectangleWorldMatrix.M31 == 0 && rectangleWorldMatrix.M32 == 0) + { + var halfSize1 = Math.Abs(rectangleWorldMatrix[(testAxis1 << 2) + testAxis1] * rectangleSize[testAxis1] / 2f); + var halfSize2 = Math.Abs(rectangleWorldMatrix[(testAxis2 << 2) + testAxis2] * rectangleSize[testAxis2] / 2f); + + intersects = -halfSize1 <= intersectionInRectangle[testAxis1] && intersectionInRectangle[testAxis1] <= halfSize1 && + -halfSize2 <= intersectionInRectangle[testAxis2] && intersectionInRectangle[testAxis2] <= halfSize2; + } + // general case: decompose the rectangle into two triangles and check that all angles are less than 180 degrees in at least one of the triangles. + else + { + // find the most significant component of the plane normal + var normalTestIndex = 0; + for (int i = 1; i < 3; i++) + { + if (Math.Abs(plane.Normal[i]) > Math.Abs(plane.Normal[normalTestIndex])) + normalTestIndex = i; + } + var normalSign = Math.Sign(plane.Normal[normalTestIndex]); + + // the base vector + var base1 = rectangleSize[testAxis1] * new Vector3(rectangleWorldMatrix[(testAxis1 << 2)], rectangleWorldMatrix[(testAxis1 << 2) + 1], rectangleWorldMatrix[(testAxis1 << 2) + 2]) / 2; + var base2 = rectangleSize[testAxis2] * new Vector3(rectangleWorldMatrix[(testAxis2 << 2)], rectangleWorldMatrix[(testAxis2 << 2) + 1], rectangleWorldMatrix[(testAxis2 << 2) + 2]) / 2; + + // build the first triangle and perform the test + var v1 = -base1 - base2 - intersectionInRectangle; + var v2 = +base1 - base2 - intersectionInRectangle; + var v3 = +base1 + base2 - intersectionInRectangle; + + intersects = Math.Sign(Vector3.Cross(v1, v2)[normalTestIndex]) == normalSign && + Math.Sign(Vector3.Cross(v2, v3)[normalTestIndex]) == normalSign && + Math.Sign(Vector3.Cross(v3, v1)[normalTestIndex]) == normalSign; + + // early exit on success + if (intersects) + return true; + + // build second triangle and perform the test + v1 = -base1 - base2 - intersectionInRectangle; + v2 = +base1 + base2 - intersectionInRectangle; + v3 = -base1 + base2 - intersectionInRectangle; + + intersects = Math.Sign(Vector3.Cross(v1, v2)[normalTestIndex]) == normalSign && + Math.Sign(Vector3.Cross(v2, v3)[normalTestIndex]) == normalSign && + Math.Sign(Vector3.Cross(v3, v1)[normalTestIndex]) == normalSign; + } + + return intersects; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The ray to test. + /// The box to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public static bool RayIntersectsBox(ref Ray ray, ref BoundingBox box, out float distance) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 179 + + distance = 0f; + float tmax = float.MaxValue; + + if (Math.Abs(ray.Direction.X) < MathUtil.ZeroTolerance) + { + if (ray.Position.X < box.Minimum.X || ray.Position.X > box.Maximum.X) + { + distance = 0f; + return false; + } + } + else + { + float inverse = 1.0f / ray.Direction.X; + float t1 = (box.Minimum.X - ray.Position.X) * inverse; + float t2 = (box.Maximum.X - ray.Position.X) * inverse; + + if (t1 > t2) + { + float temp = t1; + t1 = t2; + t2 = temp; + } + + distance = Math.Max(t1, distance); + tmax = Math.Min(t2, tmax); + + if (distance > tmax) + { + distance = 0f; + return false; + } + } + + if (Math.Abs(ray.Direction.Y) < MathUtil.ZeroTolerance) + { + if (ray.Position.Y < box.Minimum.Y || ray.Position.Y > box.Maximum.Y) + { + distance = 0f; + return false; + } + } + else + { + float inverse = 1.0f / ray.Direction.Y; + float t1 = (box.Minimum.Y - ray.Position.Y) * inverse; + float t2 = (box.Maximum.Y - ray.Position.Y) * inverse; + + if (t1 > t2) + { + float temp = t1; + t1 = t2; + t2 = temp; + } + + distance = Math.Max(t1, distance); + tmax = Math.Min(t2, tmax); + + if (distance > tmax) + { + distance = 0f; + return false; + } + } + + if (Math.Abs(ray.Direction.Z) < MathUtil.ZeroTolerance) + { + if (ray.Position.Z < box.Minimum.Z || ray.Position.Z > box.Maximum.Z) + { + distance = 0f; + return false; + } + } + else + { + float inverse = 1.0f / ray.Direction.Z; + float t1 = (box.Minimum.Z - ray.Position.Z) * inverse; + float t2 = (box.Maximum.Z - ray.Position.Z) * inverse; + + if (t1 > t2) + { + float temp = t1; + t1 = t2; + t2 = temp; + } + + distance = Math.Max(t1, distance); + tmax = Math.Min(t2, tmax); + + if (distance > tmax) + { + distance = 0f; + return false; + } + } + + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The ray to test. + /// The box to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public static bool RayIntersectsBox(ref Ray ray, ref BoundingBox box, out Vector3 point) + { + float distance; + if (!RayIntersectsBox(ref ray, ref box, out distance)) + { + point = Vector3.Zero; + return false; + } + + point = ray.Position + (ray.Direction * distance); + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The ray to test. + /// The sphere to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public static bool RayIntersectsSphere(ref Ray ray, ref BoundingSphere sphere, out float distance) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 177 + + Vector3 m; + Vector3.Subtract(ref ray.Position, ref sphere.Center, out m); + + float b = Vector3.Dot(m, ray.Direction); + float c = Vector3.Dot(m, m) - (sphere.Radius * sphere.Radius); + + if (c > 0f && b > 0f) + { + distance = 0f; + return false; + } + + float discriminant = b * b - c; + + if (discriminant < 0f) + { + distance = 0f; + return false; + } + + distance = -b - (float)Math.Sqrt(discriminant); + + if (distance < 0f) + distance = 0f; + + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The ray to test. + /// The sphere to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public static bool RayIntersectsSphere(ref Ray ray, ref BoundingSphere sphere, out Vector3 point) + { + float distance; + if (!RayIntersectsSphere(ref ray, ref sphere, out distance)) + { + point = Vector3.Zero; + return false; + } + + point = ray.Position + (ray.Direction * distance); + return true; + } + + /// + /// Determines whether there is an intersection between a and a point. + /// + /// The plane to test. + /// The point to test. + /// Whether the two objects intersected. + public static PlaneIntersectionType PlaneIntersectsPoint(ref Plane plane, ref Vector3 point) + { + float distance; + Vector3.Dot(ref plane.Normal, ref point, out distance); + distance += plane.D; + + if (distance > 0f) + return PlaneIntersectionType.Front; + + if (distance < 0f) + return PlaneIntersectionType.Back; + + return PlaneIntersectionType.Intersecting; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The first plane to test. + /// The second plane to test. + /// Whether the two objects intersected. + public static bool PlaneIntersectsPlane(ref Plane plane1, ref Plane plane2) + { + Vector3 direction; + Vector3.Cross(ref plane1.Normal, ref plane2.Normal, out direction); + + //If direction is the zero vector, the planes are parallel and possibly + //coincident. It is not an intersection. The dot product will tell us. + float denominator; + Vector3.Dot(ref direction, ref direction, out denominator); + + if (Math.Abs(denominator) < MathUtil.ZeroTolerance) + return false; + + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The first plane to test. + /// The second plane to test. + /// When the method completes, contains the line of intersection + /// as a , or a zero ray if there was no intersection. + /// Whether the two objects intersected. + /// + /// Although a ray is set to have an origin, the ray returned by this method is really + /// a line in three dimensions which has no real origin. The ray is considered valid when + /// both the positive direction is used and when the negative direction is used. + /// + public static bool PlaneIntersectsPlane(ref Plane plane1, ref Plane plane2, out Ray line) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 207 + + Vector3 direction; + Vector3.Cross(ref plane1.Normal, ref plane2.Normal, out direction); + + //If direction is the zero vector, the planes are parallel and possibly + //coincident. It is not an intersection. The dot product will tell us. + float denominator; + Vector3.Dot(ref direction, ref direction, out denominator); + + //We assume the planes are normalized, therefore the denominator + //only serves as a parallel and coincident check. Otherwise we need + //to deivide the point by the denominator. + if (Math.Abs(denominator) < MathUtil.ZeroTolerance) + { + line = new Ray(); + return false; + } + + Vector3 point; + Vector3 temp = plane1.D * plane2.Normal - plane2.D * plane1.Normal; + Vector3.Cross(ref temp, ref direction, out point); + + line.Position = point; + line.Direction = direction; + line.Direction.Normalize(); + + return true; + } + + /// + /// Determines whether there is an intersection between a and a triangle. + /// + /// The plane to test. + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// Whether the two objects intersected. + public static PlaneIntersectionType PlaneIntersectsTriangle(ref Plane plane, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 207 + + PlaneIntersectionType test1 = PlaneIntersectsPoint(ref plane, ref vertex1); + PlaneIntersectionType test2 = PlaneIntersectsPoint(ref plane, ref vertex2); + PlaneIntersectionType test3 = PlaneIntersectsPoint(ref plane, ref vertex3); + + if (test1 == PlaneIntersectionType.Front && test2 == PlaneIntersectionType.Front && test3 == PlaneIntersectionType.Front) + return PlaneIntersectionType.Front; + + if (test1 == PlaneIntersectionType.Back && test2 == PlaneIntersectionType.Back && test3 == PlaneIntersectionType.Back) + return PlaneIntersectionType.Back; + + return PlaneIntersectionType.Intersecting; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The plane to test. + /// The box to test. + /// Whether the two objects intersected. + public static PlaneIntersectionType PlaneIntersectsBox(ref Plane plane, ref BoundingBox box) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 161 + + Vector3 min; + Vector3 max; + + max.X = (plane.Normal.X >= 0.0f) ? box.Minimum.X : box.Maximum.X; + max.Y = (plane.Normal.Y >= 0.0f) ? box.Minimum.Y : box.Maximum.Y; + max.Z = (plane.Normal.Z >= 0.0f) ? box.Minimum.Z : box.Maximum.Z; + min.X = (plane.Normal.X >= 0.0f) ? box.Maximum.X : box.Minimum.X; + min.Y = (plane.Normal.Y >= 0.0f) ? box.Maximum.Y : box.Minimum.Y; + min.Z = (plane.Normal.Z >= 0.0f) ? box.Maximum.Z : box.Minimum.Z; + + float distance; + Vector3.Dot(ref plane.Normal, ref max, out distance); + + if (distance + plane.D > 0.0f) + return PlaneIntersectionType.Front; + + distance = Vector3.Dot(plane.Normal, min); + + if (distance + plane.D < 0.0f) + return PlaneIntersectionType.Back; + + return PlaneIntersectionType.Intersecting; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The plane to test. + /// The sphere to test. + /// Whether the two objects intersected. + public static PlaneIntersectionType PlaneIntersectsSphere(ref Plane plane, ref BoundingSphere sphere) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 160 + + float distance; + Vector3.Dot(ref plane.Normal, ref sphere.Center, out distance); + distance += plane.D; + + if (distance > sphere.Radius) + return PlaneIntersectionType.Front; + + if (distance < -sphere.Radius) + return PlaneIntersectionType.Back; + + return PlaneIntersectionType.Intersecting; + } + + /* This implentation is wrong + /// + /// Determines whether there is an intersection between a and a triangle. + /// + /// The box to test. + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// Whether the two objects intersected. + public static bool BoxIntersectsTriangle(ref BoundingBox box, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + if (BoxContainsPoint(ref box, ref vertex1) == ContainmentType.Contains) + return true; + + if (BoxContainsPoint(ref box, ref vertex2) == ContainmentType.Contains) + return true; + + if (BoxContainsPoint(ref box, ref vertex3) == ContainmentType.Contains) + return true; + + return false; + } + */ + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The first box to test. + /// The second box to test. + /// Whether the two objects intersected. + public static bool BoxIntersectsBox(ref BoundingBox box1, ref BoundingBox box2) + { + if (box1.Minimum.X > box2.Maximum.X || box2.Minimum.X > box1.Maximum.X) + return false; + + if (box1.Minimum.Y > box2.Maximum.Y || box2.Minimum.Y > box1.Maximum.Y) + return false; + + if (box1.Minimum.Z > box2.Maximum.Z || box2.Minimum.Z > box1.Maximum.Z) + return false; + + return true; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// The box to test. + /// The sphere to test. + /// Whether the two objects intersected. + public static bool BoxIntersectsSphere(ref BoundingBox box, ref BoundingSphere sphere) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 166 + + Vector3 vector; + Vector3.Clamp(ref sphere.Center, ref box.Minimum, ref box.Maximum, out vector); + float distance = Vector3.DistanceSquared(sphere.Center, vector); + + return distance <= sphere.Radius * sphere.Radius; + } + + /// + /// Determines whether there is an intersection between a and a triangle. + /// + /// The sphere to test. + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// Whether the two objects intersected. + public static bool SphereIntersectsTriangle(ref BoundingSphere sphere, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + //Source: Real-Time Collision Detection by Christer Ericson + //Reference: Page 167 + + Vector3 point; + ClosestPointPointTriangle(ref sphere.Center, ref vertex1, ref vertex2, ref vertex3, out point); + Vector3 v = point - sphere.Center; + + float dot; + Vector3.Dot(ref v, ref v, out dot); + + return dot <= sphere.Radius * sphere.Radius; + } + + /// + /// Determines whether there is an intersection between a and a . + /// + /// First sphere to test. + /// Second sphere to test. + /// Whether the two objects intersected. + public static bool SphereIntersectsSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2) + { + float radiisum = sphere1.Radius + sphere2.Radius; + return Vector3.DistanceSquared(sphere1.Center, sphere2.Center) <= radiisum * radiisum; + } + + /// + /// Determines whether a contains a point. + /// + /// The box to test. + /// The point to test. + /// The type of containment the two objects have. + public static ContainmentType BoxContainsPoint(ref BoundingBox box, ref Vector3 point) + { + if (box.Minimum.X <= point.X && box.Maximum.X >= point.X && + box.Minimum.Y <= point.Y && box.Maximum.Y >= point.Y && + box.Minimum.Z <= point.Z && box.Maximum.Z >= point.Z) + { + return ContainmentType.Contains; + } + + return ContainmentType.Disjoint; + } + + /* This implentation is wrong + /// + /// Determines whether a contains a triangle. + /// + /// The box to test. + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// The type of containment the two objects have. + public static ContainmentType BoxContainsTriangle(ref BoundingBox box, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + ContainmentType test1 = BoxContainsPoint(ref box, ref vertex1); + ContainmentType test2 = BoxContainsPoint(ref box, ref vertex2); + ContainmentType test3 = BoxContainsPoint(ref box, ref vertex3); + + if (test1 == ContainmentType.Contains && test2 == ContainmentType.Contains && test3 == ContainmentType.Contains) + return ContainmentType.Contains; + + if (test1 == ContainmentType.Contains || test2 == ContainmentType.Contains || test3 == ContainmentType.Contains) + return ContainmentType.Intersects; + + return ContainmentType.Disjoint; + } + */ + + /// + /// Determines whether a contains a . + /// + /// The first box to test. + /// The second box to test. + /// The type of containment the two objects have. + public static ContainmentType BoxContainsBox(ref BoundingBox box1, ref BoundingBox box2) + { + if (box1.Maximum.X < box2.Minimum.X || box1.Minimum.X > box2.Maximum.X) + return ContainmentType.Disjoint; + + if (box1.Maximum.Y < box2.Minimum.Y || box1.Minimum.Y > box2.Maximum.Y) + return ContainmentType.Disjoint; + + if (box1.Maximum.Z < box2.Minimum.Z || box1.Minimum.Z > box2.Maximum.Z) + return ContainmentType.Disjoint; + + if (box1.Minimum.X <= box2.Minimum.X && (box2.Maximum.X <= box1.Maximum.X && + box1.Minimum.Y <= box2.Minimum.Y && box2.Maximum.Y <= box1.Maximum.Y) && + box1.Minimum.Z <= box2.Minimum.Z && box2.Maximum.Z <= box1.Maximum.Z) + { + return ContainmentType.Contains; + } + + return ContainmentType.Intersects; + } + + /// + /// Determines whether a contains a . + /// + /// The box to test. + /// The sphere to test. + /// The type of containment the two objects have. + public static ContainmentType BoxContainsSphere(ref BoundingBox box, ref BoundingSphere sphere) + { + Vector3 vector; + Vector3.Clamp(ref sphere.Center, ref box.Minimum, ref box.Maximum, out vector); + float distance = Vector3.DistanceSquared(sphere.Center, vector); + + if (distance > sphere.Radius * sphere.Radius) + return ContainmentType.Disjoint; + + if (((box.Minimum.X + sphere.Radius <= sphere.Center.X) && (sphere.Center.X <= box.Maximum.X - sphere.Radius) && (box.Maximum.X - box.Minimum.X > sphere.Radius)) && + ((box.Minimum.Y + sphere.Radius <= sphere.Center.Y) && (sphere.Center.Y <= box.Maximum.Y - sphere.Radius) && (box.Maximum.Y - box.Minimum.Y > sphere.Radius)) && + ((box.Minimum.Z + sphere.Radius <= sphere.Center.Z) && (sphere.Center.Z <= box.Maximum.Z - sphere.Radius) && (box.Maximum.Z - box.Minimum.Z > sphere.Radius))) + { + return ContainmentType.Contains; + } + + return ContainmentType.Intersects; + } + + /// + /// Determines whether a contains a point. + /// + /// The sphere to test. + /// The point to test. + /// The type of containment the two objects have. + public static ContainmentType SphereContainsPoint(ref BoundingSphere sphere, ref Vector3 point) + { + if (Vector3.DistanceSquared(point, sphere.Center) <= sphere.Radius * sphere.Radius) + return ContainmentType.Contains; + + return ContainmentType.Disjoint; + } + + /// + /// Determines whether a contains a triangle. + /// + /// The sphere to test. + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// The type of containment the two objects have. + public static ContainmentType SphereContainsTriangle(ref BoundingSphere sphere, ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + //Source: Jorgy343 + //Reference: None + + ContainmentType test1 = SphereContainsPoint(ref sphere, ref vertex1); + ContainmentType test2 = SphereContainsPoint(ref sphere, ref vertex2); + ContainmentType test3 = SphereContainsPoint(ref sphere, ref vertex3); + + if (test1 == ContainmentType.Contains && test2 == ContainmentType.Contains && test3 == ContainmentType.Contains) + return ContainmentType.Contains; + + if (SphereIntersectsTriangle(ref sphere, ref vertex1, ref vertex2, ref vertex3)) + return ContainmentType.Intersects; + + return ContainmentType.Disjoint; + } + + /// + /// Determines whether a contains a . + /// + /// The sphere to test. + /// The box to test. + /// The type of containment the two objects have. + public static ContainmentType SphereContainsBox(ref BoundingSphere sphere, ref BoundingBox box) + { + Vector3 vector; + + if (!BoxIntersectsSphere(ref box, ref sphere)) + return ContainmentType.Disjoint; + + float radiussquared = sphere.Radius * sphere.Radius; + vector.X = sphere.Center.X - box.Minimum.X; + vector.Y = sphere.Center.Y - box.Maximum.Y; + vector.Z = sphere.Center.Z - box.Maximum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + vector.X = sphere.Center.X - box.Maximum.X; + vector.Y = sphere.Center.Y - box.Maximum.Y; + vector.Z = sphere.Center.Z - box.Maximum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + vector.X = sphere.Center.X - box.Maximum.X; + vector.Y = sphere.Center.Y - box.Minimum.Y; + vector.Z = sphere.Center.Z - box.Maximum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + vector.X = sphere.Center.X - box.Minimum.X; + vector.Y = sphere.Center.Y - box.Minimum.Y; + vector.Z = sphere.Center.Z - box.Maximum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + vector.X = sphere.Center.X - box.Minimum.X; + vector.Y = sphere.Center.Y - box.Maximum.Y; + vector.Z = sphere.Center.Z - box.Minimum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + vector.X = sphere.Center.X - box.Maximum.X; + vector.Y = sphere.Center.Y - box.Maximum.Y; + vector.Z = sphere.Center.Z - box.Minimum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + vector.X = sphere.Center.X - box.Maximum.X; + vector.Y = sphere.Center.Y - box.Minimum.Y; + vector.Z = sphere.Center.Z - box.Minimum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + vector.X = sphere.Center.X - box.Minimum.X; + vector.Y = sphere.Center.Y - box.Minimum.Y; + vector.Z = sphere.Center.Z - box.Minimum.Z; + + if (vector.LengthSquared() > radiussquared) + return ContainmentType.Intersects; + + return ContainmentType.Contains; + } + + /// + /// Determines whether a contains a . + /// + /// The first sphere to test. + /// The second sphere to test. + /// The type of containment the two objects have. + public static ContainmentType SphereContainsSphere(ref BoundingSphere sphere1, ref BoundingSphere sphere2) + { + float distance = Vector3.Distance(sphere1.Center, sphere2.Center); + + if (sphere1.Radius + sphere2.Radius < distance) + return ContainmentType.Disjoint; + + if (sphere1.Radius - sphere2.Radius < distance) + return ContainmentType.Intersects; + + return ContainmentType.Contains; + } + + /// + /// Determines whether a intersects or contains an AABB determined by its center and extent. + /// Faster variant specific for frustum culling. + /// + /// The frustum. + /// The bounding box ext. + /// true if XXXX, false otherwise. + public static bool FrustumContainsBox(ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt) + { + unsafe + { + fixed (Plane* planeStart = &frustum.LeftPlane) + { + var plane = planeStart; + for (int i = 0; i < 6; ++i) + { + // Previous code: + if (Vector3.Dot(boundingBoxExt.Center, plane->Normal) + + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X) + + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y) + + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z) + <= -plane->D) + return false; + plane++; + } + } + + return true; + } +/* + unsafe + { + fixed (Plane* planeStart = &frustum.LeftPlane) + fixed (Vector3* pExtent = &boundingBoxExt.Extent) + { + var plane = planeStart; + for (int i = 0; i < 6; ++i) + { + // Previous code: + //if (Vector3.Dot(boundingBoxExt.Center, plane->Normal) + // + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X) + // + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y) + // + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z) + // <= -plane->D) + + // Optimized version (only 1 dot and cheaper Math.Abs) + // https://fgiesen.wordpress.com/2010/10/17/view-frustum-culling/ + // return dot3(center, plane) + dot3(extent, absPlane) <= -plane.w; + // or + // vector4 signFlip = componentwise_and(plane, 0x80000000); + // vector3 centerOffset = xor(extent, signFlip) + // dot3(center + centerOffset, plane) <= -plane.w; + + uint val = (((uint*)&plane->Normal)[0] & 0x80000000) ^ ((uint*)pExtent)[0]; + var dist = plane->Normal.X * ((*(float*)(&val)) + boundingBoxExt.Center.X); + + val = (((uint*)&plane->Normal)[1] & 0x80000000) ^ ((uint*)pExtent)[1]; + dist += plane->Normal.Y * ((*(float*)(&val)) + boundingBoxExt.Center.Y); + + val = (((uint*)&plane->Normal)[2] & 0x80000000) ^ ((uint*)pExtent)[2]; + dist += plane->Normal.Z * ((*(float*)(&val)) + boundingBoxExt.Center.Z); + + if (dist <= -plane->D) + return false; + + plane++; + } + } + + return true; + } + */ + } + } +} diff --git a/math/Color.Palette.cs b/math/Color.Palette.cs new file mode 100644 index 0000000..21bf89b --- /dev/null +++ b/math/Color.Palette.cs @@ -0,0 +1,725 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +namespace math +{ + /// + /// List of predefined . + /// + public partial struct Color + { + /// + /// Zero color. + /// + public static readonly Color Zero = Color.FromBgra(0x00000000); + + /// + /// Transparent color. + /// + public static readonly Color Transparent = Color.FromBgra(0x00000000); + + /// + /// AliceBlue color. + /// + public static readonly Color AliceBlue = Color.FromBgra(0xFFF0F8FF); + + /// + /// AntiqueWhite color. + /// + public static readonly Color AntiqueWhite = Color.FromBgra(0xFFFAEBD7); + + /// + /// Aqua color. + /// + public static readonly Color Aqua = Color.FromBgra(0xFF00FFFF); + + /// + /// Aquamarine color. + /// + public static readonly Color Aquamarine = Color.FromBgra(0xFF7FFFD4); + + /// + /// Azure color. + /// + public static readonly Color Azure = Color.FromBgra(0xFFF0FFFF); + + /// + /// Beige color. + /// + public static readonly Color Beige = Color.FromBgra(0xFFF5F5DC); + + /// + /// Bisque color. + /// + public static readonly Color Bisque = Color.FromBgra(0xFFFFE4C4); + + /// + /// Black color. + /// + public static readonly Color Black = Color.FromBgra(0xFF000000); + + /// + /// BlanchedAlmond color. + /// + public static readonly Color BlanchedAlmond = Color.FromBgra(0xFFFFEBCD); + + /// + /// Blue color. + /// + public static readonly Color Blue = Color.FromBgra(0xFF0000FF); + + /// + /// BlueViolet color. + /// + public static readonly Color BlueViolet = Color.FromBgra(0xFF8A2BE2); + + /// + /// Brown color. + /// + public static readonly Color Brown = Color.FromBgra(0xFFA52A2A); + + /// + /// BurlyWood color. + /// + public static readonly Color BurlyWood = Color.FromBgra(0xFFDEB887); + + /// + /// CadetBlue color. + /// + public static readonly Color CadetBlue = Color.FromBgra(0xFF5F9EA0); + + /// + /// Chartreuse color. + /// + public static readonly Color Chartreuse = Color.FromBgra(0xFF7FFF00); + + /// + /// Chocolate color. + /// + public static readonly Color Chocolate = Color.FromBgra(0xFFD2691E); + + /// + /// Coral color. + /// + public static readonly Color Coral = Color.FromBgra(0xFFFF7F50); + + /// + /// CornflowerBlue color. + /// + public static readonly Color CornflowerBlue = Color.FromBgra(0xFF6495ED); + + /// + /// Cornsilk color. + /// + public static readonly Color Cornsilk = Color.FromBgra(0xFFFFF8DC); + + /// + /// Crimson color. + /// + public static readonly Color Crimson = Color.FromBgra(0xFFDC143C); + + /// + /// Cyan color. + /// + public static readonly Color Cyan = Color.FromBgra(0xFF00FFFF); + + /// + /// DarkBlue color. + /// + public static readonly Color DarkBlue = Color.FromBgra(0xFF00008B); + + /// + /// DarkCyan color. + /// + public static readonly Color DarkCyan = Color.FromBgra(0xFF008B8B); + + /// + /// DarkGoldenrod color. + /// + public static readonly Color DarkGoldenrod = Color.FromBgra(0xFFB8860B); + + /// + /// DarkGray color. + /// + public static readonly Color DarkGray = Color.FromBgra(0xFFA9A9A9); + + /// + /// DarkGreen color. + /// + public static readonly Color DarkGreen = Color.FromBgra(0xFF006400); + + /// + /// DarkKhaki color. + /// + public static readonly Color DarkKhaki = Color.FromBgra(0xFFBDB76B); + + /// + /// DarkMagenta color. + /// + public static readonly Color DarkMagenta = Color.FromBgra(0xFF8B008B); + + /// + /// DarkOliveGreen color. + /// + public static readonly Color DarkOliveGreen = Color.FromBgra(0xFF556B2F); + + /// + /// DarkOrange color. + /// + public static readonly Color DarkOrange = Color.FromBgra(0xFFFF8C00); + + /// + /// DarkOrchid color. + /// + public static readonly Color DarkOrchid = Color.FromBgra(0xFF9932CC); + + /// + /// DarkRed color. + /// + public static readonly Color DarkRed = Color.FromBgra(0xFF8B0000); + + /// + /// DarkSalmon color. + /// + public static readonly Color DarkSalmon = Color.FromBgra(0xFFE9967A); + + /// + /// DarkSeaGreen color. + /// + public static readonly Color DarkSeaGreen = Color.FromBgra(0xFF8FBC8B); + + /// + /// DarkSlateBlue color. + /// + public static readonly Color DarkSlateBlue = Color.FromBgra(0xFF483D8B); + + /// + /// DarkSlateGray color. + /// + public static readonly Color DarkSlateGray = Color.FromBgra(0xFF2F4F4F); + + /// + /// DarkTurquoise color. + /// + public static readonly Color DarkTurquoise = Color.FromBgra(0xFF00CED1); + + /// + /// DarkViolet color. + /// + public static readonly Color DarkViolet = Color.FromBgra(0xFF9400D3); + + /// + /// DeepPink color. + /// + public static readonly Color DeepPink = Color.FromBgra(0xFFFF1493); + + /// + /// DeepSkyBlue color. + /// + public static readonly Color DeepSkyBlue = Color.FromBgra(0xFF00BFFF); + + /// + /// DimGray color. + /// + public static readonly Color DimGray = Color.FromBgra(0xFF696969); + + /// + /// VeryDimGray color. + /// + public static readonly Color VeryDimGray = Color.FromBgra(0xFF404040); + + /// + /// DodgerBlue color. + /// + public static readonly Color DodgerBlue = Color.FromBgra(0xFF1E90FF); + + /// + /// Firebrick color. + /// + public static readonly Color Firebrick = Color.FromBgra(0xFFB22222); + + /// + /// FloralWhite color. + /// + public static readonly Color FloralWhite = Color.FromBgra(0xFFFFFAF0); + + /// + /// ForestGreen color. + /// + public static readonly Color ForestGreen = Color.FromBgra(0xFF228B22); + + /// + /// Fuchsia color. + /// + public static readonly Color Fuchsia = Color.FromBgra(0xFFFF00FF); + + /// + /// Gainsboro color. + /// + public static readonly Color Gainsboro = Color.FromBgra(0xFFDCDCDC); + + /// + /// GhostWhite color. + /// + public static readonly Color GhostWhite = Color.FromBgra(0xFFF8F8FF); + + /// + /// Gold color. + /// + public static readonly Color Gold = Color.FromBgra(0xFFFFD700); + + /// + /// Goldenrod color. + /// + public static readonly Color Goldenrod = Color.FromBgra(0xFFDAA520); + + /// + /// Gray color. + /// + public static readonly Color Gray = Color.FromBgra(0xFF808080); + + /// + /// Green color. + /// + public static readonly Color Green = Color.FromBgra(0xFF008000); + + /// + /// GreenYellow color. + /// + public static readonly Color GreenYellow = Color.FromBgra(0xFFADFF2F); + + /// + /// Honeydew color. + /// + public static readonly Color Honeydew = Color.FromBgra(0xFFF0FFF0); + + /// + /// HotPink color. + /// + public static readonly Color HotPink = Color.FromBgra(0xFFFF69B4); + + /// + /// IndianRed color. + /// + public static readonly Color IndianRed = Color.FromBgra(0xFFCD5C5C); + + /// + /// Indigo color. + /// + public static readonly Color Indigo = Color.FromBgra(0xFF4B0082); + + /// + /// Ivory color. + /// + public static readonly Color Ivory = Color.FromBgra(0xFFFFFFF0); + + /// + /// Khaki color. + /// + public static readonly Color Khaki = Color.FromBgra(0xFFF0E68C); + + /// + /// Lavender color. + /// + public static readonly Color Lavender = Color.FromBgra(0xFFE6E6FA); + + /// + /// LavenderBlush color. + /// + public static readonly Color LavenderBlush = Color.FromBgra(0xFFFFF0F5); + + /// + /// LawnGreen color. + /// + public static readonly Color LawnGreen = Color.FromBgra(0xFF7CFC00); + + /// + /// LemonChiffon color. + /// + public static readonly Color LemonChiffon = Color.FromBgra(0xFFFFFACD); + + /// + /// LightBlue color. + /// + public static readonly Color LightBlue = Color.FromBgra(0xFFADD8E6); + + /// + /// LightCoral color. + /// + public static readonly Color LightCoral = Color.FromBgra(0xFFF08080); + + /// + /// LightCyan color. + /// + public static readonly Color LightCyan = Color.FromBgra(0xFFE0FFFF); + + /// + /// LightGoldenrodYellow color. + /// + public static readonly Color LightGoldenrodYellow = Color.FromBgra(0xFFFAFAD2); + + /// + /// LightGray color. + /// + public static readonly Color LightGray = Color.FromBgra(0xFFD3D3D3); + + /// + /// LightGreen color. + /// + public static readonly Color LightGreen = Color.FromBgra(0xFF90EE90); + + /// + /// LightPink color. + /// + public static readonly Color LightPink = Color.FromBgra(0xFFFFB6C1); + + /// + /// LightSalmon color. + /// + public static readonly Color LightSalmon = Color.FromBgra(0xFFFFA07A); + + /// + /// LightSeaGreen color. + /// + public static readonly Color LightSeaGreen = Color.FromBgra(0xFF20B2AA); + + /// + /// LightSkyBlue color. + /// + public static readonly Color LightSkyBlue = Color.FromBgra(0xFF87CEFA); + + /// + /// LightSlateGray color. + /// + public static readonly Color LightSlateGray = Color.FromBgra(0xFF778899); + + /// + /// LightSteelBlue color. + /// + public static readonly Color LightSteelBlue = Color.FromBgra(0xFFB0C4DE); + + /// + /// LightYellow color. + /// + public static readonly Color LightYellow = Color.FromBgra(0xFFFFFFE0); + + /// + /// Lime color. + /// + public static readonly Color Lime = Color.FromBgra(0xFF00FF00); + + /// + /// LimeGreen color. + /// + public static readonly Color LimeGreen = Color.FromBgra(0xFF32CD32); + + /// + /// Linen color. + /// + public static readonly Color Linen = Color.FromBgra(0xFFFAF0E6); + + /// + /// Magenta color. + /// + public static readonly Color Magenta = Color.FromBgra(0xFFFF00FF); + + /// + /// Maroon color. + /// + public static readonly Color Maroon = Color.FromBgra(0xFF800000); + + /// + /// MediumAquamarine color. + /// + public static readonly Color MediumAquamarine = Color.FromBgra(0xFF66CDAA); + + /// + /// MediumBlue color. + /// + public static readonly Color MediumBlue = Color.FromBgra(0xFF0000CD); + + /// + /// MediumOrchid color. + /// + public static readonly Color MediumOrchid = Color.FromBgra(0xFFBA55D3); + + /// + /// MediumPurple color. + /// + public static readonly Color MediumPurple = Color.FromBgra(0xFF9370DB); + + /// + /// MediumSeaGreen color. + /// + public static readonly Color MediumSeaGreen = Color.FromBgra(0xFF3CB371); + + /// + /// MediumSlateBlue color. + /// + public static readonly Color MediumSlateBlue = Color.FromBgra(0xFF7B68EE); + + /// + /// MediumSpringGreen color. + /// + public static readonly Color MediumSpringGreen = Color.FromBgra(0xFF00FA9A); + + /// + /// MediumTurquoise color. + /// + public static readonly Color MediumTurquoise = Color.FromBgra(0xFF48D1CC); + + /// + /// MediumVioletRed color. + /// + public static readonly Color MediumVioletRed = Color.FromBgra(0xFFC71585); + + /// + /// MidnightBlue color. + /// + public static readonly Color MidnightBlue = Color.FromBgra(0xFF191970); + + /// + /// MintCream color. + /// + public static readonly Color MintCream = Color.FromBgra(0xFFF5FFFA); + + /// + /// MistyRose color. + /// + public static readonly Color MistyRose = Color.FromBgra(0xFFFFE4E1); + + /// + /// Moccasin color. + /// + public static readonly Color Moccasin = Color.FromBgra(0xFFFFE4B5); + + /// + /// NavajoWhite color. + /// + public static readonly Color NavajoWhite = Color.FromBgra(0xFFFFDEAD); + + /// + /// Navy color. + /// + public static readonly Color Navy = Color.FromBgra(0xFF000080); + + /// + /// OldLace color. + /// + public static readonly Color OldLace = Color.FromBgra(0xFFFDF5E6); + + /// + /// Olive color. + /// + public static readonly Color Olive = Color.FromBgra(0xFF808000); + + /// + /// OliveDrab color. + /// + public static readonly Color OliveDrab = Color.FromBgra(0xFF6B8E23); + + /// + /// Orange color. + /// + public static readonly Color Orange = Color.FromBgra(0xFFFFA500); + + /// + /// OrangeRed color. + /// + public static readonly Color OrangeRed = Color.FromBgra(0xFFFF4500); + + /// + /// Orchid color. + /// + public static readonly Color Orchid = Color.FromBgra(0xFFDA70D6); + + /// + /// PaleGoldenrod color. + /// + public static readonly Color PaleGoldenrod = Color.FromBgra(0xFFEEE8AA); + + /// + /// PaleGreen color. + /// + public static readonly Color PaleGreen = Color.FromBgra(0xFF98FB98); + + /// + /// PaleTurquoise color. + /// + public static readonly Color PaleTurquoise = Color.FromBgra(0xFFAFEEEE); + + /// + /// PaleVioletRed color. + /// + public static readonly Color PaleVioletRed = Color.FromBgra(0xFFDB7093); + + /// + /// PapayaWhip color. + /// + public static readonly Color PapayaWhip = Color.FromBgra(0xFFFFEFD5); + + /// + /// PeachPuff color. + /// + public static readonly Color PeachPuff = Color.FromBgra(0xFFFFDAB9); + + /// + /// Peru color. + /// + public static readonly Color Peru = Color.FromBgra(0xFFCD853F); + + /// + /// Pink color. + /// + public static readonly Color Pink = Color.FromBgra(0xFFFFC0CB); + + /// + /// Plum color. + /// + public static readonly Color Plum = Color.FromBgra(0xFFDDA0DD); + + /// + /// PowderBlue color. + /// + public static readonly Color PowderBlue = Color.FromBgra(0xFFB0E0E6); + + /// + /// Purple color. + /// + public static readonly Color Purple = Color.FromBgra(0xFF800080); + + /// + /// Red color. + /// + public static readonly Color Red = Color.FromBgra(0xFFFF0000); + + /// + /// RosyBrown color. + /// + public static readonly Color RosyBrown = Color.FromBgra(0xFFBC8F8F); + + /// + /// RoyalBlue color. + /// + public static readonly Color RoyalBlue = Color.FromBgra(0xFF4169E1); + + /// + /// SaddleBrown color. + /// + public static readonly Color SaddleBrown = Color.FromBgra(0xFF8B4513); + + /// + /// Salmon color. + /// + public static readonly Color Salmon = Color.FromBgra(0xFFFA8072); + + /// + /// SandyBrown color. + /// + public static readonly Color SandyBrown = Color.FromBgra(0xFFF4A460); + + /// + /// SeaGreen color. + /// + public static readonly Color SeaGreen = Color.FromBgra(0xFF2E8B57); + + /// + /// SeaShell color. + /// + public static readonly Color SeaShell = Color.FromBgra(0xFFFFF5EE); + + /// + /// Sienna color. + /// + public static readonly Color Sienna = Color.FromBgra(0xFFA0522D); + + /// + /// Silver color. + /// + public static readonly Color Silver = Color.FromBgra(0xFFC0C0C0); + + /// + /// SkyBlue color. + /// + public static readonly Color SkyBlue = Color.FromBgra(0xFF87CEEB); + + /// + /// SlateBlue color. + /// + public static readonly Color SlateBlue = Color.FromBgra(0xFF6A5ACD); + + /// + /// SlateGray color. + /// + public static readonly Color SlateGray = Color.FromBgra(0xFF708090); + + /// + /// Snow color. + /// + public static readonly Color Snow = Color.FromBgra(0xFFFFFAFA); + + /// + /// SpringGreen color. + /// + public static readonly Color SpringGreen = Color.FromBgra(0xFF00FF7F); + + /// + /// SteelBlue color. + /// + public static readonly Color SteelBlue = Color.FromBgra(0xFF4682B4); + + /// + /// Tan color. + /// + public static readonly Color Tan = Color.FromBgra(0xFFD2B48C); + + /// + /// Teal color. + /// + public static readonly Color Teal = Color.FromBgra(0xFF008080); + + /// + /// Thistle color. + /// + public static readonly Color Thistle = Color.FromBgra(0xFFD8BFD8); + + /// + /// Tomato color. + /// + public static readonly Color Tomato = Color.FromBgra(0xFFFF6347); + + /// + /// Turquoise color. + /// + public static readonly Color Turquoise = Color.FromBgra(0xFF40E0D0); + + /// + /// Violet color. + /// + public static readonly Color Violet = Color.FromBgra(0xFFEE82EE); + + /// + /// Wheat color. + /// + public static readonly Color Wheat = Color.FromBgra(0xFFF5DEB3); + + /// + /// White color. + /// + public static readonly Color White = Color.FromBgra(0xFFFFFFFF); + + /// + /// WhiteSmoke color. + /// + public static readonly Color WhiteSmoke = Color.FromBgra(0xFFF5F5F5); + + /// + /// Yellow color. + /// + public static readonly Color Yellow = Color.FromBgra(0xFFFFFF00); + + /// + /// YellowGreen color. + /// + public static readonly Color YellowGreen = Color.FromBgra(0xFF9ACD32); + } +} diff --git a/math/Color.cs b/math/Color.cs new file mode 100644 index 0000000..e74b319 --- /dev/null +++ b/math/Color.cs @@ -0,0 +1,1128 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a 32-bit color (4 bytes) in the form of RGBA (in byte order: R, G, B, A). + /// + [DataContract(Name = "Color")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Size = 4)] + public partial struct Color : IEquatable + { + /// + /// The red component of the color. + /// + [DataMember(Order = 0)] + public byte R; + + /// + /// The green component of the color. + /// + [DataMember( Order = 1 )] + public byte G; + + /// + /// The blue component of the color. + /// + [DataMember( Order = 2 )] + public byte B; + + /// + /// The alpha component of the color. + /// + [DataMember( Order = 3 )] + public byte A; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Color(byte value) + { + A = R = G = B = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Color(float value) + { + A = R = G = B = ToByte(value); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + /// The alpha component of the color. + public Color(byte red, byte green, byte blue, byte alpha) + { + R = red; + G = green; + B = blue; + A = alpha; + } + + /// + /// Initializes a new instance of the struct. Alpha is set to 255. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + public Color(byte red, byte green, byte blue) + { + R = red; + G = green; + B = blue; + A = 255; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + /// The alpha component of the color. + public Color(float red, float green, float blue, float alpha) + { + R = ToByte(red); + G = ToByte(green); + B = ToByte(blue); + A = ToByte(alpha); + } + + /// + /// Initializes a new instance of the struct. Alpha is set to 255. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + public Color(float red, float green, float blue) + { + R = ToByte(red); + G = ToByte(green); + B = ToByte(blue); + A = 255; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red, green, blue, and alpha components of the color. + public Color(Vector4 value) + { + R = ToByte(value.X); + G = ToByte(value.Y); + B = ToByte(value.Z); + A = ToByte(value.W); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red, green, and blue components of the color. + /// The alpha component of the color. + public Color(Vector3 value, float alpha) + { + R = ToByte(value.X); + G = ToByte(value.Y); + B = ToByte(value.Z); + A = ToByte(alpha); + } + + /// + /// Initializes a new instance of the struct. Alpha is set to 255. + /// + /// The red, green, and blue components of the color. + public Color(Vector3 value) + { + R = ToByte(value.X); + G = ToByte(value.Y); + B = ToByte(value.Z); + A = 255; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed integer containing all four color components in RGBA order. + public Color(uint rgba) + { + A = (byte)((rgba >> 24) & 255); + B = (byte)((rgba >> 16) & 255); + G = (byte)((rgba >> 8) & 255); + R = (byte)(rgba & 255); + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed integer containing all four color components in RGBA order. + public Color(int rgba) + { + A = (byte)((rgba >> 24) & 255); + B = (byte)((rgba >> 16) & 255); + G = (byte)((rgba >> 8) & 255); + R = (byte)(rgba & 255); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the red, green, and blue, alpha components of the color. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Color(float[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (values.Length != 4) + throw new ArgumentOutOfRangeException(nameof(values), "There must be four and only four input values for Color."); + + R = ToByte(values[0]); + G = ToByte(values[1]); + B = ToByte(values[2]); + A = ToByte(values[3]); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the alpha, red, green, and blue components of the color. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Color(byte[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (values.Length != 4) + throw new ArgumentOutOfRangeException(nameof(values), "There must be four and only four input values for Color."); + + R = values[0]; + G = values[1]; + B = values[2]; + A = values[3]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the alpha, red, green, or blue component, depending on the index. + /// The index of the component to access. Use 0 for the alpha component, 1 for the red component, 2 for the green component, and 3 for the blue component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public byte this[int index] + { + get + { + switch (index) + { + case 0: return R; + case 1: return G; + case 2: return B; + case 3: return A; + } + + throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: R = value; break; + case 1: G = value; break; + case 2: B = value; break; + case 3: A = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color run from 0 to 3, inclusive."); + } + } + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToBgra() + { + int value = B; + value |= G << 8; + value |= R << 16; + value |= A << 24; + + return value; + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToRgba() + { + int value = R; + value |= G << 8; + value |= B << 16; + value |= A << 24; + + return value; + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToArgb() + { + int value = A; + value |= R << 8; + value |= G << 16; + value |= B << 24; + + return value; + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToAbgr() + { + int value = A; + value |= B << 8; + value |= G << 16; + value |= R << 24; + + return value; + } + + /// + /// Converts the color into a three component vector. + /// + /// A three component vector containing the red, green, and blue components of the color. + public Vector3 ToVector3() + { + return new Vector3(R / 255.0f, G / 255.0f, B / 255.0f); + } + + /// + /// Converts the color into a three component color. + /// + /// A three component color containing the red, green, and blue components of the color. + public Color3 ToColor3() + { + return new Color3(R / 255.0f, G / 255.0f, B / 255.0f); + } + + /// + /// Converts the color into a four component vector. + /// + /// A four component vector containing all four color components. + public Vector4 ToVector4() + { + return new Vector4(R / 255.0f, G / 255.0f, B / 255.0f, A / 255.0f); + } + + /// + /// Creates an array containing the elements of the color. + /// + /// A four-element array containing the components of the color in RGBA order. + public byte[] ToArray() + { + return new[] { R, G, B, A }; + } + + /// + /// Gets the brightness. + /// + /// The Hue-Saturation-Brightness (HSB) saturation for this + public float GetBrightness() + { + float r = R / 255.0f; + float g = G / 255.0f; + float b = B / 255.0f; + + float max, min; + + max = r; + min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + return (max + min) / 2; + } + + /// + /// Gets the hue. + /// + /// The Hue-Saturation-Brightness (HSB) saturation for this + public float GetHue() + { + if (R == G && G == B) + return 0; // 0 makes as good an UNDEFINED value as any + + float r = R / 255.0f; + float g = G / 255.0f; + float b = B / 255.0f; + + float max, min; + float delta; + float hue = 0.0f; + + max = r; + min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + delta = max - min; + + if (r == max) + { + hue = (g - b) / delta; + } + else if (g == max) + { + hue = 2 + (b - r) / delta; + } + else if (b == max) + { + hue = 4 + (r - g) / delta; + } + hue *= 60; + + if (hue < 0.0f) + { + hue += 360.0f; + } + return hue; + } + + /// + /// Gets the saturation. + /// + /// The Hue-Saturation-Brightness (HSB) saturation for this + public float GetSaturation() + { + float r = R / 255.0f; + float g = G / 255.0f; + float b = B / 255.0f; + + float max, min; + float l, s = 0; + + max = r; + min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + // if max == min, then there is no color and + // the saturation is zero. + if (max != min) + { + l = (max + min) / 2; + + if (l <= .5) + { + s = (max - min) / (max + min); + } + else + { + s = (max - min) / (2 - max - min); + } + } + return s; + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// When the method completes, completes the sum of the two colors. + public static void Add(ref Color left, ref Color right, out Color result) + { + result.A = (byte)(left.A + right.A); + result.R = (byte)(left.R + right.R); + result.G = (byte)(left.G + right.G); + result.B = (byte)(left.B + right.B); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static Color Add(Color left, Color right) + { + return new Color((byte)(left.R + right.R), (byte)(left.G + right.G), (byte)(left.B + right.B), (byte)(left.A + right.A)); + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// WHen the method completes, contains the difference of the two colors. + public static void Subtract(ref Color left, ref Color right, out Color result) + { + result.A = (byte)(left.A - right.A); + result.R = (byte)(left.R - right.R); + result.G = (byte)(left.G - right.G); + result.B = (byte)(left.B - right.B); + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract + /// The difference of the two colors. + public static Color Subtract(Color left, Color right) + { + return new Color((byte)(left.R - right.R), (byte)(left.G - right.G), (byte)(left.B - right.B), (byte)(left.A - right.A)); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// When the method completes, contains the modulated color. + public static void Modulate(ref Color left, ref Color right, out Color result) + { + result.A = (byte)(left.A * right.A / 255); + result.R = (byte)(left.R * right.R / 255); + result.G = (byte)(left.G * right.G / 255); + result.B = (byte)(left.B * right.B / 255); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static Color Modulate(Color left, Color right) + { + return new Color((byte)(left.R * right.R / 255), (byte)(left.G * right.G / 255), (byte)(left.B * right.B / 255), (byte)(left.A * right.A / 255)); + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// When the method completes, contains the scaled color. + public static void Scale(ref Color value, float scale, out Color result) + { + result.A = (byte)(value.A * scale); + result.R = (byte)(value.R * scale); + result.G = (byte)(value.G * scale); + result.B = (byte)(value.B * scale); + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// The scaled color. + public static Color Scale(Color value, float scale) + { + return new Color((byte)(value.R * scale), (byte)(value.G * scale), (byte)(value.B * scale), (byte)(value.A * scale)); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// When the method completes, contains the negated color. + public static void Negate(ref Color value, out Color result) + { + result.A = (byte)(255 - value.A); + result.R = (byte)(255 - value.R); + result.G = (byte)(255 - value.G); + result.B = (byte)(255 - value.B); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// The negated color. + public static Color Negate(Color value) + { + return new Color((byte)(255 - value.R), (byte)(255 - value.G), (byte)(255 - value.B), (byte)(255 - value.A)); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Color value, ref Color min, ref Color max, out Color result) + { + byte alpha = value.A; + alpha = (alpha > max.A) ? max.A : alpha; + alpha = (alpha < min.A) ? min.A : alpha; + + byte red = value.R; + red = (red > max.R) ? max.R : red; + red = (red < min.R) ? min.R : red; + + byte green = value.G; + green = (green > max.G) ? max.G : green; + green = (green < min.G) ? min.G : green; + + byte blue = value.B; + blue = (blue > max.B) ? max.B : blue; + blue = (blue < min.B) ? min.B : blue; + + result = new Color(red, green, blue, alpha); + } + + /// + /// Converts the color from a packed BGRA integer. + /// + /// A packed integer containing all four color components in BGRA order + /// A color. + public static Color FromBgra(int color) + { + return new Color((byte)((color >> 16) & 255), (byte)((color >> 8) & 255), (byte)(color & 255), (byte)((color >> 24) & 255)); + } + + /// + /// Converts the color from a packed BGRA integer. + /// + /// A packed integer containing all four color components in BGRA order + /// A color. + public static Color FromBgra(uint color) + { + return FromBgra(unchecked((int)color)); + } + + /// + /// Converts the color from a packed ABGR integer. + /// + /// A packed integer containing all four color components in ABGR order + /// A color. + public static Color FromAbgr(int color) + { + return new Color((byte)(color >> 24), (byte)(color >> 16), (byte)(color >> 8), (byte)color); + } + + /// + /// Converts the color from a packed ABGR integer. + /// + /// A packed integer containing all four color components in ABGR order + /// A color. + public static Color FromAbgr(uint color) + { + return FromAbgr(unchecked((int)color)); + } + + /// + /// Converts the color from a packed RGBA integer. + /// + /// A packed integer containing all four color components in RGBA order + /// A color. + public static Color FromRgba(int color) + { + return new Color(color); + } + + /// + /// Converts the color from a packed RGBA integer. + /// + /// A packed integer containing all four color components in RGBA order + /// A color. + public static Color FromRgba(uint color) + { + return new Color(color); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Color Clamp(Color value, Color min, Color max) + { + Color result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two colors. + /// + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Color start, ref Color end, float amount, out Color result) + { + result.R = MathUtil.Lerp(start.R, end.R, amount); + result.G = MathUtil.Lerp(start.G, end.G, amount); + result.B = MathUtil.Lerp(start.B, end.B, amount); + result.A = MathUtil.Lerp(start.A, end.A, amount); + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two colors. + /// + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Color Lerp(Color start, Color end, float amount) + { + Color result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two colors. + public static void SmoothStep(ref Color start, ref Color end, float amount, out Color result) + { + amount = MathUtil.SmoothStep(amount); + Lerp(ref start, ref end, amount, out result); + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two colors. + public static Color SmoothStep(Color start, Color end, float amount) + { + Color result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the largest components of the source colors. + public static void Max(ref Color left, ref Color right, out Color result) + { + result.A = (left.A > right.A) ? left.A : right.A; + result.R = (left.R > right.R) ? left.R : right.R; + result.G = (left.G > right.G) ? left.G : right.G; + result.B = (left.B > right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the largest components of the specified colorss. + /// + /// The first source color. + /// The second source color. + /// A color containing the largest components of the source colors. + public static Color Max(Color left, Color right) + { + Color result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the smallest components of the source colors. + public static void Min(ref Color left, ref Color right, out Color result) + { + result.A = (left.A < right.A) ? left.A : right.A; + result.R = (left.R < right.R) ? left.R : right.R; + result.G = (left.G < right.G) ? left.G : right.G; + result.B = (left.B < right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// A color containing the smallest components of the source colors. + public static Color Min(Color left, Color right) + { + Color result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// When the method completes, contains the adjusted color. + public static void AdjustContrast(ref Color value, float contrast, out Color result) + { + result.A = value.A; + result.R = ToByte(0.5f + contrast * (value.R / 255.0f - 0.5f)); + result.G = ToByte(0.5f + contrast * (value.G / 255.0f - 0.5f)); + result.B = ToByte(0.5f + contrast * (value.B / 255.0f - 0.5f)); + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// The adjusted color. + public static Color AdjustContrast(Color value, float contrast) + { + return new Color( + ToByte(0.5f + contrast * (value.R / 255.0f - 0.5f)), + ToByte(0.5f + contrast * (value.G / 255.0f - 0.5f)), + ToByte(0.5f + contrast * (value.B / 255.0f - 0.5f)), + value.A); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// When the method completes, contains the adjusted color. + public static void AdjustSaturation(ref Color value, float saturation, out Color result) + { + float grey = value.R / 255.0f * 0.2125f + value.G / 255.0f * 0.7154f + value.B / 255.0f * 0.0721f; + + result.A = value.A; + result.R = ToByte(grey + saturation * (value.R / 255.0f - grey)); + result.G = ToByte(grey + saturation * (value.G / 255.0f - grey)); + result.B = ToByte(grey + saturation * (value.B / 255.0f - grey)); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// The adjusted color. + public static Color AdjustSaturation(Color value, float saturation) + { + float grey = value.R / 255.0f * 0.2125f + value.G / 255.0f * 0.7154f + value.B / 255.0f * 0.0721f; + + return new Color( + ToByte(grey + saturation * (value.R / 255.0f - grey)), + ToByte(grey + saturation * (value.G / 255.0f - grey)), + ToByte(grey + saturation * (value.B / 255.0f - grey)), + value.A); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static Color operator +(Color left, Color right) + { + return new Color((byte)(left.R + right.R), (byte)(left.G + right.G), (byte)(left.B + right.B), (byte)(left.A + right.A)); + } + + /// + /// Assert a color (return it unchanged). + /// + /// The color to assert (unchanged). + /// The asserted (unchanged) color. + public static Color operator +(Color value) + { + return value; + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// The difference of the two colors. + public static Color operator -(Color left, Color right) + { + return new Color((byte)(left.R - right.R), (byte)(left.G - right.G), (byte)(left.B - right.B), (byte)(left.A - right.A)); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// A negated color. + public static Color operator -(Color value) + { + return new Color(-value.R, -value.G, -value.B, -value.A); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static Color operator *(float scale, Color value) + { + return new Color((byte)(value.R * scale), (byte)(value.G * scale), (byte)(value.B * scale), (byte)(value.A * scale)); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static Color operator *(Color value, float scale) + { + return new Color((byte)(value.R * scale), (byte)(value.G * scale), (byte)(value.B * scale), (byte)(value.A * scale)); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static Color operator *(Color left, Color right) + { + return new Color((byte)(left.R * right.R / 255.0f), (byte)(left.G * right.G / 255.0f), (byte)(left.B * right.B / 255.0f), (byte)(left.A * right.A / 255.0f)); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Color left, Color right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Color left, Color right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color3(Color value) + { + return value.ToColor3(); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Color value) + { + return new Vector3(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Color value) + { + return new Vector4(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f, value.A / 255.0f); + } + + /// + /// Convert this instance to a + /// + /// The result of the conversion. + public Color4 ToColor4() + { + return new Color4(R / 255.0f, G / 255.0f, B / 255.0f, A / 255.0f); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Color4(Color value) + { + return value.ToColor4(); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color(Vector3 value) + { + return new Color(value.X, value.Y, value.Z, 1.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color(Color3 value) + { + return new Color(value.R, value.G, value.B, 1.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color(Vector4 value) + { + return new Color(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color(Color4 value) + { + return new Color(value.R, value.G, value.B, value.A); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// + /// The result of the conversion. + /// + public static explicit operator int(Color value) + { + return value.ToRgba(); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// + /// The result of the conversion. + /// + public static explicit operator Color(int value) + { + return new Color(value); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return ColorExtensions.RgbaToString(ToRgba()); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return A.GetHashCode() + R.GetHashCode() + G.GetHashCode() + B.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Color other) + { + return R == other.R && G == other.G && B == other.B && A == other.A; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (!ReferenceEquals(value.GetType(), typeof(Color))) + return false; + + return Equals((Color)value); + } + + private static byte ToByte(float component) + { + var value = (int)(component * 255.0f); + return (byte)(value < 0 ? 0 : value > 255 ? 255 : value); + } + } +} diff --git a/math/Color3.cs b/math/Color3.cs new file mode 100644 index 0000000..3be151f --- /dev/null +++ b/math/Color3.cs @@ -0,0 +1,905 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a color in the form of rgb. + /// + [DataContract( Name = "Color3")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Color3 : IEquatable, IFormattable + { + private const string ToStringFormat = "R:{0} G:{1} B:{2}"; + + /// + /// The red component of the color. + /// + [DataMember( Order = 0 )] + public float R; + + /// + /// The green component of the color. + /// + [DataMember( Order = 1 )] + public float G; + + /// + /// The blue component of the color. + /// + [DataMember( Order = 2 )] + public float B; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Color3(float value) + { + R = G = B = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + public Color3(float red, float green, float blue) + { + R = red; + G = green; + B = blue; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red, green, and blue components of the color. + public Color3(Vector3 value) + { + R = value.X; + G = value.Y; + B = value.Z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed integer containing all three color components. + /// The alpha component is ignored. + public Color3(int rgb) + { + B = ((rgb >> 16) & 255) / 255.0f; + G = ((rgb >> 8) & 255) / 255.0f; + R = (rgb & 255) / 255.0f; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed unsigned integer containing all three color components. + /// The alpha component is ignored. + public Color3(uint rgb) + { + B = ((rgb >> 16) & 255) / 255.0f; + G = ((rgb >> 8) & 255) / 255.0f; + R = (rgb & 255) / 255.0f; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the red, green, and blue components of the color. This must be an array with three elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Color3(float[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (values.Length != 3) + throw new ArgumentOutOfRangeException(nameof(values), "There must be three and only three input values for Color3."); + + R = values[0]; + G = values[1]; + B = values[2]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the red, green, or blue component, depending on the index. + /// The index of the component to access. Use 0 for the red component, 1 for the green component, and 2 for the blue component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 2]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return R; + case 1: return G; + case 2: return B; + } + + throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color3 run from 0 to 2, inclusive."); + } + + set + { + switch (index) + { + case 0: R = value; break; + case 1: G = value; break; + case 2: B = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color3 run from 0 to 2, inclusive."); + } + } + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all three color components. + /// The alpha channel is set to 255. + public int ToRgb() + { + uint a = 255; + uint r = (uint)(R * 255.0f); + uint g = (uint)(G * 255.0f); + uint b = (uint)(B * 255.0f); + + uint value = r; + value += g << 8; + value += b << 16; + value += a << 24; + + return (int)value; + } + + /// + /// Raises the exponent for each components. + /// + /// The exponent. + public void Pow(float exponent) + { + R = (float)Math.Pow(R, exponent); + G = (float)Math.Pow(G, exponent); + B = (float)Math.Pow(B, exponent); + } + + /// + /// Converts the color into a three component vector. + /// + /// A three component vector containing the red, green, and blue components of the color. + public Vector3 ToVector3() + { + return new Vector3(R, G, B); + } + + /// + /// Creates an array containing the elements of the color. + /// + /// A three-element array containing the components of the color. + public float[] ToArray() + { + return new[] { R, G, B }; + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// When the method completes, completes the sum of the two colors. + public static void Add(ref Color3 left, ref Color3 right, out Color3 result) + { + result.R = left.R + right.R; + result.G = left.G + right.G; + result.B = left.B + right.B; + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static Color3 Add(Color3 left, Color3 right) + { + return new Color3(left.R + right.R, left.G + right.G, left.B + right.B); + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// WHen the method completes, contains the difference of the two colors. + public static void Subtract(ref Color3 left, ref Color3 right, out Color3 result) + { + result.R = left.R - right.R; + result.G = left.G - right.G; + result.B = left.B - right.B; + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract + /// The difference of the two colors. + public static Color3 Subtract(Color3 left, Color3 right) + { + return new Color3(left.R - right.R, left.G - right.G, left.B - right.B); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// When the method completes, contains the modulated color. + public static void Modulate(ref Color3 left, ref Color3 right, out Color3 result) + { + result.R = left.R * right.R; + result.G = left.G * right.G; + result.B = left.B * right.B; + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static Color3 Modulate(Color3 left, Color3 right) + { + return new Color3(left.R * right.R, left.G * right.G, left.B * right.B); + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// When the method completes, contains the scaled color. + public static void Scale(ref Color3 value, float scale, out Color3 result) + { + result.R = value.R * scale; + result.G = value.G * scale; + result.B = value.B * scale; + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// The scaled color. + public static Color3 Scale(Color3 value, float scale) + { + return new Color3(value.R * scale, value.G * scale, value.B * scale); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// When the method completes, contains the negated color. + public static void Negate(ref Color3 value, out Color3 result) + { + result.R = 1.0f - value.R; + result.G = 1.0f - value.G; + result.B = 1.0f - value.B; + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// The negated color. + public static Color3 Negate(Color3 value) + { + return new Color3(1.0f - value.R, 1.0f - value.G, 1.0f - value.B); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Color3 value, ref Color3 min, ref Color3 max, out Color3 result) + { + float red = value.R; + red = (red > max.R) ? max.R : red; + red = (red < min.R) ? min.R : red; + + float green = value.G; + green = (green > max.G) ? max.G : green; + green = (green < min.G) ? min.G : green; + + float blue = value.B; + blue = (blue > max.B) ? max.B : blue; + blue = (blue < min.B) ? min.B : blue; + + result = new Color3(red, green, blue); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Color3 Clamp(Color3 value, Color3 min, Color3 max) + { + Color3 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two colors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Color3 start, ref Color3 end, float amount, out Color3 result) + { + result.R = start.R + amount * (end.R - start.R); + result.G = start.G + amount * (end.G - start.G); + result.B = start.B + amount * (end.B - start.B); + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two colors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Color3 Lerp(Color3 start, Color3 end, float amount) + { + return new Color3( + start.R + amount * (end.R - start.R), + start.G + amount * (end.G - start.G), + start.B + amount * (end.B - start.B)); + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two colors. + public static void SmoothStep(ref Color3 start, ref Color3 end, float amount, out Color3 result) + { + amount = (amount > 1.0f) ? 1.0f : ((amount < 0.0f) ? 0.0f : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.R = start.R + ((end.R - start.R) * amount); + result.G = start.G + ((end.G - start.G) * amount); + result.B = start.B + ((end.B - start.B) * amount); + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two colors. + public static Color3 SmoothStep(Color3 start, Color3 end, float amount) + { + amount = (amount > 1.0f) ? 1.0f : ((amount < 0.0f) ? 0.0f : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + return new Color3( + start.R + ((end.R - start.R) * amount), + start.G + ((end.G - start.G) * amount), + start.B + ((end.B - start.B) * amount)); + } + + /// + /// Returns a color containing the smallest components of the specified colorss. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the largest components of the source colorss. + public static void Max(ref Color3 left, ref Color3 right, out Color3 result) + { + result.R = (left.R > right.R) ? left.R : right.R; + result.G = (left.G > right.G) ? left.G : right.G; + result.B = (left.B > right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the largest components of the specified colorss. + /// + /// The first source color. + /// The second source color. + /// A color containing the largest components of the source colors. + public static Color3 Max(Color3 left, Color3 right) + { + Color3 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the smallest components of the source colors. + public static void Min(ref Color3 left, ref Color3 right, out Color3 result) + { + result.R = (left.R < right.R) ? left.R : right.R; + result.G = (left.G < right.G) ? left.G : right.G; + result.B = (left.B < right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// A color containing the smallest components of the source colors. + public static Color3 Min(Color3 left, Color3 right) + { + Color3 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// When the method completes, contains the adjusted color. + public static void AdjustContrast(ref Color3 value, float contrast, out Color3 result) + { + result.R = 0.5f + contrast * (value.R - 0.5f); + result.G = 0.5f + contrast * (value.G - 0.5f); + result.B = 0.5f + contrast * (value.B - 0.5f); + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// The adjusted color. + public static Color3 AdjustContrast(Color3 value, float contrast) + { + return new Color3( + 0.5f + contrast * (value.R - 0.5f), + 0.5f + contrast * (value.G - 0.5f), + 0.5f + contrast * (value.B - 0.5f)); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// When the method completes, contains the adjusted color. + public static void AdjustSaturation(ref Color3 value, float saturation, out Color3 result) + { + float grey = value.R * 0.2125f + value.G * 0.7154f + value.B * 0.0721f; + + result.R = grey + saturation * (value.R - grey); + result.G = grey + saturation * (value.G - grey); + result.B = grey + saturation * (value.B - grey); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// The adjusted color. + public static Color3 AdjustSaturation(Color3 value, float saturation) + { + float grey = value.R * 0.2125f + value.G * 0.7154f + value.B * 0.0721f; + + return new Color3( + grey + saturation * (value.R - grey), + grey + saturation * (value.G - grey), + grey + saturation * (value.B - grey)); + } + + /// + /// Converts this color from linear space to sRGB space. + /// + /// A color3 in sRGB space. + public Color3 ToSRgb() + { + return new Color3(MathUtil.LinearToSRgb(R), MathUtil.LinearToSRgb(G), MathUtil.LinearToSRgb(B)); + } + + /// + /// Converts this color from sRGB space to linear space. + /// + /// Color3. + public Color3 ToLinear() + { + return new Color3(MathUtil.SRgbToLinear(R), MathUtil.SRgbToLinear(G), MathUtil.SRgbToLinear(B)); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static Color3 operator +(Color3 left, Color3 right) + { + return new Color3(left.R + right.R, left.G + right.G, left.B + right.B); + } + + /// + /// Assert a color (return it unchanged). + /// + /// The color to assert (unchange). + /// The asserted (unchanged) color. + public static Color3 operator +(Color3 value) + { + return value; + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// The difference of the two colors. + public static Color3 operator -(Color3 left, Color3 right) + { + return new Color3(left.R - right.R, left.G - right.G, left.B - right.B); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// A negated color. + public static Color3 operator -(Color3 value) + { + return new Color3(-value.R, -value.G, -value.B); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static Color3 operator *(float scale, Color3 value) + { + return new Color3(value.R * scale, value.G * scale, value.B * scale); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static Color3 operator *(Color3 value, float scale) + { + return new Color3(value.R * scale, value.G * scale, value.B * scale); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static Color3 operator *(Color3 left, Color3 right) + { + return new Color3(left.R * right.R, left.G * right.G, left.B * right.B); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Color3 left, Color3 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Color3 left, Color3 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color4(Color3 value) + { + return new Color4(value.R, value.G, value.B, 1.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Color3 value) + { + return new Vector3(value.R, value.G, value.B); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color3(Vector3 value) + { + return new Color3(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color3(int value) + { + return new Color3(value); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return ToString(CultureInfo.CurrentCulture); + } + + /// + /// Convert this color to an equivalent with an opaque alpha. + /// + /// An equivalent with an opaque alpha. + public Color4 ToColor4() + { + return new Color4(R, G, B, 1.0f); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, ToStringFormat, R, G, B); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, ToStringFormat, + R.ToString(format, formatProvider), + G.ToString(format, formatProvider), + B.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return R.GetHashCode() + G.GetHashCode() + B.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Color3 other) + { + return R == other.R && G == other.G && B == other.B; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Color3)value); + } + +#if SlimDX1xInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator SlimDX.Color3(Color3 value) + { + return new SlimDX.Color3(value.Red, value.Green, value.Blue); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Color3(SlimDX.Color3 value) + { + return new Color3(value.Red, value.Green, value.Blue); + } +#endif + +#if WPFInterop + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator System.Windows.Media.Color(Color3 value) + { + return new System.Windows.Media.Color() + { + A = 255, + R = (byte)(255f * value.Red), + G = (byte)(255f * value.Green), + B = (byte)(255f * value.Blue) + }; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color3(System.Windows.Media.Color value) + { + return new Color3() + { + Red = (float)value.R / 255f, + Green = (float)value.G / 255f, + Blue = (float)value.B / 255f + }; + } +#endif + +#if WinFormsInterop + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Drawing.Color(Color3 value) + { + return System.Drawing.Color.FromArgb( + (byte)(255f * value.Red), + (byte)(255f * value.Green), + (byte)(255f * value.Blue)); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Color3(System.Drawing.Color value) + { + return new Color3() + { + Red = (float)value.R / 255f, + Green = (float)value.G / 255f, + Blue = (float)value.B / 255f + }; + } +#endif + } +} diff --git a/math/Color4.cs b/math/Color4.cs new file mode 100644 index 0000000..d0aa6d8 --- /dev/null +++ b/math/Color4.cs @@ -0,0 +1,999 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a color in the form of rgba. + /// + [DataContract( Name = "Color4")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Color4 : IEquatable, IFormattable + { + private const string ToStringFormat = "A:{0} R:{1} G:{2} B:{3}"; + + /// + /// The Black color (0, 0, 0, 1). + /// + public static readonly Color4 Black = new Color4(0.0f, 0.0f, 0.0f, 1.0f); + + /// + /// The White color (1, 1, 1, 1). + /// + public static readonly Color4 White = new Color4(1.0f, 1.0f, 1.0f, 1.0f); + + /// + /// The red component of the color. + /// + [DataMember( Order = 0 )] + public float R; + + /// + /// The green component of the color. + /// + [DataMember( Order = 1 )] + public float G; + + /// + /// The blue component of the color. + /// + [DataMember( Order = 2 )] + public float B; + + /// + /// The alpha component of the color. + /// + [DataMember( Order = 3 )] + public float A; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Color4(float value) + { + A = R = G = B = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + /// The alpha component of the color. + public Color4(float red, float green, float blue, float alpha) + { + R = red; + G = green; + B = blue; + A = alpha; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red, green, blue, and alpha components of the color. + public Color4(Vector4 value) + { + R = value.X; + G = value.Y; + B = value.Z; + A = value.W; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red, green, and blue components of the color. + /// The alpha component of the color. + public Color4(Vector3 value, float alpha) + { + R = value.X; + G = value.Y; + B = value.Z; + A = alpha; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed integer containing all four color components in RGBA order. + public Color4(uint rgba) + { + A = ((rgba >> 24) & 255) / 255.0f; + B = ((rgba >> 16) & 255) / 255.0f; + G = ((rgba >> 8) & 255) / 255.0f; + R = (rgba & 255) / 255.0f; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed integer containing all four color components in RGBA order. + public Color4(int rgba) + { + A = ((rgba >> 24) & 255) / 255.0f; + B = ((rgba >> 16) & 255) / 255.0f; + G = ((rgba >> 8) & 255) / 255.0f; + R = (rgba & 255) / 255.0f; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the red, green, blue, and alpha components of the color. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Color4(float[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (values.Length != 4) + throw new ArgumentOutOfRangeException(nameof(values), "There must be four and only four input values for Color4."); + + R = values[0]; + G = values[1]; + B = values[2]; + A = values[3]; + } + + /// + /// Initializes a new instance of the struct. + /// + /// used to initialize the color. + public Color4(Color3 color) + { + R = color.R; + G = color.G; + B = color.B; + A = 1.0f; + } + + /// + /// Initializes a new instance of the struct. + /// + /// used to initialize the color. + public Color4(Color color) + { + R = color.R / 255.0f; + G = color.G / 255.0f; + B = color.B / 255.0f; + A = color.A / 255.0f; + } + + /// + /// Initializes a new instance of the struct. + /// + /// used to initialize the color. + public Color4(ColorBGRA color) + { + R = color.R / 255.0f; + G = color.G / 255.0f; + B = color.B / 255.0f; + A = color.A / 255.0f; + } + + /// + /// Initializes a new instance of the struct. + /// + /// used to initialize the color. + /// The alpha component of the color. + public Color4(Color3 color, float alpha) + { + R = color.R; + G = color.G; + B = color.B; + A = alpha; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the red, green, blue, and alpha components, depending on the index. + /// The index of the component to access. Use 0 for the alpha component, 1 for the red component, 2 for the green component, and 3 for the blue component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return R; + case 1: return G; + case 2: return B; + case 3: return A; + } + + throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color4 run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: R = value; break; + case 1: G = value; break; + case 2: B = value; break; + case 3: A = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color4 run from 0 to 3, inclusive."); + } + } + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToBgra() + { + uint a = (uint)(A * 255.0f) & 255; + uint r = (uint)(R * 255.0f) & 255; + uint g = (uint)(G * 255.0f) & 255; + uint b = (uint)(B * 255.0f) & 255; + + uint value = b; + value |= g << 8; + value |= r << 16; + value |= a << 24; + + return (int)value; + } + + /// + /// Converts the color into a packed integer. + /// + public void ToBgra(out byte r, out byte g, out byte b, out byte a) + { + a = (byte)(A * 255.0f); + r = (byte)(R * 255.0f); + g = (byte)(G * 255.0f); + b = (byte)(B * 255.0f); + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToRgba() + { + uint a = (uint)(A * 255.0f) & 255; + uint r = (uint)(R * 255.0f) & 255; + uint g = (uint)(G * 255.0f) & 255; + uint b = (uint)(B * 255.0f) & 255; + + uint value = r; + value |= g << 8; + value |= b << 16; + value |= a << 24; + + return (int)value; + } + + /// + /// Converts the color into a three component vector. + /// + /// A three component vector containing the red, green, and blue components of the color. + public Vector3 ToVector3() + { + return new Vector3(R, G, B); + } + + /// + /// Converts the color into a four component vector. + /// + /// A four component vector containing all four color components. + public Vector4 ToVector4() + { + return new Vector4(R, G, B, A); + } + + /// + /// Creates an array containing the elements of the color. + /// + /// A four-element array containing the components of the color. + public float[] ToArray() + { + return new[] { R, G, B, A }; + } + + /// + /// Converts this color from linear space to sRGB space. + /// + /// A color3 in sRGB space. + public Color4 ToSRgb() + { + return new Color4(MathUtil.LinearToSRgb(R), MathUtil.LinearToSRgb(G), MathUtil.LinearToSRgb(B), A); + } + + /// + /// Converts this color from sRGB space to linear space. + /// + /// A color4 in linear space. + public Color4 ToLinear() + { + return new Color4(MathUtil.SRgbToLinear(R), MathUtil.SRgbToLinear(G), MathUtil.SRgbToLinear(B), A); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// When the method completes, completes the sum of the two colors. + public static void Add(ref Color4 left, ref Color4 right, out Color4 result) + { + result.A = left.A + right.A; + result.R = left.R + right.R; + result.G = left.G + right.G; + result.B = left.B + right.B; + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static Color4 Add(Color4 left, Color4 right) + { + return new Color4(left.R + right.R, left.G + right.G, left.B + right.B, left.A + right.A); + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// WHen the method completes, contains the difference of the two colors. + public static void Subtract(ref Color4 left, ref Color4 right, out Color4 result) + { + result.A = left.A - right.A; + result.R = left.R - right.R; + result.G = left.G - right.G; + result.B = left.B - right.B; + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract + /// The difference of the two colors. + public static Color4 Subtract(Color4 left, Color4 right) + { + return new Color4(left.R - right.R, left.G - right.G, left.B - right.B, left.A - right.A); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// When the method completes, contains the modulated color. + public static void Modulate(ref Color4 left, ref Color4 right, out Color4 result) + { + result.A = left.A * right.A; + result.R = left.R * right.R; + result.G = left.G * right.G; + result.B = left.B * right.B; + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static Color4 Modulate(Color4 left, Color4 right) + { + return new Color4(left.R * right.R, left.G * right.G, left.B * right.B, left.A * right.A); + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// When the method completes, contains the scaled color. + public static void Scale(ref Color4 value, float scale, out Color4 result) + { + result.A = value.A * scale; + result.R = value.R * scale; + result.G = value.G * scale; + result.B = value.B * scale; + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// The scaled color. + public static Color4 Scale(Color4 value, float scale) + { + return new Color4(value.R * scale, value.G * scale, value.B * scale, value.A * scale); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// When the method completes, contains the negated color. + public static void Negate(ref Color4 value, out Color4 result) + { + result.A = 1.0f - value.A; + result.R = 1.0f - value.R; + result.G = 1.0f - value.G; + result.B = 1.0f - value.B; + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// The negated color. + public static Color4 Negate(Color4 value) + { + return new Color4(1.0f - value.R, 1.0f - value.G, 1.0f - value.B, 1.0f - value.A); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Color4 value, ref Color4 min, ref Color4 max, out Color4 result) + { + float alpha = value.A; + alpha = (alpha > max.A) ? max.A : alpha; + alpha = (alpha < min.A) ? min.A : alpha; + + float red = value.R; + red = (red > max.R) ? max.R : red; + red = (red < min.R) ? min.R : red; + + float green = value.G; + green = (green > max.G) ? max.G : green; + green = (green < min.G) ? min.G : green; + + float blue = value.B; + blue = (blue > max.B) ? max.B : blue; + blue = (blue < min.B) ? min.B : blue; + + result = new Color4(red, green, blue, alpha); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Color4 Clamp(Color4 value, Color4 min, Color4 max) + { + Color4 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two colors. + /// + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Color4 start, ref Color4 end, float amount, out Color4 result) + { + result.R = MathUtil.Lerp(start.R, end.R, amount); + result.G = MathUtil.Lerp(start.G, end.G, amount); + result.B = MathUtil.Lerp(start.B, end.B, amount); + result.A = MathUtil.Lerp(start.A, end.A, amount); + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two colors. + /// + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Color4 Lerp(Color4 start, Color4 end, float amount) + { + Color4 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two colors. + public static void SmoothStep(ref Color4 start, ref Color4 end, float amount, out Color4 result) + { + amount = MathUtil.SmoothStep(amount); + Lerp(ref start, ref end, amount, out result); + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two colors. + public static Color4 SmoothStep(Color4 start, Color4 end, float amount) + { + Color4 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the largest components of the source colors. + public static void Max(ref Color4 left, ref Color4 right, out Color4 result) + { + result.A = (left.A > right.A) ? left.A : right.A; + result.R = (left.R > right.R) ? left.R : right.R; + result.G = (left.G > right.G) ? left.G : right.G; + result.B = (left.B > right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the largest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// A color containing the largest components of the source colors. + public static Color4 Max(Color4 left, Color4 right) + { + Color4 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the smallest components of the source colors. + public static void Min(ref Color4 left, ref Color4 right, out Color4 result) + { + result.A = (left.A < right.A) ? left.A : right.A; + result.R = (left.R < right.R) ? left.R : right.R; + result.G = (left.G < right.G) ? left.G : right.G; + result.B = (left.B < right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// A color containing the smallest components of the source colors. + public static Color4 Min(Color4 left, Color4 right) + { + Color4 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// When the method completes, contains the adjusted color. + public static void AdjustContrast(ref Color4 value, float contrast, out Color4 result) + { + result.A = value.A; + result.R = 0.5f + contrast * (value.R - 0.5f); + result.G = 0.5f + contrast * (value.G - 0.5f); + result.B = 0.5f + contrast * (value.B - 0.5f); + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// The adjusted color. + public static Color4 AdjustContrast(Color4 value, float contrast) + { + return new Color4( + 0.5f + contrast * (value.R - 0.5f), + 0.5f + contrast * (value.G - 0.5f), + 0.5f + contrast * (value.B - 0.5f), + value.A); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// When the method completes, contains the adjusted color. + public static void AdjustSaturation(ref Color4 value, float saturation, out Color4 result) + { + float grey = value.R * 0.2125f + value.G * 0.7154f + value.B * 0.0721f; + + result.A = value.A; + result.R = grey + saturation * (value.R - grey); + result.G = grey + saturation * (value.G - grey); + result.B = grey + saturation * (value.B - grey); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// The adjusted color. + public static Color4 AdjustSaturation(Color4 value, float saturation) + { + float grey = value.R * 0.2125f + value.G * 0.7154f + value.B * 0.0721f; + + return new Color4( + grey + saturation * (value.R - grey), + grey + saturation * (value.G - grey), + grey + saturation * (value.B - grey), + value.A); + } + + /// + /// Premultiplies the color components by the alpha value. + /// + /// The color to premultiply. + /// A color with premultiplied alpha. + public static Color4 PremultiplyAlpha(Color4 value) + { + return new Color4(value.R * value.A, value.G * value.A, value.B * value.A, value.A); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static Color4 operator +(Color4 left, Color4 right) + { + return new Color4(left.R + right.R, left.G + right.G, left.B + right.B, left.A + right.A); + } + + /// + /// Assert a color (return it unchanged). + /// + /// The color to assert (unchanged). + /// The asserted (unchanged) color. + public static Color4 operator +(Color4 value) + { + return value; + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// The difference of the two colors. + public static Color4 operator -(Color4 left, Color4 right) + { + return new Color4(left.R - right.R, left.G - right.G, left.B - right.B, left.A - right.A); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// A negated color. + public static Color4 operator -(Color4 value) + { + return new Color4(-value.R, -value.G, -value.B, -value.A); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static Color4 operator *(float scale, Color4 value) + { + return new Color4(value.R * scale, value.G * scale, value.B * scale, value.A * scale); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static Color4 operator *(Color4 value, float scale) + { + return new Color4(value.R * scale, value.G * scale, value.B * scale, value.A * scale); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static Color4 operator *(Color4 left, Color4 right) + { + return new Color4(left.R * right.R, left.G * right.G, left.B * right.B, left.A * right.A); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Color4 left, Color4 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Color4 left, Color4 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color3(Color4 value) + { + return new Color3(value.R, value.G, value.B); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Color4 value) + { + return new Vector3(value.R, value.G, value.B); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vector4(Color4 value) + { + return new Vector4(value.R, value.G, value.B, value.A); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color4(Vector3 value) + { + return new Color4(value.X, value.Y, value.Z, 1.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color4(Vector4 value) + { + return new Color4(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color4(ColorBGRA value) + { + return new Color4(value); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator ColorBGRA(Color4 value) + { + return new ColorBGRA(value.R, value.G, value.B, value.A); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// + /// The result of the conversion. + /// + public static explicit operator int(Color4 value) + { + return value.ToRgba(); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// + /// The result of the conversion. + /// + public static explicit operator Color4(int value) + { + return new Color4(value); + } + + /// + /// Converts this color to an equivalent , discarding the alpha channel. + /// + /// An equivalent , discarding the alpha channel. + public Color3 ToColor3() + { + return new Color3(R, G, B); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return ToString(CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format to apply to each channel (float). + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, ToStringFormat, A, R, G, B); + } + + /// + /// Returns a that represents this instance. + /// + /// The format to apply to each channel (float). + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, ToStringFormat, + A.ToString(format, formatProvider), + R.ToString(format, formatProvider), + G.ToString(format, formatProvider), + B.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return A.GetHashCode() + R.GetHashCode() + G.GetHashCode() + B.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Color4 other) + { + return A == other.A && R == other.R && G == other.G && B == other.B; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (!ReferenceEquals(value.GetType(), typeof(Color4))) + return false; + + return Equals((Color4)value); + } + } +} diff --git a/math/ColorBGRA.cs b/math/ColorBGRA.cs new file mode 100644 index 0000000..1da32da --- /dev/null +++ b/math/ColorBGRA.cs @@ -0,0 +1,1097 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a 32-bit color (4 bytes) in the form of BGRA (in byte order: B, G, R, A). + /// + [DataContract( Name = "ColorBGRA")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Size = 4)] + public partial struct ColorBGRA : IEquatable, IFormattable + { + private const string ToStringFormat = "A:{0} R:{1} G:{2} B:{3}"; + + /// + /// The blue component of the color. + /// + [DataMember( Order = 0 )] + public byte B; + + /// + /// The green component of the color. + /// + [DataMember( Order = 1 )] + public byte G; + + /// + /// The red component of the color. + /// + [DataMember( Order = 2 )] + public byte R; + + /// + /// The alpha component of the color. + /// + [DataMember( Order = 3 )] + public byte A; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public ColorBGRA(byte value) + { + A = R = G = B = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public ColorBGRA(float value) + { + A = R = G = B = ToByte(value); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + /// The alpha component of the color. + public ColorBGRA(byte red, byte green, byte blue, byte alpha) + { + R = red; + G = green; + B = blue; + A = alpha; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component of the color. + /// The green component of the color. + /// The blue component of the color. + /// The alpha component of the color. + public ColorBGRA(float red, float green, float blue, float alpha) + { + R = ToByte(red); + G = ToByte(green); + B = ToByte(blue); + A = ToByte(alpha); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red, green, blue, and alpha components of the color. + public ColorBGRA(Vector4 value) + { + R = ToByte(value.X); + G = ToByte(value.Y); + B = ToByte(value.Z); + A = ToByte(value.W); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red, green, and blue components of the color. + /// The alpha component of the color. + public ColorBGRA(Vector3 value, float alpha) + { + R = ToByte(value.X); + G = ToByte(value.Y); + B = ToByte(value.Z); + A = ToByte(alpha); + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed integer containing all four color components in BGRA order. + public ColorBGRA(uint bgra) + { + A = (byte)((bgra >> 24) & 255); + R = (byte)((bgra >> 16) & 255); + G = (byte)((bgra >> 8) & 255); + B = (byte)(bgra & 255); + } + + /// + /// Initializes a new instance of the struct. + /// + /// A packed integer containing all four color components in BGRA. + public ColorBGRA(int bgra) + { + A = (byte)((bgra >> 24) & 255); + R = (byte)((bgra >> 16) & 255); + G = (byte)((bgra >> 8) & 255); + B = (byte)(bgra & 255); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the red, green, and blue, alpha components of the color. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public ColorBGRA(float[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (values.Length != 4) + throw new ArgumentOutOfRangeException(nameof(values), "There must be four and only four input values for ColorBGRA."); + + B = ToByte(values[0]); + G = ToByte(values[1]); + R = ToByte(values[2]); + A = ToByte(values[3]); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the red, green, and blue, alpha components of the color. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public ColorBGRA(byte[] values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (values.Length != 4) + throw new ArgumentOutOfRangeException(nameof(values), "There must be four and only four input values for ColorBGRA."); + + B = values[0]; + G = values[1]; + R = values[2]; + A = values[3]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the alpha, red, green, or blue component, depending on the index. + /// The index of the component to access. Use 0 for the alpha component, 1 for the red component, 2 for the green component, and 3 for the blue component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public byte this[int index] + { + get + { + switch (index) + { + case 0: return B; + case 1: return G; + case 2: return R; + case 3: return A; + } + + throw new ArgumentOutOfRangeException(nameof(index), "Indices for ColorBGRA run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: B = value; break; + case 1: G = value; break; + case 2: R = value; break; + case 3: A = value; break; + default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for ColorBGRA run from 0 to 3, inclusive."); + } + } + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToBgra() + { + int value = B; + value |= G << 8; + value |= R << 16; + value |= A << 24; + + return value; + } + + /// + /// Converts the color into a packed integer. + /// + /// A packed integer containing all four color components. + public int ToRgba() + { + int value = R; + value |= G << 8; + value |= B << 16; + value |= A << 24; + + return value; + } + + /// + /// Converts the color into a three component vector. + /// + /// A three component vector containing the red, green, and blue components of the color. + public Vector3 ToVector3() + { + return new Vector3(R / 255.0f, G / 255.0f, B / 255.0f); + } + + /// + /// Converts the color into a three component color. + /// + /// A three component color containing the red, green, and blue components of the color. + public Color3 ToColor3() + { + return new Color3(R / 255.0f, G / 255.0f, B / 255.0f); + } + + /// + /// Converts the color into a four component vector. + /// + /// A four component vector containing all four color components. + public Vector4 ToVector4() + { + return new Vector4(R / 255.0f, G / 255.0f, B / 255.0f, A / 255.0f); + } + + /// + /// Creates an array containing the elements of the color. + /// + /// A four-element array containing the components of the color in BGRA order. + public byte[] ToArray() + { + return new[] { B, G, R, A }; + } + + /// + /// Gets the brightness. + /// + /// The Hue-Saturation-Brightness (HSB) saturation for this + public float GetBrightness() + { + float r = R / 255.0f; + float g = G / 255.0f; + float b = B / 255.0f; + + float max, min; + + max = r; + min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + return (max + min) / 2; + } + + /// + /// Gets the hue. + /// + /// The Hue-Saturation-Brightness (HSB) saturation for this + public float GetHue() + { + if (R == G && G == B) + return 0; // 0 makes as good an UNDEFINED value as any + + float r = R / 255.0f; + float g = G / 255.0f; + float b = B / 255.0f; + + float max, min; + float delta; + float hue = 0.0f; + + max = r; + min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + delta = max - min; + + if (r == max) + { + hue = (g - b) / delta; + } + else if (g == max) + { + hue = 2 + (b - r) / delta; + } + else if (b == max) + { + hue = 4 + (r - g) / delta; + } + hue *= 60; + + if (hue < 0.0f) + { + hue += 360.0f; + } + return hue; + } + + /// + /// Gets the saturation. + /// + /// The Hue-Saturation-Brightness (HSB) saturation for this + public float GetSaturation() + { + float r = R / 255.0f; + float g = G / 255.0f; + float b = B / 255.0f; + + float max, min; + float l, s = 0; + + max = r; + min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + // if max == min, then there is no color and + // the saturation is zero. + if (max != min) + { + l = (max + min) / 2; + + if (l <= .5) + { + s = (max - min) / (max + min); + } + else + { + s = (max - min) / (2 - max - min); + } + } + return s; + } + + /// + /// Converts the color from a packed BGRA integer. + /// + /// A packed integer containing all four color components in BGRA order + /// A color. + public static ColorBGRA FromBgra(int color) + { + return new ColorBGRA(color); + } + + /// + /// Converts the color from a packed BGRA integer. + /// + /// A packed integer containing all four color components in BGRA order + /// A color. + public static ColorBGRA FromBgra(uint color) + { + return new ColorBGRA(color); + } + + /// + /// Converts the color from a packed RGBA integer. + /// + /// A packed integer containing all four color components in RGBA order + /// A color. + public static ColorBGRA FromRgba(int color) + { + return new ColorBGRA((byte)(color & 255), (byte)((color >> 8) & 255), (byte)((color >> 16) & 255), (byte)((color >> 24) & 255)); + } + + /// + /// Converts the color from a packed RGBA integer. + /// + /// A packed integer containing all four color components in RGBA order + /// A color. + public static ColorBGRA FromRgba(uint color) + { + return FromRgba(unchecked((int)color)); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// When the method completes, completes the sum of the two colors. + public static void Add(ref ColorBGRA left, ref ColorBGRA right, out ColorBGRA result) + { + result.A = (byte)(left.A + right.A); + result.R = (byte)(left.R + right.R); + result.G = (byte)(left.G + right.G); + result.B = (byte)(left.B + right.B); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static ColorBGRA Add(ColorBGRA left, ColorBGRA right) + { + return new ColorBGRA((byte)(left.R + right.R), (byte)(left.G + right.G), (byte)(left.B + right.B), (byte)(left.A + right.A)); + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// WHen the method completes, contains the difference of the two colors. + public static void Subtract(ref ColorBGRA left, ref ColorBGRA right, out ColorBGRA result) + { + result.A = (byte)(left.A - right.A); + result.R = (byte)(left.R - right.R); + result.G = (byte)(left.G - right.G); + result.B = (byte)(left.B - right.B); + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract + /// The difference of the two colors. + public static ColorBGRA Subtract(ColorBGRA left, ColorBGRA right) + { + return new ColorBGRA((byte)(left.R - right.R), (byte)(left.G - right.G), (byte)(left.B - right.B), (byte)(left.A - right.A)); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// When the method completes, contains the modulated color. + public static void Modulate(ref ColorBGRA left, ref ColorBGRA right, out ColorBGRA result) + { + result.A = (byte)(left.A * right.A / 255); + result.R = (byte)(left.R * right.R / 255); + result.G = (byte)(left.G * right.G / 255); + result.B = (byte)(left.B * right.B / 255); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static ColorBGRA Modulate(ColorBGRA left, ColorBGRA right) + { + return new ColorBGRA((byte)((left.R * right.R) / 255), (byte)((left.G * right.G) / 255), (byte)((left.B * right.B) / 255), (byte)((left.A * right.A) / 255)); + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// When the method completes, contains the scaled color. + public static void Scale(ref ColorBGRA value, float scale, out ColorBGRA result) + { + result.A = (byte)(value.A * scale); + result.R = (byte)(value.R * scale); + result.G = (byte)(value.G * scale); + result.B = (byte)(value.B * scale); + } + + /// + /// Scales a color. + /// + /// The color to scale. + /// The amount by which to scale. + /// The scaled color. + public static ColorBGRA Scale(ColorBGRA value, float scale) + { + return new ColorBGRA((byte)(value.R * scale), (byte)(value.G * scale), (byte)(value.B * scale), (byte)(value.A * scale)); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// When the method completes, contains the negated color. + public static void Negate(ref ColorBGRA value, out ColorBGRA result) + { + result.A = (byte)(255 - value.A); + result.R = (byte)(255 - value.R); + result.G = (byte)(255 - value.G); + result.B = (byte)(255 - value.B); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// The negated color. + public static ColorBGRA Negate(ColorBGRA value) + { + return new ColorBGRA((byte)(255 - value.R), (byte)(255 - value.G), (byte)(255 - value.B), (byte)(255 - value.A)); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref ColorBGRA value, ref ColorBGRA min, ref ColorBGRA max, out ColorBGRA result) + { + byte alpha = value.A; + alpha = (alpha > max.A) ? max.A : alpha; + alpha = (alpha < min.A) ? min.A : alpha; + + byte red = value.R; + red = (red > max.R) ? max.R : red; + red = (red < min.R) ? min.R : red; + + byte green = value.G; + green = (green > max.G) ? max.G : green; + green = (green < min.G) ? min.G : green; + + byte blue = value.B; + blue = (blue > max.B) ? max.B : blue; + blue = (blue < min.B) ? min.B : blue; + + result = new ColorBGRA(red, green, blue, alpha); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static ColorBGRA Clamp(ColorBGRA value, ColorBGRA min, ColorBGRA max) + { + ColorBGRA result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two colors. + /// + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref ColorBGRA start, ref ColorBGRA end, float amount, out ColorBGRA result) + { + result.B = MathUtil.Lerp(start.B, end.B, amount); + result.G = MathUtil.Lerp(start.G, end.G, amount); + result.R = MathUtil.Lerp(start.R, end.R, amount); + result.A = MathUtil.Lerp(start.A, end.A, amount); + } + + /// + /// Performs a linear interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two colors. + /// + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static ColorBGRA Lerp(ColorBGRA start, ColorBGRA end, float amount) + { + ColorBGRA result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two colors. + public static void SmoothStep(ref ColorBGRA start, ref ColorBGRA end, float amount, out ColorBGRA result) + { + amount = MathUtil.SmoothStep(amount); + Lerp(ref start, ref end, amount, out result); + } + + /// + /// Performs a cubic interpolation between two colors. + /// + /// Start color. + /// End color. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two colors. + public static ColorBGRA SmoothStep(ColorBGRA start, ColorBGRA end, float amount) + { + ColorBGRA result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Returns a color containing the smallest components of the specified colorss. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the largest components of the source colorss. + public static void Max(ref ColorBGRA left, ref ColorBGRA right, out ColorBGRA result) + { + result.A = (left.A > right.A) ? left.A : right.A; + result.R = (left.R > right.R) ? left.R : right.R; + result.G = (left.G > right.G) ? left.G : right.G; + result.B = (left.B > right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the largest components of the specified colorss. + /// + /// The first source color. + /// The second source color. + /// A color containing the largest components of the source colors. + public static ColorBGRA Max(ColorBGRA left, ColorBGRA right) + { + ColorBGRA result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// When the method completes, contains an new color composed of the smallest components of the source colors. + public static void Min(ref ColorBGRA left, ref ColorBGRA right, out ColorBGRA result) + { + result.A = (left.A < right.A) ? left.A : right.A; + result.R = (left.R < right.R) ? left.R : right.R; + result.G = (left.G < right.G) ? left.G : right.G; + result.B = (left.B < right.B) ? left.B : right.B; + } + + /// + /// Returns a color containing the smallest components of the specified colors. + /// + /// The first source color. + /// The second source color. + /// A color containing the smallest components of the source colors. + public static ColorBGRA Min(ColorBGRA left, ColorBGRA right) + { + ColorBGRA result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// When the method completes, contains the adjusted color. + public static void AdjustContrast(ref ColorBGRA value, float contrast, out ColorBGRA result) + { + result.A = value.A; + result.R = ToByte(0.5f + contrast * (value.R / 255.0f - 0.5f)); + result.G = ToByte(0.5f + contrast * (value.G / 255.0f - 0.5f)); + result.B = ToByte(0.5f + contrast * (value.B / 255.0f - 0.5f)); + } + + /// + /// Adjusts the contrast of a color. + /// + /// The color whose contrast is to be adjusted. + /// The amount by which to adjust the contrast. + /// The adjusted color. + public static ColorBGRA AdjustContrast(ColorBGRA value, float contrast) + { + return new ColorBGRA( + ToByte(0.5f + contrast * (value.R / 255.0f - 0.5f)), + ToByte(0.5f + contrast * (value.G / 255.0f - 0.5f)), + ToByte(0.5f + contrast * (value.B / 255.0f - 0.5f)), + value.A); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// When the method completes, contains the adjusted color. + public static void AdjustSaturation(ref ColorBGRA value, float saturation, out ColorBGRA result) + { + float grey = value.R / 255.0f * 0.2125f + value.G / 255.0f * 0.7154f + value.B / 255.0f * 0.0721f; + + result.A = value.A; + result.R = ToByte(grey + saturation * (value.R / 255.0f - grey)); + result.G = ToByte(grey + saturation * (value.G / 255.0f - grey)); + result.B = ToByte(grey + saturation * (value.B / 255.0f - grey)); + } + + /// + /// Adjusts the saturation of a color. + /// + /// The color whose saturation is to be adjusted. + /// The amount by which to adjust the saturation. + /// The adjusted color. + public static ColorBGRA AdjustSaturation(ColorBGRA value, float saturation) + { + float grey = value.R / 255.0f * 0.2125f + value.G / 255.0f * 0.7154f + value.B / 255.0f * 0.0721f; + + return new ColorBGRA( + ToByte(grey + saturation * (value.R / 255.0f - grey)), + ToByte(grey + saturation * (value.G / 255.0f - grey)), + ToByte(grey + saturation * (value.B / 255.0f - grey)), + value.A); + } + + /// + /// Adds two colors. + /// + /// The first color to add. + /// The second color to add. + /// The sum of the two colors. + public static ColorBGRA operator +(ColorBGRA left, ColorBGRA right) + { + return new ColorBGRA((byte)(left.R + right.R), (byte)(left.G + right.G), (byte)(left.B + right.B), (byte)(left.A + right.A)); + } + + /// + /// Assert a color (return it unchanged). + /// + /// The color to assert (unchange). + /// The asserted (unchanged) color. + public static ColorBGRA operator +(ColorBGRA value) + { + return value; + } + + /// + /// Subtracts two colors. + /// + /// The first color to subtract. + /// The second color to subtract. + /// The difference of the two colors. + public static ColorBGRA operator -(ColorBGRA left, ColorBGRA right) + { + return new ColorBGRA((byte)(left.R - right.R), (byte)(left.G - right.G), (byte)(left.B - right.B), (byte)(left.A - right.A)); + } + + /// + /// Negates a color. + /// + /// The color to negate. + /// A negated color. + public static ColorBGRA operator -(ColorBGRA value) + { + return new ColorBGRA(-value.R, -value.G, -value.B, -value.A); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static ColorBGRA operator *(float scale, ColorBGRA value) + { + return new ColorBGRA((byte)(value.R * scale), (byte)(value.G * scale), (byte)(value.B * scale), (byte)(value.A * scale)); + } + + /// + /// Scales a color. + /// + /// The factor by which to scale the color. + /// The color to scale. + /// The scaled color. + public static ColorBGRA operator *(ColorBGRA value, float scale) + { + return new ColorBGRA((byte)(value.R * scale), (byte)(value.G * scale), (byte)(value.B * scale), (byte)(value.A * scale)); + } + + /// + /// Modulates two colors. + /// + /// The first color to modulate. + /// The second color to modulate. + /// The modulated color. + public static ColorBGRA operator *(ColorBGRA left, ColorBGRA right) + { + return new ColorBGRA((byte)(left.R * right.R / 255.0f), (byte)(left.G * right.G / 255.0f), (byte)(left.B * right.B / 255.0f), (byte)(left.A * right.A / 255.0f)); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(ColorBGRA left, ColorBGRA right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(ColorBGRA left, ColorBGRA right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color3(ColorBGRA value) + { + return new Color3(value.R, value.G, value.B); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(ColorBGRA value) + { + return new Vector3(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(ColorBGRA value) + { + return new Vector4(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f, value.A / 255.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Color4(ColorBGRA value) + { + return new Color4(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f, value.A / 255.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator ColorBGRA(Vector3 value) + { + return new ColorBGRA(value.X / 255.0f, value.Y / 255.0f, value.Z / 255.0f, 1.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator ColorBGRA(Color3 value) + { + return new ColorBGRA(value.R, value.G, value.B, 1.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator ColorBGRA(Vector4 value) + { + return new ColorBGRA(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator ColorBGRA(Color4 value) + { + return new ColorBGRA(value.R, value.G, value.B, value.A); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator ColorBGRA(Color value) + { + return new ColorBGRA(value.R, value.G, value.B, value.A); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Color(ColorBGRA value) + { + return new Color(value.R, value.G, value.B, value.A); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// + /// The result of the conversion. + /// + public static explicit operator int(ColorBGRA value) + { + return value.ToBgra(); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// + /// The result of the conversion. + /// + public static explicit operator ColorBGRA(int value) + { + return new ColorBGRA(value); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return ToString(CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format to apply to each channel (byte). + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, ToStringFormat, A, R, G, B); + } + + /// + /// Returns a that represents this instance. + /// + /// The format to apply to each channel (byte). + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, ToStringFormat, + A.ToString(format, formatProvider), + R.ToString(format, formatProvider), + G.ToString(format, formatProvider), + B.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return A.GetHashCode() + R.GetHashCode() + G.GetHashCode() + B.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(ColorBGRA other) + { + return R == other.R && G == other.G && B == other.B && A == other.A; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (!ReferenceEquals(value.GetType(), typeof(ColorBGRA))) + return false; + + return Equals((ColorBGRA)value); + } + + private static byte ToByte(float component) + { + var value = (int)(component * 255.0f); + return (byte)(value < 0 ? 0 : value > 255 ? 255 : value); + } + } +} diff --git a/math/ColorExtensions.cs b/math/ColorExtensions.cs new file mode 100644 index 0000000..c811b2e --- /dev/null +++ b/math/ColorExtensions.cs @@ -0,0 +1,94 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System.Globalization; +using Xenko.Core.Annotations; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// A class containing extension methods for processing colors. + /// + public static class ColorExtensions + { + /// + /// Indicates if the given string can be converted to an RGBA value using . + /// + /// The string to convert. + /// True if the string can be converted, false otherwise. + public static bool CanConvertStringToRgba([CanBeNull] string stringColor) + { + return stringColor?.StartsWith("#") ?? false; + } + + /// + /// Converts the given string to an RGBA value. + /// + /// The string to convert. + /// The converted RGBA value. + public static uint StringToRgba([CanBeNull] string stringColor) + { + var intValue = 0xFF000000; + if (stringColor != null) + { + if (stringColor.StartsWith("#")) + { + if (stringColor.Length == "#000".Length && uint.TryParse(stringColor.Substring(1, 3), NumberStyles.HexNumber, null, out intValue)) + { + intValue = ((intValue & 0x00F) << 16) + | ((intValue & 0x00F) << 20) + | ((intValue & 0x0F0) << 4) + | ((intValue & 0x0F0) << 8) + | ((intValue & 0xF00) >> 4) + | ((intValue & 0xF00) >> 8) + | (0xFF000000); + } + if (stringColor.Length == "#000000".Length && uint.TryParse(stringColor.Substring(1, 6), NumberStyles.HexNumber, null, out intValue)) + { + intValue = ((intValue & 0x000000FF) << 16) + | (intValue & 0x0000FF00) + | ((intValue & 0x00FF0000) >> 16) + | (0xFF000000); + } + if (stringColor.Length == "#00000000".Length && uint.TryParse(stringColor.Substring(1, 8), NumberStyles.HexNumber, null, out intValue)) + { + intValue = ((intValue & 0x000000FF) << 16) + | (intValue & 0x0000FF00) + | ((intValue & 0x00FF0000) >> 16) + | (intValue & 0xFF000000); + } + } + } + return intValue; + } + + /// + /// Converts the given RGB value to a string. + /// + /// The RGB value to convert. + /// The converted string. + [NotNull] + public static string RgbToString(int value) + { + var r = (value & 0x000000FF); + var g = (value & 0x0000FF00) >> 8; + var b = (value & 0x00FF0000) >> 16; + return $"#{r:X2}{g:X2}{b:X2}"; + } + + /// + /// Converts the given RGBA value to a string. + /// + /// The RGBA value to convert. + /// The converted string. + [NotNull] + public static string RgbaToString(int value) + { + var r = (value & 0x000000FF); + var g = (value & 0x0000FF00) >> 8; + var b = (value & 0x00FF0000) >> 16; + var a = (value & 0xFF000000) >> 24; + return $"#{a:X2}{r:X2}{g:X2}{b:X2}"; + } + } +} diff --git a/math/ColorHSV.cs b/math/ColorHSV.cs new file mode 100644 index 0000000..b328943 --- /dev/null +++ b/math/ColorHSV.cs @@ -0,0 +1,203 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a color in the form of Hue, Saturation, Value, Alpha. + /// + [DataContract( Name = "ColorHSV")] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct ColorHSV : IEquatable, IFormattable + { + private const string ToStringFormat = "Hue:{0} Saturation:{1} Value:{2} Alpha:{3}"; + + /// + /// The Hue of the color. + /// + [DataMember( Order = 0 )] + public float H; + + /// + /// The Saturation of the color. + /// + [DataMember( Order = 1 )] + public float S; + + /// + /// The Value of the color. + /// + [DataMember( Order = 2 )] + public float V; + + /// + /// The alpha component of the color. + /// + [DataMember( Order = 3 )] + public float A; + + /// + /// Initializes a new instance of the struct. + /// + /// The h. + /// The s. + /// The v. + /// A. + public ColorHSV(float h, float s, float v, float a) + { + H = h; + S = s; + V = v; + A = a; + } + + /// + /// Converts the color into a three component vector. + /// + /// A three component vector containing the red, green, and blue components of the color. + public Color4 ToColor() + { + float hdiv = H / 60; + int hi = (int)hdiv; + float f = hdiv - hi; + + float p = V * (1 - S); + float q = V * (1 - (S * f)); + float t = V * (1 - (S * (1 - f))); + + switch (hi) + { + case 0: + return new Color4(V, t, p, A); + case 1: + return new Color4(q, V, p, A); + case 2: + return new Color4(p, V, t, A); + case 3: + return new Color4(p, q, V, A); + case 4: + return new Color4(t, p, V, A); + default: + return new Color4(V, p, q, A); + } + } + + /// + /// Converts the color into a HSV color. + /// + /// The color. + /// A HSV color + public static ColorHSV FromColor(Color4 color) + { + float max = Math.Max(color.R, Math.Max(color.G, color.B)); + float min = Math.Min(color.R, Math.Min(color.G, color.B)); + + float delta = max - min; + float h = 0.0f; + + if (delta > 0.0f) + { + if (color.R >= max) + h = (color.G - color.B) / delta; + else if (color.G >= max) + h = (color.B - color.R) / delta + 2.0f; + else + h = (color.R - color.G) / delta + 4.0f; + h *= 60.0f; + + if (h < 0) + h += 360f; + } + + float s = MathUtil.IsZero(max) ? 0.0f : delta / max; + + return new ColorHSV(h, s, max, color.A); + } + + /// + public bool Equals(ColorHSV other) + { + return other.H.Equals(H) && other.S.Equals(S) && other.V.Equals(V) && other.A.Equals(A); + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof(ColorHSV)) return false; + return Equals((ColorHSV)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + int result = H.GetHashCode(); + result = (result * 397) ^ S.GetHashCode(); + result = (result * 397) ^ V.GetHashCode(); + result = (result * 397) ^ A.GetHashCode(); + return result; + } + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return ToString(CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, ToStringFormat, H, S, V, A); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, ToStringFormat, + H.ToString(format, formatProvider), + S.ToString(format, formatProvider), + V.ToString(format, formatProvider), + A.ToString(format, formatProvider)); + } + } +} diff --git a/math/ContainmentType.cs b/math/ContainmentType.cs new file mode 100644 index 0000000..bcd3288 --- /dev/null +++ b/math/ContainmentType.cs @@ -0,0 +1,51 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +namespace math +{ + /// + /// Describes how one bounding volume contains another. + /// + public enum ContainmentType + { + /// + /// The two bounding volumes don't intersect at all. + /// + Disjoint, + + /// + /// One bounding volume completely contains another. + /// + Contains, + + /// + /// The two bounding volumes overlap. + /// + Intersects, + } +} diff --git a/math/DataStyleAttribute.cs b/math/DataStyleAttribute.cs new file mode 100644 index 0000000..d586604 --- /dev/null +++ b/math/DataStyleAttribute.cs @@ -0,0 +1,53 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; + +//TODO @@ Move this stuff + +namespace math +{ + public enum DataStyle + { + Invalid = 0, + Compact = 1, + + } + + public class DataStyleAttribute : Attribute + { + + public DataStyle style { get; private set; } + + public DataStyleAttribute( DataStyle n_style ) + { + style = n_style; + } + + } +} \ No newline at end of file diff --git a/math/Double2.cs b/math/Double2.cs new file mode 100644 index 0000000..309dc83 --- /dev/null +++ b/math/Double2.cs @@ -0,0 +1,1486 @@ +// Copyright (c) Xenko contributors. (https://xenko.com) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a two dimensional mathematical vector with double-precision floats. + /// + [DataContract( Name = "double2")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Double2 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Double2 Zero = new Double2(); + + /// + /// The X unit (1, 0). + /// + public static readonly Double2 UnitX = new Double2(1.0, 0.0); + + /// + /// The Y unit (0, 1). + /// + public static readonly Double2 UnitY = new Double2(0.0, 1.0); + + /// + /// A with all of its components set to one. + /// + public static readonly Double2 One = new Double2(1.0, 1.0); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public double X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public double Y; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Double2(double value) + { + X = value; + Y = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + public Double2(double x, double y) + { + X = x; + Y = y; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X and Y components of the vector. This must be an array with two elements. + /// Thrown when is null. + /// Thrown when contains more or less than two elements. + public Double2(double[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 2) + throw new ArgumentOutOfRangeException("values", "There must be two and only two input values for Double2."); + + X = values[0]; + Y = values[1]; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The Vector2 to construct the Double2 from. + public Double2(Vec2 v) + { + X = v.X; + Y = v.Y; + } + + /// + /// Gets a value indicting whether this instance is normalized. + /// + public bool IsNormalized + { + get { return Math.Abs((X * X) + (Y * Y) - 1f) < MathUtil.ZeroTolerance; } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X or Y component, depending on the index. + /// The index of the component to access. Use 0 for the X component and 1 for the Y component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 1]. + public double this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Double2 run from 0 to 1, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Double2 run from 0 to 1, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Length() + { + return (double)Math.Sqrt((X * X) + (Y * Y)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double LengthSquared() + { + return (X * X) + (Y * Y); + } + + /// + /// Converts the vector into a unit vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Normalize() + { + double length = Length(); + if (length > MathUtil.ZeroTolerance) + { + double inv = 1.0 / length; + X *= inv; + Y *= inv; + } + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A two-element array containing the components of the vector. + public double[] ToArray() + { + return new double[] { X, Y }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Double2 left, ref Double2 right, out Double2 result) + { + result = new Double2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Add(Double2 left, Double2 right) + { + return new Double2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Subtract(ref Double2 left, ref Double2 right, out Double2 result) + { + result = new Double2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Subtract(Double2 left, Double2 right) + { + return new Double2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(ref Double2 value, double scale, out Double2 result) + { + result = new Double2(value.X * scale, value.Y * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Multiply(Double2 value, double scale) + { + return new Double2(value.X * scale, value.Y * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Modulate(ref Double2 left, ref Double2 right, out Double2 result) + { + result = new Double2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Modulate(Double2 left, Double2 right) + { + return new Double2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(ref Double2 value, double scale, out Double2 result) + { + result = new Double2(value.X / scale, value.Y / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Divide(Double2 value, double scale) + { + return new Double2(value.X / scale, value.Y / scale); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// When the method completes, contains the demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Demodulate(ref Double2 left, ref Double2 right, out Double2 result) + { + result = new Double2(left.X / right.X, left.Y / right.Y); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// The demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Demodulate(Double2 left, Double2 right) + { + return new Double2(left.X / right.X, left.Y / right.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Negate(ref Double2 value, out Double2 result) + { + result = new Double2(-value.X, -value.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Negate(Double2 value) + { + return new Double2(-value.X, -value.Y); + } + + /// + /// Returns a containing the 2D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. + /// + /// A containing the 2D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// When the method completes, contains the 2D Cartesian coordinates of the specified point. + public static void Barycentric(ref Double2 value1, ref Double2 value2, ref Double2 value3, double amount1, double amount2, out Double2 result) + { + result = new Double2((value1.X + (amount1 * (value2.X - value1.X))) + (amount2 * (value3.X - value1.X)), + (value1.Y + (amount1 * (value2.Y - value1.Y))) + (amount2 * (value3.Y - value1.Y))); + } + + /// + /// Returns a containing the 2D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. + /// + /// A containing the 2D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// A new containing the 2D Cartesian coordinates of the specified point. + public static Double2 Barycentric(Double2 value1, Double2 value2, Double2 value3, double amount1, double amount2) + { + Double2 result; + Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); + return result; + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Double2 value, ref Double2 min, ref Double2 max, out Double2 result) + { + double x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + double y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + result = new Double2(x, y); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Double2 Clamp(Double2 value, Double2 min, Double2 max) + { + Double2 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static void Distance(ref Double2 value1, ref Double2 value2, out double result) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + + result = (double)Math.Sqrt((x * x) + (y * y)); + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static double Distance(Double2 value1, Double2 value2) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + + return (double)Math.Sqrt((x * x) + (y * y)); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector + /// When the method completes, contains the squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static void DistanceSquared(ref Double2 value1, ref Double2 value2, out double result) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + + result = (x * x) + (y * y); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static double DistanceSquared(Double2 value1, Double2 value2) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + + return (x * x) + (y * y); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dot(ref Double2 left, ref Double2 right, out double result) + { + result = (left.X * right.X) + (left.Y * right.Y); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Dot(Double2 left, Double2 right) + { + return (left.X * right.X) + (left.Y * right.Y); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// When the method completes, contains the normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ref Double2 value, out Double2 result) + { + result = value; + result.Normalize(); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// The normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Normalize(Double2 value) + { + value.Normalize(); + return value; + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Double2 start, ref Double2 end, double amount, out Double2 result) + { + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Double2 Lerp(Double2 start, Double2 end, double amount) + { + Double2 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Double2 start, ref Double2 end, double amount, out Double2 result) + { + amount = (amount > 1.0) ? 1.0 : ((amount < 0.0) ? 0.0 : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Double2 SmoothStep(Double2 start, Double2 end, double amount) + { + Double2 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// When the method completes, contains the result of the Hermite spline interpolation. + public static void Hermite(ref Double2 value1, ref Double2 tangent1, ref Double2 value2, ref Double2 tangent2, double amount, out Double2 result) + { + double squared = amount * amount; + double cubed = amount * squared; + double part1 = ((2.0f * cubed) - (3.0f * squared)) + 1.0; + double part2 = (-2.0f * cubed) + (3.0f * squared); + double part3 = (cubed - (2.0f * squared)) + amount; + double part4 = cubed - squared; + + result.X = (((value1.X * part1) + (value2.X * part2)) + (tangent1.X * part3)) + (tangent2.X * part4); + result.Y = (((value1.Y * part1) + (value2.Y * part2)) + (tangent1.Y * part3)) + (tangent2.Y * part4); + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// The result of the Hermite spline interpolation. + public static Double2 Hermite(Double2 value1, Double2 tangent1, Double2 value2, Double2 tangent2, double amount) + { + Double2 result; + Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result); + return result; + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// When the method completes, contains the result of the Catmull-Rom interpolation. + public static void CatmullRom(ref Double2 value1, ref Double2 value2, ref Double2 value3, ref Double2 value4, double amount, out Double2 result) + { + double squared = amount * amount; + double cubed = amount * squared; + + result.X = 0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * squared)) + + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * cubed)); + + result.Y = 0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * squared)) + + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * cubed)); + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// A vector that is the result of the Catmull-Rom interpolation. + public static Double2 CatmullRom(Double2 value1, Double2 value2, Double2 value3, Double2 value4, double amount) + { + Double2 result; + CatmullRom(ref value1, ref value2, ref value3, ref value4, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Max(ref Double2 left, ref Double2 right, out Double2 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Max(Double2 left, Double2 right) + { + Double2 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Min(ref Double2 left, ref Double2 right, out Double2 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 Min(Double2 left, Double2 right) + { + Double2 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// When the method completes, contains the reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static void Reflect(ref Double2 vector, ref Double2 normal, out Double2 result) + { + double dot = (vector.X * normal.X) + (vector.Y * normal.Y); + + result.X = vector.X - ((2.0f * dot) * normal.X); + result.Y = vector.Y - ((2.0f * dot) * normal.Y); + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// The reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static Double2 Reflect(Double2 vector, Double2 normal) + { + Double2 result; + Reflect(ref vector, ref normal, out result); + return result; + } + + /// + /// Orthogonalizes a list of vectors. + /// + /// The list of orthogonalized vectors. + /// The list of vectors to orthogonalize. + /// + /// Orthogonalization is the process of making all vectors orthogonal to each other. This + /// means that any given vector in the list will be orthogonal to any other given vector in the + /// list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthogonalize(Double2[] destination, params Double2[] source) + { + //Uses the modified Gram-Schmidt process. + //q1 = m1 + //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 + //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 + //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Double2 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= (Double2.Dot(destination[r], newvector) / Double2.Dot(destination[r], destination[r])) * destination[r]; + } + + destination[i] = newvector; + } + } + + /// + /// Orthonormalizes a list of vectors. + /// + /// The list of orthonormalized vectors. + /// The list of vectors to orthonormalize. + /// + /// Orthonormalization is the process of making all vectors orthogonal to each + /// other and making all vectors of unit length. This means that any given vector will + /// be orthogonal to any other given vector in the list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthonormalize(Double2[] destination, params Double2[] source) + { + //Uses the modified Gram-Schmidt process. + //Because we are making unit vectors, we can optimize the math for orthogonalization + //and simplify the projection operation to remove the division. + //q1 = m1 / |m1| + //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| + //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| + //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Double2 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= Double2.Dot(destination[r], newvector) * destination[r]; + } + + newvector.Normalize(); + destination[i] = newvector; + } + } + + /// + /// Transforms a 2D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// When the method completes, contains the transformed . + public static void Transform(ref Double2 vector, ref Quaternion rotation, out Double2 result) + { + double x = rotation.X + rotation.X; + double y = rotation.Y + rotation.Y; + double z = rotation.Z + rotation.Z; + double wz = rotation.W * z; + double xx = rotation.X * x; + double xy = rotation.X * y; + double yy = rotation.Y * y; + double zz = rotation.Z * z; + + result = new Double2((vector.X * (1.0 - yy - zz)) + (vector.Y * (xy - wz)), (vector.X * (xy + wz)) + (vector.Y * (1.0 - xx - zz))); + } + + /// + /// Transforms a 2D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// The transformed . + public static Double2 Transform(Double2 vector, Quaternion rotation) + { + Double2 result; + Transform(ref vector, ref rotation, out result); + return result; + } + + /// + /// Transforms an array of vectors by the given rotation. + /// + /// The array of vectors to transform. + /// The rotation to apply. + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Double2[] source, ref Quaternion rotation, Double2[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + double x = rotation.X + rotation.X; + double y = rotation.Y + rotation.Y; + double z = rotation.Z + rotation.Z; + double wz = rotation.W * z; + double xx = rotation.X * x; + double xy = rotation.X * y; + double yy = rotation.Y * y; + double zz = rotation.Z * z; + + double num1 = (1.0 - yy - zz); + double num2 = (xy - wz); + double num3 = (xy + wz); + double num4 = (1.0 - xx - zz); + + for (int i = 0; i < source.Length; ++i) + { + destination[i] = new Double2( + (source[i].X * num1) + (source[i].Y * num2), + (source[i].X * num3) + (source[i].Y * num4)); + } + } + + /// + /// Transforms a 2D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Double2 vector, ref Matrix transform, out Double4 result) + { + result = new Double4( + (vector.X * transform.M11) + (vector.Y * transform.M21) + transform.M41, + (vector.X * transform.M12) + (vector.Y * transform.M22) + transform.M42, + (vector.X * transform.M13) + (vector.Y * transform.M23) + transform.M43, + (vector.X * transform.M14) + (vector.Y * transform.M24) + transform.M44); + } + + /// + /// Transforms a 2D vector by the given . + /// + /// The source vector. + /// The transformation . + /// The transformed . + public static Double4 Transform(Double2 vector, Matrix transform) + { + Double4 result; + Transform(ref vector, ref transform, out result); + return result; + } + + /// + /// Transforms an array of 2D vectors by the given . + /// + /// The array of vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Double2[] source, ref Matrix transform, Double4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Transform(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// When the method completes, contains the transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(ref Double2 coordinate, ref Matrix transform, out Double2 result) + { + Double4 vector = new Double4(); + vector.X = (coordinate.X * transform.M11) + (coordinate.Y * transform.M21) + transform.M41; + vector.Y = (coordinate.X * transform.M12) + (coordinate.Y * transform.M22) + transform.M42; + vector.Z = (coordinate.X * transform.M13) + (coordinate.Y * transform.M23) + transform.M43; + vector.W = 1f / ((coordinate.X * transform.M14) + (coordinate.Y * transform.M24) + transform.M44); + + result = new Double2(vector.X * vector.W, vector.Y * vector.W); + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// The transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static Double2 TransformCoordinate(Double2 coordinate, Matrix transform) + { + Double2 result; + TransformCoordinate(ref coordinate, ref transform, out result); + return result; + } + + /// + /// Performs a coordinate transformation on an array of vectors using the given . + /// + /// The array of coordinate vectors to trasnform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(Double2[] source, ref Matrix transform, Double2[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformCoordinate(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// When the method completes, contains the transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(ref Double2 normal, ref Matrix transform, out Double2 result) + { + result = new Double2( + (normal.X * transform.M11) + (normal.Y * transform.M21), + (normal.X * transform.M12) + (normal.Y * transform.M22)); + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// The transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static Double2 TransformNormal(Double2 normal, Matrix transform) + { + Double2 result; + TransformNormal(ref normal, ref transform, out result); + return result; + } + + /// + /// Performs a normal transformation on an array of vectors using the given . + /// + /// The array of normal vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(Double2[] source, ref Matrix transform, Double2[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformNormal(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator +(Double2 left, Double2 right) + { + return new Double2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator +(Double2 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator -(Double2 left, Double2 right) + { + return new Double2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator -(Double2 value) + { + return new Double2(-value.X, -value.Y); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to multiply. + /// The second vector to multiply. + /// The multiplication of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator *(Double2 left, Double2 right) + { + return new Double2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator *(double scale, Double2 value) + { + return new Double2(value.X * scale, value.Y * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator *(Double2 value, double scale) + { + return new Double2(value.X * scale, value.Y * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator /(Double2 value, double scale) + { + return new Double2(value.X / scale, value.Y / scale); + } + + /// + /// Divides a numerator by a vector. + /// + /// The numerator. + /// The value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator /(double numerator, Double2 value) + { + return new Double2(numerator / value.X, numerator / value.Y); + } + + /// + /// Divides a vector by the given vector, component-wise. + /// + /// The vector to scale. + /// The by. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double2 operator /(Double2 value, Double2 by) + { + return new Double2(value.X / by.X, value.Y / by.Y); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Double2 left, Double2 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Double2 left, Double2 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(Double2 value) + { + return new Vec2((float)value.X, (float)value.Y); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Double2(Vec2 value) + { + return new Double2(value); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Half2(Double2 value) + { + return new Half2((Half)value.X, (Half)value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double2(Half2 value) + { + return new Double2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double3(Double2 value) + { + return new Double3(value, 0.0); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double4(Double2 value) + { + return new Double4(value, 0.0, 0.0); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X, Y); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X.ToString(format, CultureInfo.CurrentCulture), Y.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1}", X, Y); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1}", X.ToString(format, formatProvider), Y.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Double2 other) + { + return ((double)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (double)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Double2)value); + } + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Point(Double2 value) + { + return new System.Windows.Point(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double2(System.Windows.Point value) + { + return new Double2(value.X, value.Y); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Vector2(Double2 value) + { + return new Microsoft.Xna.Framework.Vector2(value.X, value.Y); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Double2(Microsoft.Xna.Framework.Vector2 value) + { + return new Double2(value.X, value.Y); + } +#endif + } +} diff --git a/math/Double3.cs b/math/Double3.cs new file mode 100644 index 0000000..531f71c --- /dev/null +++ b/math/Double3.cs @@ -0,0 +1,1765 @@ +// Copyright (c) Xenko contributors. (https://xenko.com) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a three dimensional mathematical vector with double-precision floats. + /// + [DataContract( Name = "double3")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Double3 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Double3 Zero = new Double3(); + + /// + /// The X unit (1, 0, 0). + /// + public static readonly Double3 UnitX = new Double3(1.0, 0.0, 0.0); + + /// + /// The Y unit (0, 1, 0). + /// + public static readonly Double3 UnitY = new Double3(0.0, 1.0, 0.0); + + /// + /// The Z unit (0, 0, 1). + /// + public static readonly Double3 UnitZ = new Double3(0.0, 0.0, 1.0); + + /// + /// A with all of its components set to one. + /// + public static readonly Double3 One = new Double3(1.0, 1.0, 1.0); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public double X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public double Y; + + /// + /// The Z component of the vector. + /// + [DataMember( Order = 2 )] + public double Z; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Double3(double value) + { + X = value; + Y = value; + Z = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + /// Initial value for the Z component of the vector. + public Double3(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + /// Initial value for the Z component of the vector. + public Double3(Double2 value, double z) + { + X = value.X; + Y = value.Y; + Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, and Z components of the vector. This must be an array with three elements. + /// Thrown when is null. + /// Thrown when contains more or less than three elements. + public Double3(double[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 3) + throw new ArgumentOutOfRangeException("values", "There must be three and only three input values for Double3."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The Vector3 to construct the Double3 from. + public Double3(Vector3 v) + { + X = v.X; + Y = v.Y; + Z = v.Z; + } + + /// + /// Gets a value indicting whether this instance is normalized. + /// + public bool IsNormalized + { + get { return Math.Abs((X * X) + (Y * Y) + (Z * Z) - 1f) < MathUtil.ZeroTolerance; } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, or Z component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, and 2 for the Z component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 2]. + public double this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Double3 run from 0 to 2, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Double3 run from 0 to 2, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Length() + { + return (double)Math.Sqrt((X * X) + (Y * Y) + (Z * Z)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double LengthSquared() + { + return (X * X) + (Y * Y) + (Z * Z); + } + + /// + /// Converts the vector into a unit vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Normalize() + { + double length = Length(); + if (length > MathUtil.ZeroTolerance) + { + double inv = 1.0 / length; + X *= inv; + Y *= inv; + Z *= inv; + } + } + + /// + /// Raises the exponent for each components. + /// + /// The exponent. + public void Pow(double exponent) + { + X = (double)Math.Pow(X, exponent); + Y = (double)Math.Pow(Y, exponent); + Z = (double)Math.Pow(Z, exponent); + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A three-element array containing the components of the vector. + public double[] ToArray() + { + return new double[] { X, Y, Z }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Double3 left, ref Double3 right, out Double3 result) + { + result = new Double3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Add(Double3 left, Double3 right) + { + return new Double3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Subtract(ref Double3 left, ref Double3 right, out Double3 result) + { + result = new Double3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Subtract(Double3 left, Double3 right) + { + return new Double3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(ref Double3 value, double scale, out Double3 result) + { + result = new Double3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Multiply(Double3 value, double scale) + { + return new Double3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Modulate(ref Double3 left, ref Double3 right, out Double3 result) + { + result = new Double3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Modulate(Double3 left, Double3 right) + { + return new Double3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(ref Double3 value, double scale, out Double3 result) + { + result = new Double3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Divide(Double3 value, double scale) + { + return new Double3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// When the method completes, contains the demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Demodulate(ref Double3 left, ref Double3 right, out Double3 result) + { + result = new Double3(left.X / right.X, left.Y / right.Y, left.Z / right.Z); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// The demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Demodulate(Double3 left, Double3 right) + { + return new Double3(left.X / right.X, left.Y / right.Y, left.Z / right.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Negate(ref Double3 value, out Double3 result) + { + result = new Double3(-value.X, -value.Y, -value.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Negate(Double3 value) + { + return new Double3(-value.X, -value.Y, -value.Z); + } + + /// + /// Returns a containing the 3D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 3D triangle. + /// + /// A containing the 3D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// When the method completes, contains the 3D Cartesian coordinates of the specified point. + public static void Barycentric(ref Double3 value1, ref Double3 value2, ref Double3 value3, double amount1, double amount2, out Double3 result) + { + result = new Double3((value1.X + (amount1 * (value2.X - value1.X))) + (amount2 * (value3.X - value1.X)), + (value1.Y + (amount1 * (value2.Y - value1.Y))) + (amount2 * (value3.Y - value1.Y)), + (value1.Z + (amount1 * (value2.Z - value1.Z))) + (amount2 * (value3.Z - value1.Z))); + } + + /// + /// Returns a containing the 3D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 3D triangle. + /// + /// A containing the 3D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// A new containing the 3D Cartesian coordinates of the specified point. + public static Double3 Barycentric(Double3 value1, Double3 value2, Double3 value3, double amount1, double amount2) + { + Double3 result; + Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); + return result; + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Double3 value, ref Double3 min, ref Double3 max, out Double3 result) + { + double x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + double y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + double z = value.Z; + z = (z > max.Z) ? max.Z : z; + z = (z < min.Z) ? min.Z : z; + + result = new Double3(x, y, z); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Double3 Clamp(Double3 value, Double3 min, Double3 max) + { + Double3 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the cross product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains he cross product of the two vectors. + public static void Cross(ref Double3 left, ref Double3 right, out Double3 result) + { + result = new Double3( + (left.Y * right.Z) - (left.Z * right.Y), + (left.Z * right.X) - (left.X * right.Z), + (left.X * right.Y) - (left.Y * right.X)); + } + + /// + /// Calculates the cross product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The cross product of the two vectors. + public static Double3 Cross(Double3 left, Double3 right) + { + Double3 result; + Cross(ref left, ref right, out result); + return result; + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static void Distance(ref Double3 value1, ref Double3 value2, out double result) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + + result = (double)Math.Sqrt((x * x) + (y * y) + (z * z)); + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static double Distance(Double3 value1, Double3 value2) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + + return (double)Math.Sqrt((x * x) + (y * y) + (z * z)); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static void DistanceSquared(ref Double3 value1, ref Double3 value2, out double result) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + + result = (x * x) + (y * y) + (z * z); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static double DistanceSquared(Double3 value1, Double3 value2) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + + return (x * x) + (y * y) + (z * z); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dot(ref Double3 left, ref Double3 right, out double result) + { + result = (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Dot(Double3 left, Double3 right) + { + return (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// When the method completes, contains the normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ref Double3 value, out Double3 result) + { + result = value; + result.Normalize(); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// The normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Normalize(Double3 value) + { + value.Normalize(); + return value; + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Double3 start, ref Double3 end, double amount, out Double3 result) + { + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Double3 Lerp(Double3 start, Double3 end, double amount) + { + Double3 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Double3 start, ref Double3 end, double amount, out Double3 result) + { + amount = (amount > 1.0) ? 1.0 : ((amount < 0.0) ? 0.0 : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Double3 SmoothStep(Double3 start, Double3 end, double amount) + { + Double3 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// When the method completes, contains the result of the Hermite spline interpolation. + public static void Hermite(ref Double3 value1, ref Double3 tangent1, ref Double3 value2, ref Double3 tangent2, double amount, out Double3 result) + { + double squared = amount * amount; + double cubed = amount * squared; + double part1 = ((2.0f * cubed) - (3.0f * squared)) + 1.0; + double part2 = (-2.0f * cubed) + (3.0f * squared); + double part3 = (cubed - (2.0f * squared)) + amount; + double part4 = cubed - squared; + + result.X = (((value1.X * part1) + (value2.X * part2)) + (tangent1.X * part3)) + (tangent2.X * part4); + result.Y = (((value1.Y * part1) + (value2.Y * part2)) + (tangent1.Y * part3)) + (tangent2.Y * part4); + result.Z = (((value1.Z * part1) + (value2.Z * part2)) + (tangent1.Z * part3)) + (tangent2.Z * part4); + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// The result of the Hermite spline interpolation. + public static Double3 Hermite(Double3 value1, Double3 tangent1, Double3 value2, Double3 tangent2, double amount) + { + Double3 result; + Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result); + return result; + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// When the method completes, contains the result of the Catmull-Rom interpolation. + public static void CatmullRom(ref Double3 value1, ref Double3 value2, ref Double3 value3, ref Double3 value4, double amount, out Double3 result) + { + double squared = amount * amount; + double cubed = amount * squared; + + result.X = 0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * squared)) + + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * cubed)); + + result.Y = 0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * squared)) + + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * cubed)); + + result.Z = 0.5f * ((((2.0f * value2.Z) + ((-value1.Z + value3.Z) * amount)) + + (((((2.0f * value1.Z) - (5.0f * value2.Z)) + (4.0f * value3.Z)) - value4.Z) * squared)) + + ((((-value1.Z + (3.0f * value2.Z)) - (3.0f * value3.Z)) + value4.Z) * cubed)); + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// A vector that is the result of the Catmull-Rom interpolation. + public static Double3 CatmullRom(Double3 value1, Double3 value2, Double3 value3, Double3 value4, double amount) + { + Double3 result; + CatmullRom(ref value1, ref value2, ref value3, ref value4, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Max(ref Double3 left, ref Double3 right, out Double3 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + result.Z = (left.Z > right.Z) ? left.Z : right.Z; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Max(Double3 left, Double3 right) + { + Double3 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Min(ref Double3 left, ref Double3 right, out Double3 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + result.Z = (left.Z < right.Z) ? left.Z : right.Z; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 Min(Double3 left, Double3 right) + { + Double3 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Projects a 3D vector from object space into screen space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// When the method completes, contains the vector in screen space. + public static void Project(ref Double3 vector, double x, double y, double width, double height, double minZ, double maxZ, ref Matrix worldViewProjection, out Double3 result) + { + Double3 v; + TransformCoordinate(ref vector, ref worldViewProjection, out v); + + result = new Double3(((1.0 + v.X) * 0.5f * width) + x, ((1.0 - v.Y) * 0.5f * height) + y, (v.Z * (maxZ - minZ)) + minZ); + } + + /// + /// Projects a 3D vector from object space into screen space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// The vector in screen space. + public static Double3 Project(Double3 vector, double x, double y, double width, double height, double minZ, double maxZ, Matrix worldViewProjection) + { + Double3 result; + Project(ref vector, x, y, width, height, minZ, maxZ, ref worldViewProjection, out result); + return result; + } + + /// + /// Projects a 3D vector from screen space into object space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// When the method completes, contains the vector in object space. + public static void Unproject(ref Double3 vector, double x, double y, double width, double height, double minZ, double maxZ, ref Matrix worldViewProjection, out Double3 result) + { + Double3 v = new Double3(); + Matrix matrix; + Matrix.Invert(ref worldViewProjection, out matrix); + + v.X = (((vector.X - x) / width) * 2.0f) - 1.0; + v.Y = -((((vector.Y - y) / height) * 2.0f) - 1.0); + v.Z = (vector.Z - minZ) / (maxZ - minZ); + + TransformCoordinate(ref v, ref matrix, out result); + } + + /// + /// Projects a 3D vector from screen space into object space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// The vector in object space. + public static Double3 Unproject(Double3 vector, double x, double y, double width, double height, double minZ, double maxZ, Matrix worldViewProjection) + { + Double3 result; + Unproject(ref vector, x, y, width, height, minZ, maxZ, ref worldViewProjection, out result); + return result; + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// When the method completes, contains the reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static void Reflect(ref Double3 vector, ref Double3 normal, out Double3 result) + { + double dot = (vector.X * normal.X) + (vector.Y * normal.Y) + (vector.Z * normal.Z); + + result.X = vector.X - ((2.0f * dot) * normal.X); + result.Y = vector.Y - ((2.0f * dot) * normal.Y); + result.Z = vector.Z - ((2.0f * dot) * normal.Z); + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// The reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static Double3 Reflect(Double3 vector, Double3 normal) + { + Double3 result; + Reflect(ref vector, ref normal, out result); + return result; + } + + /// + /// Orthogonalizes a list of vectors. + /// + /// The list of orthogonalized vectors. + /// The list of vectors to orthogonalize. + /// + /// Orthogonalization is the process of making all vectors orthogonal to each other. This + /// means that any given vector in the list will be orthogonal to any other given vector in the + /// list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthogonalize(Double3[] destination, params Double3[] source) + { + //Uses the modified Gram-Schmidt process. + //q1 = m1 + //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 + //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 + //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Double3 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= (Double3.Dot(destination[r], newvector) / Double3.Dot(destination[r], destination[r])) * destination[r]; + } + + destination[i] = newvector; + } + } + + /// + /// Orthonormalizes a list of vectors. + /// + /// The list of orthonormalized vectors. + /// The list of vectors to orthonormalize. + /// + /// Orthonormalization is the process of making all vectors orthogonal to each + /// other and making all vectors of unit length. This means that any given vector will + /// be orthogonal to any other given vector in the list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthonormalize(Double3[] destination, params Double3[] source) + { + //Uses the modified Gram-Schmidt process. + //Because we are making unit vectors, we can optimize the math for orthogonalization + //and simplify the projection operation to remove the division. + //q1 = m1 / |m1| + //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| + //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| + //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Double3 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= Double3.Dot(destination[r], newvector) * destination[r]; + } + + newvector.Normalize(); + destination[i] = newvector; + } + } + + /// + /// Transforms a 3D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// When the method completes, contains the transformed . + public static void Transform(ref Double3 vector, ref Quaternion rotation, out Double3 result) + { + double x = rotation.X + rotation.X; + double y = rotation.Y + rotation.Y; + double z = rotation.Z + rotation.Z; + double wx = rotation.W * x; + double wy = rotation.W * y; + double wz = rotation.W * z; + double xx = rotation.X * x; + double xy = rotation.X * y; + double xz = rotation.X * z; + double yy = rotation.Y * y; + double yz = rotation.Y * z; + double zz = rotation.Z * z; + + result = new Double3( + ((vector.X * ((1.0 - yy) - zz)) + (vector.Y * (xy - wz))) + (vector.Z * (xz + wy)), + ((vector.X * (xy + wz)) + (vector.Y * ((1.0 - xx) - zz))) + (vector.Z * (yz - wx)), + ((vector.X * (xz - wy)) + (vector.Y * (yz + wx))) + (vector.Z * ((1.0 - xx) - yy))); + } + + /// + /// Transforms a 3D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// The transformed . + public static Double3 Transform(Double3 vector, Quaternion rotation) + { + Double3 result; + Transform(ref vector, ref rotation, out result); + return result; + } + + /// + /// Transforms an array of vectors by the given rotation. + /// + /// The array of vectors to transform. + /// The rotation to apply. + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Double3[] source, ref Quaternion rotation, Double3[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + double x = rotation.X + rotation.X; + double y = rotation.Y + rotation.Y; + double z = rotation.Z + rotation.Z; + double wx = rotation.W * x; + double wy = rotation.W * y; + double wz = rotation.W * z; + double xx = rotation.X * x; + double xy = rotation.X * y; + double xz = rotation.X * z; + double yy = rotation.Y * y; + double yz = rotation.Y * z; + double zz = rotation.Z * z; + + double num1 = ((1.0 - yy) - zz); + double num2 = (xy - wz); + double num3 = (xz + wy); + double num4 = (xy + wz); + double num5 = ((1.0 - xx) - zz); + double num6 = (yz - wx); + double num7 = (xz - wy); + double num8 = (yz + wx); + double num9 = ((1.0 - xx) - yy); + + for (int i = 0; i < source.Length; ++i) + { + destination[i] = new Double3( + ((source[i].X * num1) + (source[i].Y * num2)) + (source[i].Z * num3), + ((source[i].X * num4) + (source[i].Y * num5)) + (source[i].Z * num6), + ((source[i].X * num7) + (source[i].Y * num8)) + (source[i].Z * num9)); + } + } + + /// + /// Transforms a 3D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Double3 vector, ref Matrix transform, out Double4 result) + { + result = new Double4( + (vector.X * transform.M11) + (vector.Y * transform.M21) + (vector.Z * transform.M31) + transform.M41, + (vector.X * transform.M12) + (vector.Y * transform.M22) + (vector.Z * transform.M32) + transform.M42, + (vector.X * transform.M13) + (vector.Y * transform.M23) + (vector.Z * transform.M33) + transform.M43, + (vector.X * transform.M14) + (vector.Y * transform.M24) + (vector.Z * transform.M34) + transform.M44); + } + + /// + /// Transforms a 3D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Double3 vector, ref Matrix transform, out Double3 result) + { + result = new Double3( + (vector.X * transform.M11) + (vector.Y * transform.M21) + (vector.Z * transform.M31) + transform.M41, + (vector.X * transform.M12) + (vector.Y * transform.M22) + (vector.Z * transform.M32) + transform.M42, + (vector.X * transform.M13) + (vector.Y * transform.M23) + (vector.Z * transform.M33) + transform.M43); + } + + /// + /// Transforms a 3D vector by the given . + /// + /// The source vector. + /// The transformation . + /// The transformed . + public static Double4 Transform(Double3 vector, Matrix transform) + { + Double4 result; + Transform(ref vector, ref transform, out result); + return result; + } + + /// + /// Transforms an array of 3D vectors by the given . + /// + /// The array of vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Double3[] source, ref Matrix transform, Double4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Transform(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// When the method completes, contains the transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(ref Double3 coordinate, ref Matrix transform, out Double3 result) + { + var invW = 1f / ((coordinate.X * transform.M14) + (coordinate.Y * transform.M24) + (coordinate.Z * transform.M34) + transform.M44); + result = new Double3( + ((coordinate.X * transform.M11) + (coordinate.Y * transform.M21) + (coordinate.Z * transform.M31) + transform.M41) * invW, + ((coordinate.X * transform.M12) + (coordinate.Y * transform.M22) + (coordinate.Z * transform.M32) + transform.M42) * invW, + ((coordinate.X * transform.M13) + (coordinate.Y * transform.M23) + (coordinate.Z * transform.M33) + transform.M43) * invW); + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// The transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static Double3 TransformCoordinate(Double3 coordinate, Matrix transform) + { + Double3 result; + TransformCoordinate(ref coordinate, ref transform, out result); + return result; + } + + /// + /// Performs a coordinate transformation on an array of vectors using the given . + /// + /// The array of coordinate vectors to trasnform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(Double3[] source, ref Matrix transform, Double3[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformCoordinate(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// When the method completes, contains the transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(ref Double3 normal, ref Matrix transform, out Double3 result) + { + result = new Double3( + (normal.X * transform.M11) + (normal.Y * transform.M21) + (normal.Z * transform.M31), + (normal.X * transform.M12) + (normal.Y * transform.M22) + (normal.Z * transform.M32), + (normal.X * transform.M13) + (normal.Y * transform.M23) + (normal.Z * transform.M33)); + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// The transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static Double3 TransformNormal(Double3 normal, Matrix transform) + { + Double3 result; + TransformNormal(ref normal, ref transform, out result); + return result; + } + + /// + /// Performs a normal transformation on an array of vectors using the given . + /// + /// The array of normal vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(Double3[] source, ref Matrix transform, Double3[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformNormal(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Calculate the yaw/pitch/roll rotation equivalent to the provided quaterion. + /// + /// The input rotation as quaternion + /// The equivation yaw/pitch/roll rotation + public static Double3 RotationYawPitchRoll(Quaternion quaternion) + { + Vector3 yawPitchRoll; + Quaternion.RotationYawPitchRoll(ref quaternion, out yawPitchRoll.X, out yawPitchRoll.Y, out yawPitchRoll.Z); + return yawPitchRoll; + } + + /// + /// Calculate the yaw/pitch/roll rotation equivalent to the provided quaterion. + /// + /// The input rotation as quaternion + /// The equivation yaw/pitch/roll rotation + public static void RotationYawPitchRoll(ref Quaternion quaternion, out Double3 yawPitchRoll) + { + Vector3 yawPitchRollV; + Quaternion.RotationYawPitchRoll(ref quaternion, out yawPitchRollV.X, out yawPitchRollV.Y, out yawPitchRollV.Z); + yawPitchRoll = yawPitchRollV; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator +(Double3 left, Double3 right) + { + return new Double3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator +(Double3 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator -(Double3 left, Double3 right) + { + return new Double3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator -(Double3 value) + { + return new Double3(-value.X, -value.Y, -value.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator *(double scale, Double3 value) + { + return new Double3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator *(Double3 value, double scale) + { + return new Double3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to multiply. + /// The second vector to multiply. + /// The multiplication of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator *(Double3 left, Double3 right) + { + return new Double3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Adds a vector with the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The vector offset. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator +(Double3 value, double scale) + { + return new Double3(value.X + scale, value.Y + scale, value.Z + scale); + } + + /// + /// Substracts a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The vector offset. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator -(Double3 value, double scale) + { + return new Double3(value.X - scale, value.Y - scale, value.Z - scale); + } + + /// + /// Divides a numerator by a vector. + /// + /// The numerator. + /// The value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator /(double numerator, Double3 value) + { + return new Double3(numerator / value.X, numerator / value.Y, numerator / value.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator /(Double3 value, double scale) + { + return new Double3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Divides a vector by the given vector, component-wise. + /// + /// The vector to scale. + /// The by. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double3 operator /(Double3 value, Double3 by) + { + return new Double3(value.X / by.X, value.Y / by.Y, value.Z / by.Z); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Double3 left, Double3 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Double3 left, Double3 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Double3 value) + { + return new Vector3((float)value.X, (float)value.Y, (float)value.Z); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Double3(Vector3 value) + { + return new Double3(value); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Half3(Double3 value) + { + return new Half3((Half)value.X, (Half)value.Y, (Half)value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double3(Half3 value) + { + return new Double3(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double2(Double3 value) + { + return new Double2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double4(Double3 value) + { + return new Double4(value, 0.0); + } + + /// + /// Tests whether one 3D vector is near another 3D vector. + /// + /// The left vector. + /// The right vector. + /// The epsilon. + /// true if left and right are near another 3D, false otherwise + public static bool NearEqual(Double3 left, Double3 right, Double3 epsilon) + { + return NearEqual(ref left, ref right, ref epsilon); + } + + /// + /// Tests whether one 3D vector is near another 3D vector. + /// + /// The left vector. + /// The right vector. + /// The epsilon. + /// true if left and right are near another 3D, false otherwise + public static bool NearEqual(ref Double3 left, ref Double3 right, ref Double3 epsilon) + { + return MathUtil.WithinEpsilon((float)left.X, (float)right.X, (float)epsilon.X) && + MathUtil.WithinEpsilon((float)left.Y, (float)right.Y, (float)epsilon.Y) && + MathUtil.WithinEpsilon((float)left.Z, (float)right.Z, (float)epsilon.Z); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}", X, Y, Z); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}", X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2}", X, Y, Z); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Double3 other) + { + return ((double)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (double)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance && + (double)Math.Abs(other.Z - Z) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Double3)value); + } + + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Double3D(Double3 value) + { + return new System.Windows.Media.Media3D.Double3D(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double3(System.Windows.Media.Media3D.Double3D value) + { + return new Double3((double)value.X, (double)value.Y, (double)value.Z); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Vector3(Double3 value) + { + return new Microsoft.Xna.Framework.Vector3(value.X, value.Y, value.Z); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Double3(Microsoft.Xna.Framework.Vector3 value) + { + return new Double3(value.X, value.Y, value.Z); + } +#endif + } +} diff --git a/math/Double4.cs b/math/Double4.cs new file mode 100644 index 0000000..f49f96f --- /dev/null +++ b/math/Double4.cs @@ -0,0 +1,1436 @@ +// Copyright (c) Xenko contributors. (https://xenko.com) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a four dimensional mathematical vector with double-precision floats. + /// + [DataContract( Name = "double4")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Double4 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Double4 Zero = new Double4(); + + /// + /// The X unit (1, 0, 0, 0). + /// + public static readonly Double4 UnitX = new Double4(1.0, 0.0, 0.0, 0.0); + + /// + /// The Y unit (0, 1, 0, 0). + /// + public static readonly Double4 UnitY = new Double4(0.0, 1.0, 0.0, 0.0); + + /// + /// The Z unit (0, 0, 1, 0). + /// + public static readonly Double4 UnitZ = new Double4(0.0, 0.0, 1.0, 0.0); + + /// + /// The W unit (0, 0, 0, 1). + /// + public static readonly Double4 UnitW = new Double4(0.0, 0.0, 0.0, 1.0); + + /// + /// A with all of its components set to one. + /// + public static readonly Double4 One = new Double4(1.0, 1.0, 1.0, 1.0); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public double X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public double Y; + + /// + /// The Z component of the vector. + /// + [DataMember( Order = 2 )] + public double Z; + + /// + /// The W component of the vector. + /// + [DataMember( Order = 3 )] + public double W; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Double4(double value) + { + X = value; + Y = value; + Z = value; + W = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + /// Initial value for the Z component of the vector. + /// Initial value for the W component of the vector. + public Double4(double x, double y, double z, double w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X, Y, and Z components. + /// Initial value for the W component of the vector. + public Double4(Double3 value, double w) + { + X = value.X; + Y = value.Y; + Z = value.Z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + /// Initial value for the Z component of the vector. + /// Initial value for the W component of the vector. + public Double4(Double2 value, double z, double w) + { + X = value.X; + Y = value.Y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, Z, and W components of the vector. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Double4(double[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 4) + throw new ArgumentOutOfRangeException("values", "There must be four and only four input values for Double4."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + W = values[3]; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The Vector4 to construct the Double4 from. + public Double4(Vector4 v) + { + X = v.X; + Y = v.Y; + Z = v.Z; + W = v.W; + } + + /// + /// Gets a value indicting whether this instance is normalized. + /// + public bool IsNormalized + { + get { return Math.Abs((X * X) + (Y * Y) + (Z * Z) + (W * W) - 1f) < MathUtil.ZeroTolerance; } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, Z, or W component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public double this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + case 3: return W; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Double4 run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + case 3: W = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Double4 run from 0 to 3, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double Length() + { + return (double)Math.Sqrt((X * X) + (Y * Y) + (Z * Z) + (W * W)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double LengthSquared() + { + return (X * X) + (Y * Y) + (Z * Z) + (W * W); + } + + /// + /// Converts the vector into a unit vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Normalize() + { + double length = Length(); + if (length > MathUtil.ZeroTolerance) + { + double inverse = 1.0 / length; + X *= inverse; + Y *= inverse; + Z *= inverse; + W *= inverse; + } + } + + /// + /// Raises the exponent for each components. + /// + /// The exponent. + public void Pow(double exponent) + { + X = (double)Math.Pow(X, exponent); + Y = (double)Math.Pow(Y, exponent); + Z = (double)Math.Pow(Z, exponent); + W = (double)Math.Pow(W, exponent); + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A four-element array containing the components of the vector. + public double[] ToArray() + { + return new double[] { X, Y, Z, W }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Double4 left, ref Double4 right, out Double4 result) + { + result = new Double4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Add(Double4 left, Double4 right) + { + return new Double4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Subtract(ref Double4 left, ref Double4 right, out Double4 result) + { + result = new Double4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Subtract(Double4 left, Double4 right) + { + return new Double4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(ref Double4 value, double scale, out Double4 result) + { + result = new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Multiply(Double4 value, double scale) + { + return new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Modulate(ref Double4 left, ref Double4 right, out Double4 result) + { + result = new Double4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Modulate(Double4 left, Double4 right) + { + return new Double4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(ref Double4 value, double scale, out Double4 result) + { + result = new Double4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Divide(Double4 value, double scale) + { + return new Double4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// When the method completes, contains the demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Demodulate(ref Double4 left, ref Double4 right, out Double4 result) + { + result = new Double4(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// The demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Demodulate(Double4 left, Double4 right) + { + return new Double4(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Negate(ref Double4 value, out Double4 result) + { + result = new Double4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Negate(Double4 value) + { + return new Double4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 4D triangle. + /// + /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// When the method completes, contains the 4D Cartesian coordinates of the specified point. + public static void Barycentric(ref Double4 value1, ref Double4 value2, ref Double4 value3, double amount1, double amount2, out Double4 result) + { + result = new Double4((value1.X + (amount1 * (value2.X - value1.X))) + (amount2 * (value3.X - value1.X)), + (value1.Y + (amount1 * (value2.Y - value1.Y))) + (amount2 * (value3.Y - value1.Y)), + (value1.Z + (amount1 * (value2.Z - value1.Z))) + (amount2 * (value3.Z - value1.Z)), + (value1.W + (amount1 * (value2.W - value1.W))) + (amount2 * (value3.W - value1.W))); + } + + /// + /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 4D triangle. + /// + /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// A new containing the 4D Cartesian coordinates of the specified point. + public static Double4 Barycentric(Double4 value1, Double4 value2, Double4 value3, double amount1, double amount2) + { + Double4 result; + Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); + return result; + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Double4 value, ref Double4 min, ref Double4 max, out Double4 result) + { + double x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + double y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + double z = value.Z; + z = (z > max.Z) ? max.Z : z; + z = (z < min.Z) ? min.Z : z; + + double w = value.W; + w = (w > max.W) ? max.W : w; + w = (w < min.W) ? min.W : w; + + result = new Double4(x, y, z, w); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Double4 Clamp(Double4 value, Double4 min, Double4 max) + { + Double4 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static void Distance(ref Double4 value1, ref Double4 value2, out double result) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + double w = value1.W - value2.W; + + result = (double)Math.Sqrt((x * x) + (y * y) + (z * z) + (w * w)); + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static double Distance(Double4 value1, Double4 value2) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + double w = value1.W - value2.W; + + return (double)Math.Sqrt((x * x) + (y * y) + (z * z) + (w * w)); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static void DistanceSquared(ref Double4 value1, ref Double4 value2, out double result) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + double w = value1.W - value2.W; + + result = (x * x) + (y * y) + (z * z) + (w * w); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static double DistanceSquared(Double4 value1, Double4 value2) + { + double x = value1.X - value2.X; + double y = value1.Y - value2.Y; + double z = value1.Z - value2.Z; + double w = value1.W - value2.W; + + return (x * x) + (y * y) + (z * z) + (w * w); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dot(ref Double4 left, ref Double4 right, out double result) + { + result = (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z) + (left.W * right.W); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Dot(Double4 left, Double4 right) + { + return (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z) + (left.W * right.W); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// When the method completes, contains the normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ref Double4 value, out Double4 result) + { + Double4 temp = value; + result = temp; + result.Normalize(); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// The normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Normalize(Double4 value) + { + value.Normalize(); + return value; + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Double4 start, ref Double4 end, double amount, out Double4 result) + { + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + result.W = start.W + ((end.W - start.W) * amount); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Double4 Lerp(Double4 start, Double4 end, double amount) + { + Double4 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Double4 start, ref Double4 end, double amount, out Double4 result) + { + amount = (amount > 1.0) ? 1.0 : ((amount < 0.0) ? 0.0 : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + result.W = start.W + ((end.W - start.W) * amount); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Double4 SmoothStep(Double4 start, Double4 end, double amount) + { + Double4 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// When the method completes, contains the result of the Hermite spline interpolation. + public static void Hermite(ref Double4 value1, ref Double4 tangent1, ref Double4 value2, ref Double4 tangent2, double amount, out Double4 result) + { + double squared = amount * amount; + double cubed = amount * squared; + double part1 = ((2.0f * cubed) - (3.0f * squared)) + 1.0; + double part2 = (-2.0f * cubed) + (3.0f * squared); + double part3 = (cubed - (2.0f * squared)) + amount; + double part4 = cubed - squared; + + result = new Double4((((value1.X * part1) + (value2.X * part2)) + (tangent1.X * part3)) + (tangent2.X * part4), + (((value1.Y * part1) + (value2.Y * part2)) + (tangent1.Y * part3)) + (tangent2.Y * part4), + (((value1.Z * part1) + (value2.Z * part2)) + (tangent1.Z * part3)) + (tangent2.Z * part4), + (((value1.W * part1) + (value2.W * part2)) + (tangent1.W * part3)) + (tangent2.W * part4)); + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// The result of the Hermite spline interpolation. + public static Double4 Hermite(Double4 value1, Double4 tangent1, Double4 value2, Double4 tangent2, double amount) + { + Double4 result; + Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result); + return result; + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// When the method completes, contains the result of the Catmull-Rom interpolation. + public static void CatmullRom(ref Double4 value1, ref Double4 value2, ref Double4 value3, ref Double4 value4, double amount, out Double4 result) + { + double squared = amount * amount; + double cubed = amount * squared; + + result.X = 0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * squared)) + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * cubed)); + result.Y = 0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * squared)) + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * cubed)); + result.Z = 0.5f * ((((2.0f * value2.Z) + ((-value1.Z + value3.Z) * amount)) + (((((2.0f * value1.Z) - (5.0f * value2.Z)) + (4.0f * value3.Z)) - value4.Z) * squared)) + ((((-value1.Z + (3.0f * value2.Z)) - (3.0f * value3.Z)) + value4.Z) * cubed)); + result.W = 0.5f * ((((2.0f * value2.W) + ((-value1.W + value3.W) * amount)) + (((((2.0f * value1.W) - (5.0f * value2.W)) + (4.0f * value3.W)) - value4.W) * squared)) + ((((-value1.W + (3.0f * value2.W)) - (3.0f * value3.W)) + value4.W) * cubed)); + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// A vector that is the result of the Catmull-Rom interpolation. + public static Double4 CatmullRom(Double4 value1, Double4 value2, Double4 value3, Double4 value4, double amount) + { + Double4 result; + CatmullRom(ref value1, ref value2, ref value3, ref value4, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Max(ref Double4 left, ref Double4 right, out Double4 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + result.Z = (left.Z > right.Z) ? left.Z : right.Z; + result.W = (left.W > right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Max(Double4 left, Double4 right) + { + Double4 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Min(ref Double4 left, ref Double4 right, out Double4 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + result.Z = (left.Z < right.Z) ? left.Z : right.Z; + result.W = (left.W < right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 Min(Double4 left, Double4 right) + { + Double4 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Orthogonalizes a list of vectors. + /// + /// The list of orthogonalized vectors. + /// The list of vectors to orthogonalize. + /// + /// Orthogonalization is the process of making all vectors orthogonal to each other. This + /// means that any given vector in the list will be orthogonal to any other given vector in the + /// list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthogonalize(Double4[] destination, params Double4[] source) + { + //Uses the modified Gram-Schmidt process. + //q1 = m1 + //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 + //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 + //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Double4 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= (Double4.Dot(destination[r], newvector) / Double4.Dot(destination[r], destination[r])) * destination[r]; + } + + destination[i] = newvector; + } + } + + /// + /// Orthonormalizes a list of vectors. + /// + /// The list of orthonormalized vectors. + /// The list of vectors to orthonormalize. + /// + /// Orthonormalization is the process of making all vectors orthogonal to each + /// other and making all vectors of unit length. This means that any given vector will + /// be orthogonal to any other given vector in the list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthonormalize(Double4[] destination, params Double4[] source) + { + //Uses the modified Gram-Schmidt process. + //Because we are making unit vectors, we can optimize the math for orthogonalization + //and simplify the projection operation to remove the division. + //q1 = m1 / |m1| + //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| + //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| + //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Double4 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= Double4.Dot(destination[r], newvector) * destination[r]; + } + + newvector.Normalize(); + destination[i] = newvector; + } + } + + /// + /// Transforms a 4D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// When the method completes, contains the transformed . + public static void Transform(ref Double4 vector, ref Quaternion rotation, out Double4 result) + { + double x = rotation.X + rotation.X; + double y = rotation.Y + rotation.Y; + double z = rotation.Z + rotation.Z; + double wx = rotation.W * x; + double wy = rotation.W * y; + double wz = rotation.W * z; + double xx = rotation.X * x; + double xy = rotation.X * y; + double xz = rotation.X * z; + double yy = rotation.Y * y; + double yz = rotation.Y * z; + double zz = rotation.Z * z; + + result = new Double4( + ((vector.X * ((1.0 - yy) - zz)) + (vector.Y * (xy - wz))) + (vector.Z * (xz + wy)), + ((vector.X * (xy + wz)) + (vector.Y * ((1.0 - xx) - zz))) + (vector.Z * (yz - wx)), + ((vector.X * (xz - wy)) + (vector.Y * (yz + wx))) + (vector.Z * ((1.0 - xx) - yy)), + vector.W); + } + + /// + /// Transforms a 4D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// The transformed . + public static Double4 Transform(Double4 vector, Quaternion rotation) + { + Double4 result; + Transform(ref vector, ref rotation, out result); + return result; + } + + /// + /// Transforms an array of vectors by the given rotation. + /// + /// The array of vectors to transform. + /// The rotation to apply. + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Double4[] source, ref Quaternion rotation, Double4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + double x = rotation.X + rotation.X; + double y = rotation.Y + rotation.Y; + double z = rotation.Z + rotation.Z; + double wx = rotation.W * x; + double wy = rotation.W * y; + double wz = rotation.W * z; + double xx = rotation.X * x; + double xy = rotation.X * y; + double xz = rotation.X * z; + double yy = rotation.Y * y; + double yz = rotation.Y * z; + double zz = rotation.Z * z; + + double num1 = ((1.0 - yy) - zz); + double num2 = (xy - wz); + double num3 = (xz + wy); + double num4 = (xy + wz); + double num5 = ((1.0 - xx) - zz); + double num6 = (yz - wx); + double num7 = (xz - wy); + double num8 = (yz + wx); + double num9 = ((1.0 - xx) - yy); + + for (int i = 0; i < source.Length; ++i) + { + destination[i] = new Double4( + ((source[i].X * num1) + (source[i].Y * num2)) + (source[i].Z * num3), + ((source[i].X * num4) + (source[i].Y * num5)) + (source[i].Z * num6), + ((source[i].X * num7) + (source[i].Y * num8)) + (source[i].Z * num9), + source[i].W); + } + } + + /// + /// Transforms a 4D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Double4 vector, ref Matrix transform, out Double4 result) + { + result = new Double4( + (vector.X * transform.M11) + (vector.Y * transform.M21) + (vector.Z * transform.M31) + (vector.W * transform.M41), + (vector.X * transform.M12) + (vector.Y * transform.M22) + (vector.Z * transform.M32) + (vector.W * transform.M42), + (vector.X * transform.M13) + (vector.Y * transform.M23) + (vector.Z * transform.M33) + (vector.W * transform.M43), + (vector.X * transform.M14) + (vector.Y * transform.M24) + (vector.Z * transform.M34) + (vector.W * transform.M44)); + } + + /// + /// Transforms a 4D vector by the given . + /// + /// The source vector. + /// The transformation . + /// The transformed . + public static Double4 Transform(Double4 vector, Matrix transform) + { + Double4 result; + Transform(ref vector, ref transform, out result); + return result; + } + + /// + /// Transforms an array of 4D vectors by the given . + /// + /// The array of vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Double4[] source, ref Matrix transform, Double4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Transform(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator +(Double4 left, Double4 right) + { + return new Double4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator +(Double4 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator -(Double4 left, Double4 right) + { + return new Double4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator -(Double4 value) + { + return new Double4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator *(double scale, Double4 value) + { + return new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator *(Double4 value, double scale) + { + return new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to multiply. + /// The second vector to multiply. + /// The multiplication of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator *(Double4 left, Double4 right) + { + return new Double4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator /(Double4 value, double scale) + { + return new Double4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Divides a numerator by a vector. + /// + /// The numerator. + /// The value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator /(double numerator, Double4 value) + { + return new Double4(numerator / value.X, numerator / value.Y, numerator / value.Z, numerator / value.W); + } + + /// + /// Divides a vector by the given vector, component-wise. + /// + /// The vector to scale. + /// The by. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Double4 operator /(Double4 value, Double4 by) + { + return new Double4(value.X / by.X, value.Y / by.Y, value.Z / by.Z, value.W / by.W); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Double4 left, Double4 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Double4 left, Double4 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Double4 value) + { + return new Vector4((float)value.X, (float)value.Y, (float)value.Z, (float)value.W); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Double4(Vector4 value) + { + return new Double4(value); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Half4(Double4 value) + { + return new Half4((Half)value.X, (Half)value.Y, (Half)value.Z, (Half)value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double4(Half4 value) + { + return new Double4(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double2(Double4 value) + { + return new Double2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double3(Double4 value) + { + return new Double3(value.X, value.Y, value.Z); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture), W.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider), W.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Double4 other) + { + return ((double)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (double)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance && + (double)Math.Abs(other.Z - Z) < MathUtil.ZeroTolerance && + (double)Math.Abs(other.W - W) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Double4)value); + } + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Point4D(Double4 value) + { + return new System.Windows.Media.Media3D.Point4D(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Double4(System.Windows.Media.Media3D.Point4D value) + { + return new Double4((double)value.X, (double)value.Y, (double)value.Z, (double)value.W); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Vector4(Double4 value) + { + return new Microsoft.Xna.Framework.Vector4(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Double4(Microsoft.Xna.Framework.Vector4 value) + { + return new Double4(value.X, value.Y, value.Z, value.W); + } +#endif + } +} diff --git a/math/GuillotinePacker.cs b/math/GuillotinePacker.cs new file mode 100644 index 0000000..62359a6 --- /dev/null +++ b/math/GuillotinePacker.cs @@ -0,0 +1,183 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Implementation of a "Guillotine" packer. + /// More information at http://clb.demon.fi/files/RectangleBinPack.pdf. + /// + public class GuillotinePacker + { + private readonly List freeRectangles = new List(); + private readonly List tempFreeRectangles = new List(); + + /// + /// A delegate callback used by + /// + /// The index of the rectangle + /// The rectangle found + public delegate void InsertRectangleCallback(int cascadeIndex, ref Rectangle rectangle); + + /// + /// Current width used by the packer. + /// + public int Width { get; private set; } + + /// + /// Current height used by the packer. + /// + public int Height { get; private set; } + + /// + /// Clears the specified region. + /// + /// The width. + /// The height. + public void Clear(int width, int height) + { + freeRectangles.Clear(); + freeRectangles.Add(new Rectangle { X = 0, Y = 0, Width = width, Height = height }); + + Width = width; + Height = height; + } + + /// + /// Clears the whole region. + /// + public virtual void Clear() + { + Clear(Width, Height); + } + + /// + /// Frees the specified old rectangle. + /// + /// The old rectangle. + public void Free(ref Rectangle oldRectangle) + { + freeRectangles.Add(oldRectangle); + } + + /// + /// Tries to fit a single rectangle with the specified width and height. + /// + /// Width requested. + /// Height requested + /// Fill with the rectangle if it was successfully inserted. + /// true if it was successfully inserted. + public bool Insert(int width, int height, ref Rectangle bestRectangle) + { + return Insert(width, height, freeRectangles, ref bestRectangle); + } + + /// + /// Tries to fit multiple rectangle with (width, height). + /// + /// Width requested. + /// Height requested + /// The number of rectangle to fit. + /// A callback called for each rectangle successfully fitted. + /// true if all rectangles were successfully fitted. + public bool TryInsert(int width, int height, int count, InsertRectangleCallback inserted) + { + var bestRectangle = new Rectangle(); + tempFreeRectangles.Clear(); + foreach (var freeRectangle in freeRectangles) + { + tempFreeRectangles.Add(freeRectangle); + } + + for (var i = 0; i < count; ++i) + { + if (!Insert(width, height, tempFreeRectangles, ref bestRectangle)) + { + tempFreeRectangles.Clear(); + return false; + } + + inserted(i, ref bestRectangle); + } + + // if the insertion went well, use the new configuration + freeRectangles.Clear(); + foreach (var tempFreeRectangle in tempFreeRectangles) + { + freeRectangles.Add(tempFreeRectangle); + } + tempFreeRectangles.Clear(); + + return true; + } + + private static bool Insert(int width, int height, List freeRectanglesList, ref Rectangle bestRectangle) + { + // Info on algorithm: http://clb.demon.fi/files/RectangleBinPack.pdf + int bestScore = int.MaxValue; + int freeRectangleIndex = -1; + + // Find space for new rectangle + for (int i = 0; i < freeRectanglesList.Count; ++i) + { + var currentFreeRectangle = freeRectanglesList[i]; + if (width == currentFreeRectangle.Width && height == currentFreeRectangle.Height) + { + // Perfect fit + bestRectangle.X = currentFreeRectangle.X; + bestRectangle.Y = currentFreeRectangle.Y; + bestRectangle.Width = width; + bestRectangle.Height = height; + freeRectangleIndex = i; + break; + } + if (width <= currentFreeRectangle.Width && height <= currentFreeRectangle.Height) + { + // Can fit inside + // Use "BAF" heuristic (best area fit) + var score = currentFreeRectangle.Width * currentFreeRectangle.Height - width * height; + if (score < bestScore) + { + bestRectangle.X = currentFreeRectangle.X; + bestRectangle.Y = currentFreeRectangle.Y; + bestRectangle.Width = width; + bestRectangle.Height = height; + bestScore = score; + freeRectangleIndex = i; + } + } + } + + // No space could be found + if (freeRectangleIndex == -1) + return false; + + var freeRectangle = freeRectanglesList[freeRectangleIndex]; + + // Choose an axis to split (trying to minimize the smaller area "MINAS") + int w = freeRectangle.Width - bestRectangle.Width; + int h = freeRectangle.Height - bestRectangle.Height; + var splitHorizontal = (bestRectangle.Width * h > w * bestRectangle.Height); + + // Form the two new rectangles. + var bottom = new Rectangle { X = freeRectangle.X, Y = freeRectangle.Y + bestRectangle.Height, Width = splitHorizontal ? freeRectangle.Width : bestRectangle.Width, Height = h }; + var right = new Rectangle { X = freeRectangle.X + bestRectangle.Width, Y = freeRectangle.Y, Width = w, Height = splitHorizontal ? bestRectangle.Height : freeRectangle.Height }; + + if (bottom.Width > 0 && bottom.Height > 0) + freeRectanglesList.Add(bottom); + if (right.Width > 0 && right.Height > 0) + freeRectanglesList.Add(right); + + // Remove previously selected freeRectangle + if (freeRectangleIndex != freeRectanglesList.Count - 1) + freeRectanglesList[freeRectangleIndex] = freeRectanglesList[freeRectanglesList.Count - 1]; + freeRectanglesList.RemoveAt(freeRectanglesList.Count - 1); + + return true; + } + } +} diff --git a/math/Half.cs b/math/Half.cs new file mode 100644 index 0000000..aceca1b --- /dev/null +++ b/math/Half.cs @@ -0,0 +1,268 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2011 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.InteropServices; +using Xenko.Core.Serialization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// A half precision (16 bit) floating point value. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public struct Half + { + private ushort value; + + /// + /// Number of decimal digits of precision. + /// + public const int PrecisionDigits = 3; + + /// + /// Number of bits in the mantissa. + /// + public const int MantissaBits = 11; + + /// + /// Maximum decimal exponent. + /// + public const int MaximumDecimalExponent = 4; + + /// + /// Maximum binary exponent. + /// + public const int MaximumBinaryExponent = 15; + + /// + /// Minimum decimal exponent. + /// + public const int MinimumDecimalExponent = -4; + + /// + /// Minimum binary exponent. + /// + public const int MinimumBinaryExponent = -14; + + /// + /// Exponent radix. + /// + public const int ExponentRadix = 2; + + /// + /// Additional rounding. + /// + public const int AdditionRounding = 1; + + /// + /// Smallest such that 1.0 + epsilon != 1.0 + /// + public static readonly float Epsilon; + + /// + /// Maximum value of the number. + /// + public static readonly float MaxValue; + + /// + /// Minimum value of the number. + /// + public static readonly float MinValue; + + /// + /// A whose value is 0.0f. + /// + public static readonly Half Zero; + + /// + /// A whose value is 1.0f. + /// + public static readonly Half One; + + /// + /// Initializes a new instance of the structure. + /// + /// The floating point value that should be stored in 16 bit format. + public Half(float value) + { + this.value = HalfUtils.Pack(value); + } + + /// + /// Gets or sets the raw 16 bit value used to back this half-float. + /// + public ushort RawValue + { + get { return value; } + set { this.value = value; } + } + + /// + /// Converts an array of half precision values into full precision values. + /// + /// The values to be converted. + /// An array of converted values. + public static float[] ConvertToFloat(Half[] values) + { + float[] results = new float[values.Length]; + for (int i = 0; i < results.Length; i++) + results[i] = HalfUtils.Unpack(values[i].RawValue); + return results; + } + + /// + /// Converts an array of full precision values into half precision values. + /// + /// The values to be converted. + /// An array of converted values. + public static Half[] ConvertToHalf(float[] values) + { + Half[] results = new Half[values.Length]; + for (int i = 0; i < results.Length; i++) + results[i] = new Half(values[i]); + return results; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value to be converted. + /// The converted value. + public static explicit operator Half(float value) + { + return new Half(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to be converted. + /// The converted value. + public static implicit operator float(Half value) + { + return HalfUtils.Unpack(value.value); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Half left, Half right) + { + return left.value == right.value; + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Half left, Half right) + { + return left.value != right.value; + } + + /// + /// Converts the value of the object to its equivalent string representation. + /// + /// The string representation of the value of this instance. + public override string ToString() + { + float num = this; + return num.ToString(CultureInfo.CurrentCulture); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + ushort num = value; + return ((num * 3) / 2) ^ num; + } + + /// + /// Determines whether the specified object instances are considered equal. + /// + /// The first value. + /// The second value. + /// + /// true if is the same instance as or + /// if both are null references or if value1.Equals(value2) returns true; otherwise, false. + public static bool Equals(ref Half value1, ref Half value2) + { + return value1.value == value2.value; + } + + /// + /// Returns a value that indicates whether the current instance is equal to the specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public bool Equals(Half other) + { + return other.value == value; + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (obj.GetType() != GetType()) + { + return false; + } + Half half = (Half)obj; + return half.value == value; + } + + static Half() + { + Epsilon = 0.0004887581f; + MaxValue = 65504f; + MinValue = 6.103516E-05f; + Zero = (Half)0.0f; + One = (Half)1.0f; + } + } +} diff --git a/math/Half2.cs b/math/Half2.cs new file mode 100644 index 0000000..b3ab4aa --- /dev/null +++ b/math/Half2.cs @@ -0,0 +1,190 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2011 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using Xenko.Core.Serialization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a two dimensional mathematical vector with half-precision floats. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public struct Half2 : IEquatable + { + /// + /// Gets or sets the X component of the vector. + /// + /// The X component of the vector. + public Half X; + + /// + /// Gets or sets the Y component of the vector. + /// + /// The Y component of the vector. + public Half Y; + + /// + /// Initializes a new instance of the structure. + /// + /// The X component. + /// The Y component. + public Half2(Half x, Half y) + { + this.X = x; + this.Y = y; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The value to set for both the X and Y components. + public Half2(Half value) + { + this.X = value; + this.Y = value; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The X component. + /// The Y component. + public Half2(float x, float y) + { + this.X = (Half)x; + this.Y = (Half)y; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The value to set for both the X and Y components. + public Half2(float value) + { + this.X = (Half)value; + this.Y = (Half)value; + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Half2 left, Half2 right) + { + return Equals(ref left, ref right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has a different value than ; otherwise, false. + [return: MarshalAs(UnmanagedType.U1)] + public static bool operator !=(Half2 left, Half2 right) + { + return !Equals(ref left, ref right); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return (this.Y.GetHashCode() + this.X.GetHashCode()); + } + + /// + /// Determines whether the specified object instances are considered equal. + /// + /// The first value. + /// The second value. + /// + /// true if is the same instance as or + /// if both are null references or if value1.Equals(value2) returns true; otherwise, false. + public static bool Equals(ref Half2 value1, ref Half2 value2) + { + return ((value1.X == value2.X) && (value1.Y == value2.Y)); + } + + /// + /// Returns a value that indicates whether the current instance is equal to the specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public bool Equals(Half2 other) + { + return ((this.X == other.X) && (this.Y == other.Y)); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (obj.GetType() != GetType()) + { + return false; + } + return this.Equals((Half2)obj); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Half2(Vec2 value) + { + return new Half2((Half)value.X, (Half)value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(Half2 value) + { + return new Vec2(value.X, value.Y); + } + } +} diff --git a/math/Half3.cs b/math/Half3.cs new file mode 100644 index 0000000..4e48cd2 --- /dev/null +++ b/math/Half3.cs @@ -0,0 +1,203 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2011 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using Xenko.Core.Serialization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a three dimensional mathematical vector with half-precision floats. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public struct Half3 : IEquatable + { + /// + /// Gets or sets the X component of the vector. + /// + /// The X component of the vector. + public Half X; + + /// + /// Gets or sets the Y component of the vector. + /// + /// The Y component of the vector. + public Half Y; + + /// + /// Gets or sets the Z component of the vector. + /// + /// The Z component of the vector. + public Half Z; + + /// + /// Initializes a new instance of the structure. + /// + /// The X component. + /// The Y component. + /// The Z component. + public Half3(Half x, Half y, Half z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The value to set for the X, Y, and Z components. + public Half3(Half value) + { + this.X = value; + this.Y = value; + this.Z = value; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The X component. + /// The Y component. + /// The Z component. + public Half3(float x, float y, float z) + { + this.X = (Half)x; + this.Y = (Half)y; + this.Z = (Half)z; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The value to set for the X, Y, and Z components. + public Half3(float value) + { + this.X = (Half)value; + this.Y = (Half)value; + this.Z = (Half)value; + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Half3 left, Half3 right) + { + return Equals(ref left, ref right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has a different value than ; otherwise, false. + [return: MarshalAs(UnmanagedType.U1)] + public static bool operator !=(Half3 left, Half3 right) + { + return !Equals(ref left, ref right); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + int num = this.Z.GetHashCode() + this.Y.GetHashCode(); + return (this.X.GetHashCode() + num); + } + + /// + /// Determines whether the specified object instances are considered equal. + /// + /// The first value. + /// The second value. + /// + /// true if is the same instance as or + /// if both are null references or if value1.Equals(value2) returns true; otherwise, false. + public static bool Equals(ref Half3 value1, ref Half3 value2) + { + return (((value1.X == value2.X) && (value1.Y == value2.Y)) && (value1.Z == value2.Z)); + } + + /// + /// Returns a value that indicates whether the current instance is equal to the specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public bool Equals(Half3 other) + { + return (((this.X == other.X) && (this.Y == other.Y)) && (this.Z == other.Z)); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Half3(Vector3 value) + { + return new Half3((Half)value.X, (Half)value.Y, (Half)value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Half3 value) + { + return new Vector3(value.X, value.Y, value.Z); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (obj.GetType() != GetType()) + { + return false; + } + return this.Equals((Half3)obj); + } + } +} diff --git a/math/Half4.cs b/math/Half4.cs new file mode 100644 index 0000000..cdcc251 --- /dev/null +++ b/math/Half4.cs @@ -0,0 +1,188 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2011 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using Xenko.Core.Serialization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a four dimensional mathematical vector with half-precision floats. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public struct Half4 : IEquatable + { + /// + /// Gets or sets the X component of the vector. + /// + /// The X component of the vector. + public Half X; + + /// + /// Gets or sets the Y component of the vector. + /// + /// The Y component of the vector. + public Half Y; + + /// + /// Gets or sets the Z component of the vector. + /// + /// The Z component of the vector. + public Half Z; + + /// + /// Gets or sets the W component of the vector. + /// + /// The W component of the vector. + public Half W; + + /// + /// Initializes a new instance of the structure. + /// + /// The X component. + /// The Y component. + /// The Z component. + /// The W component. + public Half4(Half x, Half y, Half z, Half w) + { + this.X = x; + this.Y = y; + this.Z = z; + this.W = w; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The value to set for the X, Y, Z, and W components. + public Half4(Half value) + { + this.X = value; + this.Y = value; + this.Z = value; + this.W = value; + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Half4 left, Half4 right) + { + return Equals(ref left, ref right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Half4 left, Half4 right) + { + return !Equals(ref left, ref right); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + int num2 = this.W.GetHashCode() + this.Z.GetHashCode(); + int num = this.Y.GetHashCode() + num2; + return (this.X.GetHashCode() + num); + } + + /// + /// Determines whether the specified object instances are considered equal. + /// + /// The first value. + /// The second value. + /// + /// true if is the same instance as or + /// if both are null references or if value1.Equals(value2) returns true; otherwise, false. + public static bool Equals(ref Half4 value1, ref Half4 value2) + { + return (((value1.X == value2.X) && (value1.Y == value2.Y)) && ((value1.Z == value2.Z) && (value1.W == value2.W))); + } + + /// + /// Returns a value that indicates whether the current instance is equal to the specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public bool Equals(Half4 other) + { + return (((this.X == other.X) && (this.Y == other.Y)) && ((this.Z == other.Z) && (this.W == other.W))); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Half4(Vector4 value) + { + return new Half4((Half)value.X, (Half)value.Y, (Half)value.Z, (Half)value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Half4 value) + { + return new Vector4(value.X, value.Y, value.Z, value.W); + } + + /// + /// Returns a value that indicates whether the current instance is equal to a specified object. + /// + /// Object to make the comparison with. + /// + /// true if the current instance is equal to the specified object; false otherwise. + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + if (obj.GetType() != GetType()) + { + return false; + } + return this.Equals((Half4)obj); + } + } +} diff --git a/math/HalfUtils.cs b/math/HalfUtils.cs new file mode 100644 index 0000000..75496f3 --- /dev/null +++ b/math/HalfUtils.cs @@ -0,0 +1,173 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2011 SharpDX - Alexandre Mutel +// +// 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. +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Helper class to perform Half/Float conversion. + /// Code extract from paper : www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf by Jeroen van der Zijp + /// + internal class HalfUtils + { + [StructLayout(LayoutKind.Explicit, Pack = 4)] + private struct FloatToUint + { + [FieldOffset(0)] + public uint UIntValue; + [FieldOffset(0)] + public float FloatValue; + } + + /// + /// Unpacks the specified h. + /// + /// The packed value. + /// The float representation of the packed value. + public static float Unpack(ushort h) + { + var conv = new FloatToUint(); + conv.UIntValue = HalfToFloatMantissaTable[HalfToFloatOffsetTable[h >> 10] + (((uint)h) & 0x3ff)] + HalfToFloatExponentTable[h >> 10]; + return conv.FloatValue; + } + + /// + /// Packs the specified f. + /// + /// The float value. + /// The packed representation of the float value. + public static ushort Pack(float f) + { + FloatToUint conv = new FloatToUint(); + conv.FloatValue = f; + return (ushort)(FloatToHalfBaseTable[(conv.UIntValue >> 23) & 0x1ff] + ((conv.UIntValue & 0x007fffff) >> FloatToHalfShiftTable[(conv.UIntValue >> 23) & 0x1ff])); + } + + private static readonly uint[] HalfToFloatMantissaTable = new uint[2048]; + private static readonly uint[] HalfToFloatExponentTable = new uint[64]; + private static readonly uint[] HalfToFloatOffsetTable = new uint[64]; + private static readonly ushort[] FloatToHalfBaseTable = new ushort[512]; + private static readonly byte[] FloatToHalfShiftTable = new byte[512]; + + static HalfUtils() + { + int i; + + // ------------------------------------------------------------------- + // Half to Float tables + // ------------------------------------------------------------------- + + // Mantissa table + + // 0 => 0 + HalfToFloatMantissaTable[0] = 0; + + // Transform subnormal to normalized + for (i = 1; i < 1024; i++) + { + uint m = ((uint)i) << 13; + uint e = 0; + + while ((m & 0x00800000) == 0) + { + e -= 0x00800000; + m <<= 1; + } + m &= ~0x00800000U; + e += 0x38800000; + HalfToFloatMantissaTable[i] = m | e; + } + + // Normal case + for (i = 1024; i < 2048; i++) + HalfToFloatMantissaTable[i] = 0x38000000 + (((uint)(i - 1024)) << 13); + + // Exponent table + + // 0 => 0 + HalfToFloatExponentTable[0] = 0; + + for (i = 1; i < 63; i++) + { + if (i < 31) // Positive Numbers + HalfToFloatExponentTable[i] = ((uint)i) << 23; + else // Negative Numbers + HalfToFloatExponentTable[i] = 0x80000000 + (((uint)(i - 32)) << 23); + } + HalfToFloatExponentTable[31] = 0x47800000; + HalfToFloatExponentTable[32] = 0x80000000; + HalfToFloatExponentTable[63] = 0xC7800000; + + // Offset table + HalfToFloatOffsetTable[0] = 0; + for (i = 1; i < 64; i++) + HalfToFloatOffsetTable[i] = 1024; + HalfToFloatOffsetTable[32] = 0; + + // ------------------------------------------------------------------- + // Float to Half tables + // ------------------------------------------------------------------- + + for (i = 0; i < 256; i++) + { + int e = i - 127; + if (e < -24) + { // Very small numbers map to zero + FloatToHalfBaseTable[i | 0x000] = 0x0000; + FloatToHalfBaseTable[i | 0x100] = 0x8000; + FloatToHalfShiftTable[i | 0x000] = 24; + FloatToHalfShiftTable[i | 0x100] = 24; + } + else if (e < -14) + { // Small numbers map to denorms + FloatToHalfBaseTable[i | 0x000] = (ushort)((0x0400 >> (-e - 14))); + FloatToHalfBaseTable[i | 0x100] = (ushort)((0x0400 >> (-e - 14)) | 0x8000); + FloatToHalfShiftTable[i | 0x000] = (byte)(-e - 1); + FloatToHalfShiftTable[i | 0x100] = (byte)(-e - 1); + } + else if (e <= 15) + { // Normal numbers just lose precision + FloatToHalfBaseTable[i | 0x000] = (ushort)(((e + 15) << 10)); + FloatToHalfBaseTable[i | 0x100] = (ushort)(((e + 15) << 10) | 0x8000); + FloatToHalfShiftTable[i | 0x000] = 13; + FloatToHalfShiftTable[i | 0x100] = 13; + } + else if (e < 128) + { // Large numbers map to Infinity + FloatToHalfBaseTable[i | 0x000] = 0x7C00; + FloatToHalfBaseTable[i | 0x100] = 0xFC00; + FloatToHalfShiftTable[i | 0x000] = 24; + FloatToHalfShiftTable[i | 0x100] = 24; + } + else + { // Infinity and NaN's stay Infinity and NaN's + FloatToHalfBaseTable[i | 0x000] = 0x7C00; + FloatToHalfBaseTable[i | 0x100] = 0xFC00; + FloatToHalfShiftTable[i | 0x000] = 13; + FloatToHalfShiftTable[i | 0x100] = 13; + } + } + } + } +} diff --git a/math/Int2.cs b/math/Int2.cs new file mode 100644 index 0000000..be0cae0 --- /dev/null +++ b/math/Int2.cs @@ -0,0 +1,764 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a three dimensional mathematical vector. + /// + [DataContract( Name = "Int2")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Int2 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Int2 Zero = new Int2(); + + /// + /// The X unit (1, 0, 0). + /// + public static readonly Int2 UnitX = new Int2(1, 0); + + /// + /// The Y unit (0, 1, 0). + /// + public static readonly Int2 UnitY = new Int2(0, 1); + + /// + /// A with all of its components set to one. + /// + public static readonly Int2 One = new Int2(1, 1); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public int X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public int Y; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Int2(int value) + { + X = value; + Y = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + public Int2(int x, int y) + { + X = x; + Y = y; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + public Int2(Vec2 value) + { + X = (int)value.X; + Y = (int)value.Y; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, and Z components of the vector. This must be an array with three elements. + /// Thrown when is null. + /// Thrown when contains more or less than three elements. + public Int2(int[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 2) + throw new ArgumentOutOfRangeException("values", "There must be two and only two input values for Int2."); + + X = values[0]; + Y = values[1]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X or Y component, depending on the index. + /// The index of the component to access. Use 0 for the X component and 1 for the Y component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 1]. + public int this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Int2 run from 0 to 1, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Int2 run from 0 to 1, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + public int Length() + { + return (int)Math.Sqrt((X * X) + (Y * Y)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + public int LengthSquared() + { + return (X * X) + (Y * Y); + } + + /// + /// Raises the exponent for each components. + /// + /// The exponent. + public void Pow(int exponent) + { + X = (int)Math.Pow(X, exponent); + Y = (int)Math.Pow(Y, exponent); + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A two-element array containing the components of the vector. + public int[] ToArray() + { + return new int[] { X, Y }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + public static void Add(ref Int2 left, ref Int2 right, out Int2 result) + { + result = new Int2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static Int2 Add(Int2 left, Int2 right) + { + return new Int2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + public static void Subtract(ref Int2 left, ref Int2 right, out Int2 result) + { + result = new Int2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static Int2 Subtract(Int2 left, Int2 right) + { + return new Int2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Multiply(ref Int2 value, int scale, out Int2 result) + { + result = new Int2(value.X * scale, value.Y * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int2 Multiply(Int2 value, int scale) + { + return new Int2(value.X * scale, value.Y * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + public static void Modulate(ref Int2 left, ref Int2 right, out Int2 result) + { + result = new Int2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + public static Int2 Modulate(Int2 left, Int2 right) + { + return new Int2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Divide(ref Int2 value, int scale, out Int2 result) + { + result = new Int2(value.X / scale, value.Y / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int2 Divide(Int2 value, int scale) + { + return new Int2(value.X / scale, value.Y / scale); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + public static void Negate(ref Int2 value, out Int2 result) + { + result = new Int2(-value.X, -value.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + public static Int2 Negate(Int2 value) + { + return new Int2(-value.X, -value.Y); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Int2 value, ref Int2 min, ref Int2 max, out Int2 result) + { + int x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + int y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + result = new Int2(x, y); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Int2 Clamp(Int2 value, Int2 min, Int2 max) + { + Int2 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + public static void Dot(ref Int2 left, ref Int2 right, out int result) + { + result = (left.X * right.X) + (left.Y * right.Y); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + public static int Dot(Int2 left, Int2 right) + { + return (left.X * right.X) + (left.Y * right.Y); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Int2 start, ref Int2 end, float amount, out Int2 result) + { + result.X = (int)(start.X + ((end.X - start.X) * amount)); + result.Y = (int)(start.Y + ((end.Y - start.Y) * amount)); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Int2 Lerp(Int2 start, Int2 end, float amount) + { + Int2 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Int2 start, ref Int2 end, float amount, out Int2 result) + { + amount = (amount > 1) ? 1 : ((amount < 0) ? 0 : amount); + amount = (amount * amount) * (3 - (2 * amount)); + + result.X = (int)(start.X + ((end.X - start.X) * amount)); + result.Y = (int)(start.Y + ((end.Y - start.Y) * amount)); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Int2 SmoothStep(Int2 start, Int2 end, float amount) + { + Int2 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + public static void Max(ref Int2 left, ref Int2 right, out Int2 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + public static Int2 Max(Int2 left, Int2 right) + { + Int2 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + public static void Min(ref Int2 left, ref Int2 right, out Int2 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + public static Int2 Min(Int2 left, Int2 right) + { + Int2 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static Int2 operator +(Int2 left, Int2 right) + { + return new Int2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + public static Int2 operator +(Int2 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static Int2 operator -(Int2 left, Int2 right) + { + return new Int2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + public static Int2 operator -(Int2 value) + { + return new Int2(-value.X, -value.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int2 operator *(float scale, Int2 value) + { + return new Int2((int)(value.X * scale), (int)(value.Y * scale)); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int2 operator *(Int2 value, float scale) + { + return new Int2((int)(value.X * scale), (int)(value.Y * scale)); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int2 operator /(Int2 value, float scale) + { + return new Int2((int)(value.X / scale), (int)(value.Y / scale)); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Int2 left, Int2 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Int2 left, Int2 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(Int2 value) + { + return new Vec2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Int2 value) + { + return new Vector4(value.X, value.Y, 0, 0); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X, Y); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X.ToString(format, CultureInfo.CurrentCulture), Y.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1}", X, Y); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1}", X.ToString(format, formatProvider), Y.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Int2 other) + { + return ((float)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Int2)value); + } +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Int3D(Int2 value) + { + return new System.Windows.Media.Media3D.Int3D(value.X, value.Y, 0.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Int2(System.Windows.Media.Media3D.Int3D value) + { + return new Int2((float)value.X, (float)value.Y); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Int2(Int2 value) + { + return new Microsoft.Xna.Framework.Int2(value.X, value.Y); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Int2(Microsoft.Xna.Framework.Int2 value) + { + return new Int2(value.X, value.Y); + } +#endif + } +} diff --git a/math/Int3.cs b/math/Int3.cs new file mode 100644 index 0000000..6c35504 --- /dev/null +++ b/math/Int3.cs @@ -0,0 +1,805 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a three dimensional mathematical vector. + /// + [DataContract( Name = "Int3")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Int3 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Int3 Zero = new Int3(); + + /// + /// The X unit (1, 0, 0). + /// + public static readonly Int3 UnitX = new Int3(1, 0, 0); + + /// + /// The Y unit (0, 1, 0). + /// + public static readonly Int3 UnitY = new Int3(0, 1, 0); + + /// + /// The Z unit (0, 0, 1). + /// + public static readonly Int3 UnitZ = new Int3(0, 0, 1); + + /// + /// A with all of its components set to one. + /// + public static readonly Int3 One = new Int3(1, 1, 1); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public int X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public int Y; + + /// + /// The Z component of the vector. + /// + [DataMember( Order = 2 )] + public int Z; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Int3(int value) + { + X = value; + Y = value; + Z = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + /// Initial value for the Z component of the vector. + public Int3(int x, int y, int z) + { + X = x; + Y = y; + Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + /// Initial value for the Z component of the vector. + public Int3(Vec2 value, int z) + { + X = (int)value.X; + Y = (int)value.Y; + Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, and Z components of the vector. This must be an array with three elements. + /// Thrown when is null. + /// Thrown when contains more or less than three elements. + public Int3(int[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 3) + throw new ArgumentOutOfRangeException("values", "There must be three and only three input values for Int3."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, or Z component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, and 2 for the Z component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 2]. + public int this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Int3 run from 0 to 2, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Int3 run from 0 to 2, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + public int Length() + { + return (int)Math.Sqrt((X * X) + (Y * Y) + (Z * Z)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + public int LengthSquared() + { + return (X * X) + (Y * Y) + (Z * Z); + } + + /// + /// Raises the exponent for each components. + /// + /// The exponent. + public void Pow(int exponent) + { + X = (int)Math.Pow(X, exponent); + Y = (int)Math.Pow(Y, exponent); + Z = (int)Math.Pow(Z, exponent); + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A three-element array containing the components of the vector. + public int[] ToArray() + { + return new int[] { X, Y, Z }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + public static void Add(ref Int3 left, ref Int3 right, out Int3 result) + { + result = new Int3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static Int3 Add(Int3 left, Int3 right) + { + return new Int3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + public static void Subtract(ref Int3 left, ref Int3 right, out Int3 result) + { + result = new Int3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static Int3 Subtract(Int3 left, Int3 right) + { + return new Int3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Multiply(ref Int3 value, int scale, out Int3 result) + { + result = new Int3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int3 Multiply(Int3 value, int scale) + { + return new Int3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + public static void Modulate(ref Int3 left, ref Int3 right, out Int3 result) + { + result = new Int3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + public static Int3 Modulate(Int3 left, Int3 right) + { + return new Int3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Divide(ref Int3 value, int scale, out Int3 result) + { + result = new Int3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int3 Divide(Int3 value, int scale) + { + return new Int3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + public static void Negate(ref Int3 value, out Int3 result) + { + result = new Int3(-value.X, -value.Y, -value.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + public static Int3 Negate(Int3 value) + { + return new Int3(-value.X, -value.Y, -value.Z); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Int3 value, ref Int3 min, ref Int3 max, out Int3 result) + { + int x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + int y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + int z = value.Z; + z = (z > max.Z) ? max.Z : z; + z = (z < min.Z) ? min.Z : z; + + result = new Int3(x, y, z); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Int3 Clamp(Int3 value, Int3 min, Int3 max) + { + Int3 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + public static void Dot(ref Int3 left, ref Int3 right, out int result) + { + result = (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + public static int Dot(Int3 left, Int3 right) + { + return (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Int3 start, ref Int3 end, float amount, out Int3 result) + { + result.X = (int)(start.X + ((end.X - start.X) * amount)); + result.Y = (int)(start.Y + ((end.Y - start.Y) * amount)); + result.Z = (int)(start.Z + ((end.Z - start.Z) * amount)); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Int3 Lerp(Int3 start, Int3 end, float amount) + { + Int3 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Int3 start, ref Int3 end, float amount, out Int3 result) + { + amount = (amount > 1) ? 1 : ((amount < 0) ? 0 : amount); + amount = (amount * amount) * (3 - (2 * amount)); + + result.X = (int)(start.X + ((end.X - start.X) * amount)); + result.Y = (int)(start.Y + ((end.Y - start.Y) * amount)); + result.Z = (int)(start.Z + ((end.Z - start.Z) * amount)); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Int3 SmoothStep(Int3 start, Int3 end, float amount) + { + Int3 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + public static void Max(ref Int3 left, ref Int3 right, out Int3 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + result.Z = (left.Z > right.Z) ? left.Z : right.Z; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + public static Int3 Max(Int3 left, Int3 right) + { + Int3 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + public static void Min(ref Int3 left, ref Int3 right, out Int3 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + result.Z = (left.Z < right.Z) ? left.Z : right.Z; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + public static Int3 Min(Int3 left, Int3 right) + { + Int3 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static Int3 operator +(Int3 left, Int3 right) + { + return new Int3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + public static Int3 operator +(Int3 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static Int3 operator -(Int3 left, Int3 right) + { + return new Int3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + public static Int3 operator -(Int3 value) + { + return new Int3(-value.X, -value.Y, -value.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int3 operator *(float scale, Int3 value) + { + return new Int3((int)(value.X * scale), (int)(value.Y * scale), (int)(value.Z * scale)); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int3 operator *(Int3 value, float scale) + { + return new Int3((int)(value.X * scale), (int)(value.Y * scale), (int)(value.Z * scale)); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int3 operator /(Int3 value, float scale) + { + return new Int3((int)(value.X / scale), (int)(value.Y / scale), (int)(value.Z / scale)); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Int3 left, Int3 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Int3 left, Int3 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(Int3 value) + { + return new Vec2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Int3 value) + { + return new Vector3(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Int3 value) + { + return new Vector4(value.X, value.Y, value.Z, 0); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}", X, Y, Z); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}", X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2}", X, Y, Z); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Int3 other) + { + return ((float)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Z - Z) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Int3)value); + } +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Int3D(Int3 value) + { + return new System.Windows.Media.Media3D.Int3D(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Int3(System.Windows.Media.Media3D.Int3D value) + { + return new Int3((float)value.X, (float)value.Y, (float)value.Z); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Int3(Int3 value) + { + return new Microsoft.Xna.Framework.Int3(value.X, value.Y, value.Z); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Int3(Microsoft.Xna.Framework.Int3 value) + { + return new Int3(value.X, value.Y, value.Z); + } +#endif + } +} diff --git a/math/Int4.cs b/math/Int4.cs new file mode 100644 index 0000000..93d2b5d --- /dev/null +++ b/math/Int4.cs @@ -0,0 +1,700 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2011 SharpDX - Alexandre Mutel +// +// 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. +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a four dimensional mathematical vector. + /// + [DataContract( Name = "Int4")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Int4 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Int4 Zero = new Int4(); + + /// + /// The X unit (1, 0, 0, 0). + /// + public static readonly Int4 UnitX = new Int4(1, 0, 0, 0); + + /// + /// The Y unit (0, 1, 0, 0). + /// + public static readonly Int4 UnitY = new Int4(0, 1, 0, 0); + + /// + /// The Z unit (0, 0, 1, 0). + /// + public static readonly Int4 UnitZ = new Int4(0, 0, 1, 0); + + /// + /// The W unit (0, 0, 0, 1). + /// + public static readonly Int4 UnitW = new Int4(0, 0, 0, 1); + + /// + /// A with all of its components set to one. + /// + public static readonly Int4 One = new Int4(1, 1, 1, 1); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public int X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public int Y; + + /// + /// The Z component of the vector. + /// + [DataMember( Order = 2 )] + public int Z; + + /// + /// The W component of the vector. + /// + [DataMember( Order = 3 )] + public int W; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Int4(int value) + { + X = value; + Y = value; + Z = value; + W = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + /// Initial value for the Z component of the vector. + /// Initial value for the W component of the vector. + public Int4(int x, int y, int z, int w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, Z, and W components of the vector. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Int4(int[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 4) + throw new ArgumentOutOfRangeException("values", "There must be four and only four input values for Int4."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + W = values[3]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, Z, or W component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public int this[int index] + { + get + { + switch (index) + { + case 0: + return X; + case 1: + return Y; + case 2: + return Z; + case 3: + return W; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Int4 run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: + X = value; + break; + case 1: + Y = value; + break; + case 2: + Z = value; + break; + case 3: + W = value; + break; + default: + throw new ArgumentOutOfRangeException("index", "Indices for Int4 run from 0 to 3, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + public int Length() + { + return (int)Math.Sqrt((X * X) + (Y * Y) + (Z * Z) + (W * W)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + public int LengthSquared() + { + return (X * X) + (Y * Y) + (Z * Z) + (W * W); + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A four-element array containing the components of the vector. + public int[] ToArray() + { + return new int[] { X, Y, Z, W }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + public static void Add(ref Int4 left, ref Int4 right, out Int4 result) + { + result = new Int4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static Int4 Add(Int4 left, Int4 right) + { + return new Int4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + public static void Subtract(ref Int4 left, ref Int4 right, out Int4 result) + { + result = new Int4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static Int4 Subtract(Int4 left, Int4 right) + { + return new Int4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Multiply(ref Int4 value, int scale, out Int4 result) + { + result = new Int4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int4 Multiply(Int4 value, int scale) + { + return new Int4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + public static void Modulate(ref Int4 left, ref Int4 right, out Int4 result) + { + result = new Int4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + public static Int4 Modulate(Int4 left, Int4 right) + { + return new Int4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Divide(ref Int4 value, int scale, out Int4 result) + { + result = new Int4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int4 Divide(Int4 value, int scale) + { + return new Int4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + public static void Negate(ref Int4 value, out Int4 result) + { + result = new Int4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + public static Int4 Negate(Int4 value) + { + return new Int4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Int4 value, ref Int4 min, ref Int4 max, out Int4 result) + { + int x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + int y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + int z = value.Z; + z = (z > max.Z) ? max.Z : z; + z = (z < min.Z) ? min.Z : z; + + int w = value.W; + w = (w > max.W) ? max.W : w; + w = (w < min.W) ? min.W : w; + + result = new Int4(x, y, z, w); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Int4 Clamp(Int4 value, Int4 min, Int4 max) + { + Int4 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + public static void Max(ref Int4 left, ref Int4 right, out Int4 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + result.Z = (left.Z > right.Z) ? left.Z : right.Z; + result.W = (left.W > right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + public static Int4 Max(Int4 left, Int4 right) + { + Int4 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + public static void Min(ref Int4 left, ref Int4 right, out Int4 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + result.Z = (left.Z < right.Z) ? left.Z : right.Z; + result.W = (left.W < right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + public static Int4 Min(Int4 left, Int4 right) + { + Int4 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static Int4 operator +(Int4 left, Int4 right) + { + return new Int4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + public static Int4 operator +(Int4 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static Int4 operator -(Int4 left, Int4 right) + { + return new Int4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + public static Int4 operator -(Int4 value) + { + return new Int4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int4 operator *(int scale, Int4 value) + { + return new Int4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int4 operator *(Int4 value, int scale) + { + return new Int4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static Int4 operator /(Int4 value, int scale) + { + return new Int4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Int4 left, Int4 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Int4 left, Int4 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(Int4 value) + { + return new Vec2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Int4 value) + { + return new Vector3(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Int4 value) + { + return new Vector4(value.X, value.Y, value.Z, value.W); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", + X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), + Z.ToString(format, CultureInfo.CurrentCulture), + W.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider), + W.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Int4 other) + { + return other.X == X && other.Y == Y && other.Z == Z && other.W == W; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Int4)value); + } + + /// + /// Performs an implicit conversion from array to . + /// + /// The input. + /// The result of the conversion. + public static implicit operator Int4(int[] input) + { + return new Int4(input); + } + + /// + /// Performs an implicit conversion from to array. + /// + /// The input. + /// The result of the conversion. + public static implicit operator int[](Int4 input) + { + return input.ToArray(); + } + } +} diff --git a/math/MathUtil.cs b/math/MathUtil.cs new file mode 100644 index 0000000..9433c5b --- /dev/null +++ b/math/MathUtil.cs @@ -0,0 +1,656 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Common utility methods for math operations. + /// + public static class MathUtil + { + /// + /// The value for which all absolute numbers smaller than are considered equal to zero. + /// + public const float ZeroTolerance = 1e-6f; // Value a 8x higher than 1.19209290E-07F + + /// + /// The value for which all absolute numbers smaller than are considered equal to zero. + /// + public const double ZeroToleranceDouble = double.Epsilon * 8; + + /// + /// A value specifying the approximation of π which is 180 degrees. + /// + public const float Pi = (float)Math.PI; + + /// + /// A value specifying the approximation of 2π which is 360 degrees. + /// + public const float TwoPi = (float)(2 * Math.PI); + + /// + /// A value specifying the approximation of π/2 which is 90 degrees. + /// + public const float PiOverTwo = (float)(Math.PI / 2); + + /// + /// A value specifying the approximation of π/4 which is 45 degrees. + /// + public const float PiOverFour = (float)(Math.PI / 4); + + /// + /// Checks if a and b are almost equals, taking into account the magnitude of floating point numbers (unlike method). See Remarks. + /// See remarks. + /// + /// The left value to compare. + /// The right value to compare. + /// true if a almost equal to b, false otherwise + /// + /// The code is using the technique described by Bruce Dawson in + /// Comparing Floating point numbers 2012 edition. + /// + public static unsafe bool NearEqual(float a, float b) + { + // Check if the numbers are really close -- needed + // when comparing numbers near zero. + if (IsZero(a - b)) + return true; + + // Original from Bruce Dawson: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + int aInt = *(int*)&a; + int bInt = *(int*)&b; + + // Different signs means they do not match. + if ((aInt < 0) != (bInt < 0)) + return false; + + // Find the difference in ULPs. + int ulp = Math.Abs(aInt - bInt); + + // Choose of maxUlp = 4 + // according to http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h + const int maxUlp = 4; + return (ulp <= maxUlp); + } + + /// + /// Determines whether the specified value is close to zero (0.0f). + /// + /// The floating value. + /// true if the specified value is close to zero (0.0f); otherwise, false. + public static bool IsZero(float a) + { + return Math.Abs(a) < ZeroTolerance; + } + + /// + /// Determines whether the specified value is close to zero (0.0f). + /// + /// The floating value. + /// true if the specified value is close to zero (0.0f); otherwise, false. + public static bool IsZero(double a) + { + return Math.Abs(a) < ZeroToleranceDouble; + } + + /// + /// Determines whether the specified value is close to one (1.0f). + /// + /// The floating value. + /// true if the specified value is close to one (1.0f); otherwise, false. + public static bool IsOne(float a) + { + return IsZero(a - 1.0f); + } + + /// + /// Checks if a - b are almost equals within a float epsilon. + /// + /// The left value to compare. + /// The right value to compare. + /// Epsilon value + /// true if a almost equal to b within a float epsilon, false otherwise + public static bool WithinEpsilon(float a, float b, float epsilon) + { + float num = a - b; + return ((-epsilon <= num) && (num <= epsilon)); + } + + /// + /// Creates a one-dimensional array of the specified and filled with the specified . + /// + /// The Type of the array to create. + /// The value to fill the array with. + /// The size of the array to create. + /// A new one-dimensional array of the specified type with the specified length and filled with the specified value. + public static T[] Array(T value, int length) + { + var result = new T[length]; + for (var i = 0; i < length; i++) + result[i] = value; + + return result; + } + + /// + /// Converts revolutions to degrees. + /// + /// The value to convert. + /// The converted value. + public static float RevolutionsToDegrees(float revolution) + { + return revolution * 360.0f; + } + + /// + /// Converts revolutions to radians. + /// + /// The value to convert. + /// The converted value. + public static float RevolutionsToRadians(float revolution) + { + return revolution * TwoPi; + } + + /// + /// Converts revolutions to gradians. + /// + /// The value to convert. + /// The converted value. + public static float RevolutionsToGradians(float revolution) + { + return revolution * 400.0f; + } + + /// + /// Converts degrees to revolutions. + /// + /// The value to convert. + /// The converted value. + public static float DegreesToRevolutions(float degree) + { + return degree / 360.0f; + } + + /// + /// Converts degrees to radians. + /// + /// The value to convert. + /// The converted value. + public static float DegreesToRadians(float degree) + { + return degree * (Pi / 180.0f); + } + + /// + /// Converts radians to revolutions. + /// + /// The value to convert. + /// The converted value. + public static float RadiansToRevolutions(float radian) + { + return radian / TwoPi; + } + + /// + /// Converts radians to gradians. + /// + /// The value to convert. + /// The converted value. + public static float RadiansToGradians(float radian) + { + return radian * (200.0f / Pi); + } + + /// + /// Converts gradians to revolutions. + /// + /// The value to convert. + /// The converted value. + public static float GradiansToRevolutions(float gradian) + { + return gradian / 400.0f; + } + + /// + /// Converts gradians to degrees. + /// + /// The value to convert. + /// The converted value. + public static float GradiansToDegrees(float gradian) + { + return gradian * (9.0f / 10.0f); + } + + /// + /// Converts gradians to radians. + /// + /// The value to convert. + /// The converted value. + public static float GradiansToRadians(float gradian) + { + return gradian * (Pi / 200.0f); + } + + /// + /// Converts radians to degrees. + /// + /// The value to convert. + /// The converted value. + public static float RadiansToDegrees(float radian) + { + return radian * (180.0f / Pi); + } + + /// + /// Clamps the specified value. + /// + /// The value. + /// The min. + /// The max. + /// The result of clamping a value between min and max + public static float Clamp(float value, float min, float max) + { + return value < min ? min : value > max ? max : value; + } + + /// + /// Clamps the specified value. + /// + /// The value. + /// The min. + /// The max. + /// The result of clamping a value between min and max + public static double Clamp(double value, double min, double max) + { + return value < min ? min : value > max ? max : value; + } + + /// + /// Clamps the specified value. + /// + /// The value. + /// The min. + /// The max. + /// The result of clamping a value between min and max + public static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; + } + + /// + /// Inverse-interpolates a value linearly. + /// + /// Minimum value that takes place in inverse-interpolation. + /// Maximum value that takes place in inverse-interpolation. + /// Value to get inverse interpolation. + /// Returns an inverse-linearly interpolated coeficient. + public static float InverseLerp(float min, float max, float value) + { + if (IsZero(Math.Abs(max - min))) + return float.NaN; + return (value - min) / (max - min); + } + + /// + /// Inverse-interpolates a value linearly. + /// + /// Minimum value that takes place in inverse-interpolation. + /// Maximum value that takes place in inverse-interpolation. + /// Value to get inverse interpolation. + /// Returns an inverse-linearly interpolated coeficient. + public static double InverseLerp(double min, double max, double value) + { + if (IsZero(Math.Abs(max - min))) + return double.NaN; + return (value - min) / (max - min); + } + + /// + /// Interpolates between two values using a linear function by a given amount. + /// + /// + /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and + /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ + /// + /// Value to interpolate from. + /// Value to interpolate to. + /// Interpolation amount. + /// The result of linear interpolation of values based on the amount. + public static double Lerp(double from, double to, double amount) + { + return (1 - amount) * from + amount * to; + } + + /// + /// Interpolates between two values using a linear function by a given amount. + /// + /// + /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and + /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ + /// + /// Value to interpolate from. + /// Value to interpolate to. + /// Interpolation amount. + /// The result of linear interpolation of values based on the amount. + public static float Lerp(float from, float to, float amount) + { + return (1 - amount) * from + amount * to; + } + + /// + /// Interpolates between two values using a linear function by a given amount. + /// + /// + /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and + /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ + /// + /// Value to interpolate from. + /// Value to interpolate to. + /// Interpolation amount. + /// The result of linear interpolation of values based on the amount. + public static byte Lerp(byte from, byte to, float amount) + { + return (byte)Lerp((float)from, (float)to, amount); + } + + /// + /// Performs smooth (cubic Hermite) interpolation between 0 and 1. + /// + /// + /// See https://en.wikipedia.org/wiki/Smoothstep + /// + /// Value between 0 and 1 indicating interpolation amount. + public static float SmoothStep(float amount) + { + return (amount <= 0) ? 0 + : (amount >= 1) ? 1 + : amount * amount * (3 - (2 * amount)); + } + + /// + /// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints. + /// + /// + /// See https://en.wikipedia.org/wiki/Smoothstep + /// + /// Value between 0 and 1 indicating interpolation amount. + public static float SmootherStep(float amount) + { + return (amount <= 0) ? 0 + : (amount >= 1) ? 1 + : amount * amount * amount * (amount * ((amount * 6) - 15) + 10); + } + + /// + /// Determines whether the value is inside the given range (inclusively). + /// + /// The value. + /// The minimum value of the range. + /// The maximum value of the range. + /// true if value is inside the specified range; otherwise, false. + public static bool IsInRange(float value, float min, float max) + { + return min <= value && value <= max; + } + + /// + /// Determines whether the value is inside the given range (inclusively). + /// + /// The value. + /// The minimum value of the range. + /// The maximum value of the range. + /// true if value is inside the specified range; otherwise, false. + public static bool IsInRange(int value, int min, int max) + { + return min <= value && value <= max; + } + + /// + /// Determines whether the specified x is pow2. + /// + /// The x. + /// true if the specified x is pow2; otherwise, false. + public static bool IsPow2(int x) + { + return ((x != 0) && (x & (x - 1)) == 0); + } + + /// + /// Converts a float value from sRGB to linear. + /// + /// The sRGB value. + /// A linear value. + public static float SRgbToLinear(float sRgbValue) + { + if (sRgbValue < 0.04045f) return sRgbValue / 12.92f; + return (float)Math.Pow((sRgbValue + 0.055) / 1.055, 2.4); + } + + /// + /// Converts a float value from linear to sRGB. + /// + /// The linear value. + /// The encoded sRGB value. + public static float LinearToSRgb(float linearValue) + { + if (linearValue < 0.0031308f) return linearValue * 12.92f; + return (float)(1.055 * Math.Pow(linearValue, 1 / 2.4) - 0.055); + } + + /// + /// Calculate the logarithm 2 of a floating point. + /// + /// The input float + /// Log2(x) + public static float Log2(float x) + { + return (float)Math.Log(x) / 0.6931471805599453f; + } + + /// + /// Calculate the logarithm 2 of an integer. + /// + /// The input integer + /// the log2(i) rounded to lower integer + public static int Log2(int i) + { + var r = 0; + + while ((i >>= 1) != 0) + ++r; + + return r; + } + + /// + /// Get the next power of two of an integer. + /// + /// The size. + /// System.Int32. + /// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + public static int NextPowerOfTwo(int x) + { + if (x < 0) + return 0; + + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; + } + + /// + /// Get the next power of two for a size. + /// + /// The size. + /// System.Int32. + public static float NextPowerOfTwo(float size) + { + return (float)Math.Pow(2, Math.Ceiling(Math.Log(size, 2))); + } + + /// + /// Get the previous power of two of the provided integer. + /// + /// The value + public static int PreviousPowerOfTwo(int size) + { + return 1 << (int)Math.Floor(Math.Log(size, 2)); + } + + /// + /// Get the previous power of two of the provided float. + /// + /// The value + public static float PreviousPowerOfTwo(float size) + { + return (float)Math.Pow(2, Math.Floor(Math.Log(size, 2))); + } + + /// + /// Alignes value up to match desire alignment. + /// + /// The value. + /// The alignment. + /// Aligned value (multiple of alignment). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int AlignUp(int value, int alignment) + { + int mask = alignment - 1; + return (value + mask) & ~mask; + } + + /// + /// Alignes value down to match desire alignment. + /// + /// The value. + /// The alignment. + /// Aligned value (multiple of alignment). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int AlignDown(int value, int alignment) + { + int mask = alignment - 1; + return value & ~mask; + } + + /// + /// Determines whether the specified value is aligned. + /// + /// The value. + /// The alignment. + /// true if the specified value is aligned; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAligned(int value, int alignment) + { + return (value & (alignment - 1)) == 0; + } + + /// + /// Snaps a value to the nearest interval. + /// + /// The value to snap. + /// The interval gap. + /// The nearest interval to the provided value. + public static float Snap(float value, float gap) + { + if (gap == 0) + return value; + return (float)Math.Round((value / gap), MidpointRounding.AwayFromZero) * gap; + } + + /// + /// Snaps a value to the nearest interval. + /// + /// The value to snap. + /// The interval gap. + /// The nearest interval to the provided value. + public static double Snap(double value, double gap) + { + if (gap == 0) + return value; + return Math.Round((value / gap), MidpointRounding.AwayFromZero) * gap; + } + + /// + /// Snaps all vector components to the nearest interval. + /// + /// The vector to snap. + /// The interval gap. + /// A vector which components are snapped to the nearest interval. + public static Vec2 Snap(Vec2 value, float gap) + { + if (gap == 0) + return value; + return new Vec2( + (float)Math.Round((value.X / gap), MidpointRounding.AwayFromZero) * gap, + (float)Math.Round((value.Y / gap), MidpointRounding.AwayFromZero) * gap); + } + + /// + /// Snaps all vector components to the nearest interval. + /// + /// The vector to snap. + /// The interval gap. + /// A vector which components are snapped to the nearest interval. + public static Vector3 Snap(Vector3 value, float gap) + { + if (gap == 0) + return value; + return new Vector3( + (float)Math.Round((value.X / gap), MidpointRounding.AwayFromZero) * gap, + (float)Math.Round((value.Y / gap), MidpointRounding.AwayFromZero) * gap, + (float)Math.Round((value.Z / gap), MidpointRounding.AwayFromZero) * gap); + } + + /// + /// Snaps all vector components to the nearest interval. + /// + /// The vector to snap. + /// The interval gap. + /// A vector which components are snapped to the nearest interval. + public static Vector4 Snap(Vector4 value, float gap) + { + if (gap == 0) + return value; + return new Vector4( + (float)Math.Round((value.X / gap), MidpointRounding.AwayFromZero) * gap, + (float)Math.Round((value.Y / gap), MidpointRounding.AwayFromZero) * gap, + (float)Math.Round((value.Z / gap), MidpointRounding.AwayFromZero) * gap, + (float)Math.Round((value.W / gap), MidpointRounding.AwayFromZero) * gap); + } + } +} diff --git a/math/Matrix.cs b/math/Matrix.cs new file mode 100644 index 0000000..6e905dd --- /dev/null +++ b/math/Matrix.cs @@ -0,0 +1,3482 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +#pragma warning disable SA1107 // Code must not contain multiple statements on one line +#pragma warning disable SA1117 // Parameters must be on same line or separate lines +#pragma warning disable SA1313 // Parameter names must begin with lower-case letter +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a 4x4 mathematical matrix. + /// + [DataContract( Name = "float4x4")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Matrix : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Matrix Zero = new Matrix(); + + /// + /// The identity . + /// + public static readonly Matrix Identity = new Matrix() { M11 = 1.0f, M22 = 1.0f, M33 = 1.0f, M44 = 1.0f }; + + /// + /// Value at row 1 column 1 of the matrix. + /// + public float M11; + + /// + /// Value at row 2 column 1 of the matrix. + /// + public float M21; + + /// + /// Value at row 3 column 1 of the matrix. + /// + public float M31; + + /// + /// Value at row 4 column 1 of the matrix. + /// + public float M41; + + /// + /// Value at row 1 column 2 of the matrix. + /// + public float M12; + + /// + /// Value at row 2 column 2 of the matrix. + /// + public float M22; + + /// + /// Value at row 3 column 2 of the matrix. + /// + public float M32; + + /// + /// Value at row 4 column 2 of the matrix. + /// + public float M42; + + /// + /// Value at row 1 column 3 of the matrix. + /// + public float M13; + + /// + /// Value at row 2 column 3 of the matrix. + /// + public float M23; + + /// + /// Value at row 3 column 3 of the matrix. + /// + public float M33; + + /// + /// Value at row 4 column 3 of the matrix. + /// + public float M43; + + /// + /// Value at row 1 column 4 of the matrix. + /// + public float M14; + + /// + /// Value at row 2 column 4 of the matrix. + /// + public float M24; + + /// + /// Value at row 3 column 4 of the matrix. + /// + public float M34; + + /// + /// Value at row 4 column 4 of the matrix. + /// + public float M44; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Matrix(float value) + { + M11 = M21 = M31 = M41 = + M12 = M22 = M32 = M42 = + M13 = M23 = M33 = M43 = + M14 = M24 = M34 = M44 = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The value to assign at row 1 column 1 of the matrix. + /// The value to assign at row 1 column 2 of the matrix. + /// The value to assign at row 1 column 3 of the matrix. + /// The value to assign at row 1 column 4 of the matrix. + /// The value to assign at row 2 column 1 of the matrix. + /// The value to assign at row 2 column 2 of the matrix. + /// The value to assign at row 2 column 3 of the matrix. + /// The value to assign at row 2 column 4 of the matrix. + /// The value to assign at row 3 column 1 of the matrix. + /// The value to assign at row 3 column 2 of the matrix. + /// The value to assign at row 3 column 3 of the matrix. + /// The value to assign at row 3 column 4 of the matrix. + /// The value to assign at row 4 column 1 of the matrix. + /// The value to assign at row 4 column 2 of the matrix. + /// The value to assign at row 4 column 3 of the matrix. + /// The value to assign at row 4 column 4 of the matrix. + public Matrix(float M11, float M12, float M13, float M14, + float M21, float M22, float M23, float M24, + float M31, float M32, float M33, float M34, + float M41, float M42, float M43, float M44) + { + this.M11 = M11; this.M12 = M12; this.M13 = M13; this.M14 = M14; + this.M21 = M21; this.M22 = M22; this.M23 = M23; this.M24 = M24; + this.M31 = M31; this.M32 = M32; this.M33 = M33; this.M34 = M34; + this.M41 = M41; this.M42 = M42; this.M43 = M43; this.M44 = M44; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the components of the matrix. This must be an array with sixteen elements. + /// Thrown when is null. + /// Thrown when contains more or less than sixteen elements. + public Matrix(float[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 16) + throw new ArgumentOutOfRangeException("values", "There must be sixteen and only sixteen input values for Matrix."); + + M11 = values[0]; + M12 = values[1]; + M13 = values[2]; + M14 = values[3]; + + M21 = values[4]; + M22 = values[5]; + M23 = values[6]; + M24 = values[7]; + + M31 = values[8]; + M32 = values[9]; + M33 = values[10]; + M34 = values[11]; + + M41 = values[12]; + M42 = values[13]; + M43 = values[14]; + M44 = values[15]; + } + + /// + /// Gets or sets the first row in the matrix; that is M11, M12, M13, and M14. + /// + [DataMemberIgnore] + public Vector4 Row1 + { + get { return new Vector4(M11, M12, M13, M14); } + set { M11 = value.X; M12 = value.Y; M13 = value.Z; M14 = value.W; } + } + + /// + /// Gets or sets the second row in the matrix; that is M21, M22, M23, and M24. + /// + [DataMemberIgnore] + public Vector4 Row2 + { + get { return new Vector4(M21, M22, M23, M24); } + set { M21 = value.X; M22 = value.Y; M23 = value.Z; M24 = value.W; } + } + + /// + /// Gets or sets the third row in the matrix; that is M31, M32, M33, and M34. + /// + [DataMemberIgnore] + public Vector4 Row3 + { + get { return new Vector4(M31, M32, M33, M34); } + set { M31 = value.X; M32 = value.Y; M33 = value.Z; M34 = value.W; } + } + + /// + /// Gets or sets the fourth row in the matrix; that is M41, M42, M43, and M44. + /// + [DataMemberIgnore] + public Vector4 Row4 + { + get { return new Vector4(M41, M42, M43, M44); } + set { M41 = value.X; M42 = value.Y; M43 = value.Z; M44 = value.W; } + } + + /// + /// Gets or sets the first column in the matrix; that is M11, M21, M31, and M41. + /// + [DataMemberIgnore] + public Vector4 Column1 + { + get { return new Vector4(M11, M21, M31, M41); } + set { M11 = value.X; M21 = value.Y; M31 = value.Z; M41 = value.W; } + } + + /// + /// Gets or sets the second column in the matrix; that is M12, M22, M32, and M42. + /// + [DataMemberIgnore] + public Vector4 Column2 + { + get { return new Vector4(M12, M22, M32, M42); } + set { M12 = value.X; M22 = value.Y; M32 = value.Z; M42 = value.W; } + } + + /// + /// Gets or sets the third column in the matrix; that is M13, M23, M33, and M43. + /// + [DataMemberIgnore] + public Vector4 Column3 + { + get { return new Vector4(M13, M23, M33, M43); } + set { M13 = value.X; M23 = value.Y; M33 = value.Z; M43 = value.W; } + } + + /// + /// Gets or sets the fourth column in the matrix; that is M14, M24, M34, and M44. + /// + [DataMemberIgnore] + public Vector4 Column4 + { + get { return new Vector4(M14, M24, M34, M44); } + set { M14 = value.X; M24 = value.Y; M34 = value.Z; M44 = value.W; } + } + + /// + /// Gets or sets the translation of the matrix; that is M41, M42, and M43. + /// + [DataMemberIgnore] + public Vector3 TranslationVector + { + get { return new Vector3(M41, M42, M43); } + set { M41 = value.X; M42 = value.Y; M43 = value.Z; } + } + + /// + /// Gets or sets the scale of the matrix; that is M11, M22, and M33. + /// + /// This property does not do any computation and will return a correct scale vector only if the matrix is a scale matrix. + [DataMemberIgnore] + public Vector3 ScaleVector + { + get { return new Vector3(M11, M22, M33); } + set { M11 = value.X; M22 = value.Y; M33 = value.Z; } + } + + /// + /// Gets or sets the up of the matrix; that is M21, M22, and M23. + /// + [DataMemberIgnore] + public Vector3 Up + { + get { return new Vector3(M21, M22, M23); } + set { M21 = value.X; M22 = value.Y; M23 = value.Z; } + } + + /// + /// Gets or sets the down of the matrix; that is -M21, -M22, and -M23. + /// + [DataMemberIgnore] + public Vector3 Down + { + get { return new Vector3(-M21, -M22, -M23); } + set { M21 = -value.X; M22 = -value.Y; M23 = -value.Z; } + } + + /// + /// Gets or sets the right of the matrix; that is M11, M12, and M13. + /// + [DataMemberIgnore] + public Vector3 Right + { + get { return new Vector3(M11, M12, M13); } + set { M11 = value.X; M12 = value.Y; M13 = value.Z; } + } + + /// + /// Gets or sets the left of the matrix; that is -M11, -M12, and -M13. + /// + [DataMemberIgnore] + public Vector3 Left + { + get { return new Vector3(-M11, -M12, -M13); } + set { M11 = -value.X; M12 = -value.Y; M13 = -value.Z; } + } + + /// + /// Gets or sets the forward of the matrix; that is -M31, -M32, and -M33. + /// + [DataMemberIgnore] + public Vector3 Forward + { + get { return new Vector3(-M31, -M32, -M33); } + set { M31 = -value.X; M32 = -value.Y; M33 = -value.Z; } + } + + /// + /// Gets or sets the backward of the matrix; that is M31, M32, and M33. + /// + [DataMemberIgnore] + public Vector3 Backward + { + get { return new Vector3(M31, M32, M33); } + set { M31 = value.X; M32 = value.Y; M33 = value.Z; } + } + + /// + /// Gets a value indicating whether this instance is an identity matrix. + /// + /// + /// true if this instance is an identity matrix; otherwise, false. + /// + public bool IsIdentity + { + get { return this.Equals(Identity); } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the matrix component, depending on the index. + /// The zero-based index of the component to access. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 15]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return M11; + case 1: return M12; + case 2: return M13; + case 3: return M14; + case 4: return M21; + case 5: return M22; + case 6: return M23; + case 7: return M24; + case 8: return M31; + case 9: return M32; + case 10: return M33; + case 11: return M34; + case 12: return M41; + case 13: return M42; + case 14: return M43; + case 15: return M44; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Matrix run from 0 to 15, inclusive."); + } + + set + { + switch (index) + { + case 0: M11 = value; break; + case 1: M12 = value; break; + case 2: M13 = value; break; + case 3: M14 = value; break; + case 4: M21 = value; break; + case 5: M22 = value; break; + case 6: M23 = value; break; + case 7: M24 = value; break; + case 8: M31 = value; break; + case 9: M32 = value; break; + case 10: M33 = value; break; + case 11: M34 = value; break; + case 12: M41 = value; break; + case 13: M42 = value; break; + case 14: M43 = value; break; + case 15: M44 = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Matrix run from 0 to 15, inclusive."); + } + } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the matrix component, depending on the index. + /// The row of the matrix to access. + /// The column of the matrix to access. + /// The value of the component at the specified index. + /// Thrown when the or is out of the range [0, 3]. + public float this[int row, int column] + { + get + { + if (row < 0 || row > 3) + throw new ArgumentOutOfRangeException("row", "Rows and columns for matrices run from 0 to 3, inclusive."); + if (column < 0 || column > 3) + throw new ArgumentOutOfRangeException("column", "Rows and columns for matrices run from 0 to 3, inclusive."); + + return this[(row * 4) + column]; + } + + set + { + if (row < 0 || row > 3) + throw new ArgumentOutOfRangeException("row", "Rows and columns for matrices run from 0 to 3, inclusive."); + if (column < 0 || column > 3) + throw new ArgumentOutOfRangeException("column", "Rows and columns for matrices run from 0 to 3, inclusive."); + + this[(row * 4) + column] = value; + } + } + + /// + /// Calculates the determinant of the matrix. + /// + /// The determinant of the matrix. + public float Determinant() + { + float temp1 = (M33 * M44) - (M34 * M43); + float temp2 = (M32 * M44) - (M34 * M42); + float temp3 = (M32 * M43) - (M33 * M42); + float temp4 = (M31 * M44) - (M34 * M41); + float temp5 = (M31 * M43) - (M33 * M41); + float temp6 = (M31 * M42) - (M32 * M41); + + return ((((M11 * (((M22 * temp1) - (M23 * temp2)) + (M24 * temp3))) - (M12 * (((M21 * temp1) - + (M23 * temp4)) + (M24 * temp5)))) + (M13 * (((M21 * temp2) - (M22 * temp4)) + (M24 * temp6)))) - + (M14 * (((M21 * temp3) - (M22 * temp5)) + (M23 * temp6)))); + } + + /// + /// Inverts the matrix. + /// + public void Invert() + { + Invert(ref this, out this); + } + + /// + /// Transposes the matrix. + /// + public void Transpose() + { + float temp; + + temp = M21; M21 = M12; M12 = temp; + temp = M31; M31 = M13; M13 = temp; + temp = M41; M41 = M14; M14 = temp; + + temp = M32; M32 = M23; M23 = temp; + temp = M42; M42 = M24; M24 = temp; + + temp = M43; M43 = M34; M34 = temp; + } + + /// + /// Orthogonalizes the specified matrix. + /// + /// + /// Orthogonalization is the process of making all rows orthogonal to each other. This + /// means that any given row in the matrix will be orthogonal to any other given row in the + /// matrix. + /// Because this method uses the modified Gram-Schmidt process, the resulting matrix + /// tends to be numerically unstable. The numeric stability decreases according to the rows + /// so that the first row is the most stable and the last row is the least stable. + /// This operation is performed on the rows of the matrix rather than the columns. + /// If you wish for this operation to be performed on the columns, first transpose the + /// input and than transpose the output. + /// + public void Orthogonalize() + { + Orthogonalize(ref this, out this); + } + + /// + /// Orthonormalizes the specified matrix. + /// + /// + /// Orthonormalization is the process of making all rows and columns orthogonal to each + /// other and making all rows and columns of unit length. This means that any given row will + /// be orthogonal to any other given row and any given column will be orthogonal to any other + /// given column. Any given row will not be orthogonal to any given column. Every row and every + /// column will be of unit length. + /// Because this method uses the modified Gram-Schmidt process, the resulting matrix + /// tends to be numerically unstable. The numeric stability decreases according to the rows + /// so that the first row is the most stable and the last row is the least stable. + /// This operation is performed on the rows of the matrix rather than the columns. + /// If you wish for this operation to be performed on the columns, first transpose the + /// input and than transpose the output. + /// + public void Orthonormalize() + { + Orthonormalize(ref this, out this); + } + + /// + /// Decomposes a matrix into an orthonormalized matrix Q and a right traingular matrix R. + /// + /// When the method completes, contains the orthonormalized matrix of the decomposition. + /// When the method completes, contains the right triangular matrix of the decomposition. + public void DecomposeQR(out Matrix Q, out Matrix R) + { + Matrix temp = this; + temp.Transpose(); + Orthonormalize(ref temp, out Q); + Q.Transpose(); + + R = new Matrix(); + R.M11 = Vector4.Dot(Q.Column1, Column1); + R.M12 = Vector4.Dot(Q.Column1, Column2); + R.M13 = Vector4.Dot(Q.Column1, Column3); + R.M14 = Vector4.Dot(Q.Column1, Column4); + + R.M22 = Vector4.Dot(Q.Column2, Column2); + R.M23 = Vector4.Dot(Q.Column2, Column3); + R.M24 = Vector4.Dot(Q.Column2, Column4); + + R.M33 = Vector4.Dot(Q.Column3, Column3); + R.M34 = Vector4.Dot(Q.Column3, Column4); + + R.M44 = Vector4.Dot(Q.Column4, Column4); + } + + /// + /// Decomposes a matrix into a lower triangular matrix L and an orthonormalized matrix Q. + /// + /// When the method completes, contains the lower triangular matrix of the decomposition. + /// When the method completes, contains the orthonormalized matrix of the decomposition. + public void DecomposeLQ(out Matrix L, out Matrix Q) + { + Orthonormalize(ref this, out Q); + + L = new Matrix(); + L.M11 = Vector4.Dot(Q.Row1, Row1); + + L.M21 = Vector4.Dot(Q.Row1, Row2); + L.M22 = Vector4.Dot(Q.Row2, Row2); + + L.M31 = Vector4.Dot(Q.Row1, Row3); + L.M32 = Vector4.Dot(Q.Row2, Row3); + L.M33 = Vector4.Dot(Q.Row3, Row3); + + L.M41 = Vector4.Dot(Q.Row1, Row4); + L.M42 = Vector4.Dot(Q.Row2, Row4); + L.M43 = Vector4.Dot(Q.Row3, Row4); + L.M44 = Vector4.Dot(Q.Row4, Row4); + } + + /// + /// Decomposes a rotation matrix with the specified yaw, pitch, roll + /// + /// The yaw. + /// The pitch. + /// The roll. + public void Decompose(out float yaw, out float pitch, out float roll) + { + pitch = (float)Math.Asin(-M32); + // Hardcoded constant - burn him, he's a witch + // double threshold = 0.001; + double test = Math.Cos(pitch); + if (test > MathUtil.ZeroTolerance) + { + roll = (float)Math.Atan2(M12, M22); + yaw = (float)Math.Atan2(M31, M33); + } + else + { + roll = (float)Math.Atan2(-M21, M11); + yaw = 0.0f; + } + } + + /// + /// Decomposes a rotation matrix with the specified X, Y and Z euler angles. + /// Matrix.RotationX(rotation.X) * Matrix.RotationY(rotation.Y) * Matrix.RotationZ(rotation.Z) should represent the same rotation. + /// + /// The vector containing the 3 rotations angles to be applied in order. + public void DecomposeXYZ(out Vector3 rotation) + { + rotation.Y = (float)Math.Asin(-M13); + double test = Math.Cos(rotation.Y); + if (test > 1e-6f) + { + rotation.Z = (float)Math.Atan2(M12, M11); + rotation.X = (float)Math.Atan2(M23, M33); + } + else + { + rotation.Z = (float)Math.Atan2(-M21, M31); + rotation.X = 0.0f; + } + } + + /// + /// Decomposes a matrix into a scale, rotation, and translation. + /// + /// When the method completes, contains the scaling component of the decomposed matrix. + /// When the method completes, contains the translation component of the decomposed matrix. + /// true if a rotation exist for this matrix, false otherwise. + /// This method is designed to decompose an SRT transformation matrix only. + public bool Decompose(out Vector3 scale, out Vector3 translation) + { + //Source: Unknown + //References: http://www.gamedev.net/community/forums/topic.asp?topic_id=441695 + + //Get the translation. + translation.X = this.M41; + translation.Y = this.M42; + translation.Z = this.M43; + + //Scaling is the length of the rows. + scale.X = (float)Math.Sqrt((M11 * M11) + (M12 * M12) + (M13 * M13)); + scale.Y = (float)Math.Sqrt((M21 * M21) + (M22 * M22) + (M23 * M23)); + scale.Z = (float)Math.Sqrt((M31 * M31) + (M32 * M32) + (M33 * M33)); + + //If any of the scaling factors are zero, than the rotation matrix can not exist. + if (Math.Abs(scale.X) < MathUtil.ZeroTolerance || + Math.Abs(scale.Y) < MathUtil.ZeroTolerance || + Math.Abs(scale.Z) < MathUtil.ZeroTolerance) + { + return false; + } + + return true; + } + + /// + /// Decomposes a matrix into a scale, rotation, and translation. + /// + /// When the method completes, contains the scaling component of the decomposed matrix. + /// When the method completes, contains the rtoation component of the decomposed matrix. + /// When the method completes, contains the translation component of the decomposed matrix. + /// + /// This method is designed to decompose an SRT transformation matrix only. + /// + public bool Decompose(out Vector3 scale, out Quaternion rotation, out Vector3 translation) + { + Matrix rotationMatrix; + Decompose(out scale, out rotationMatrix, out translation); + Quaternion.RotationMatrix(ref rotationMatrix, out rotation); + return true; + } + + /// + /// Decomposes a matrix into a scale, rotation, and translation. + /// + /// When the method completes, contains the scaling component of the decomposed matrix. + /// When the method completes, contains the rtoation component of the decomposed matrix. + /// When the method completes, contains the translation component of the decomposed matrix. + /// + /// This method is designed to decompose an SRT transformation matrix only. + /// + public bool Decompose(out Vector3 scale, out Matrix rotation, out Vector3 translation) + { + //Source: Unknown + //References: http://www.gamedev.net/community/forums/topic.asp?topic_id=441695 + + //Get the translation. + translation.X = this.M41; + translation.Y = this.M42; + translation.Z = this.M43; + + //Scaling is the length of the rows. + scale.X = (float)Math.Sqrt((M11 * M11) + (M12 * M12) + (M13 * M13)); + scale.Y = (float)Math.Sqrt((M21 * M21) + (M22 * M22) + (M23 * M23)); + scale.Z = (float)Math.Sqrt((M31 * M31) + (M32 * M32) + (M33 * M33)); + + //If any of the scaling factors are zero, than the rotation matrix can not exist. + if (Math.Abs(scale.X) < MathUtil.ZeroTolerance || + Math.Abs(scale.Y) < MathUtil.ZeroTolerance || + Math.Abs(scale.Z) < MathUtil.ZeroTolerance) + { + rotation = Matrix.Identity; + return false; + } + + // Calculate an perfect orthonormal matrix (no reflections) + var at = new Vector3(M31 / scale.Z, M32 / scale.Z, M33 / scale.Z); + var up = Vector3.Cross(at, new Vector3(M11 / scale.X, M12 / scale.X, M13 / scale.X)); + var right = Vector3.Cross(up, at); + + rotation = Identity; + rotation.Right = right; + rotation.Up = up; + rotation.Backward = at; + + // In case of reflexions + scale.X = Vector3.Dot(right, Right) > 0.0f ? scale.X : -scale.X; + scale.Y = Vector3.Dot(up, Up) > 0.0f ? scale.Y : -scale.Y; + scale.Z = Vector3.Dot(at, Backward) > 0.0f ? scale.Z : -scale.Z; + + return true; + } + + /// + /// Exchanges two rows in the matrix. + /// + /// The first row to exchange. This is an index of the row starting at zero. + /// The second row to exchange. This is an index of the row starting at zero. + public void ExchangeRows(int firstRow, int secondRow) + { + if (firstRow < 0) + throw new ArgumentOutOfRangeException("firstRow", "The parameter firstRow must be greater than or equal to zero."); + if (firstRow > 3) + throw new ArgumentOutOfRangeException("firstRow", "The parameter firstRow must be less than or equal to three."); + if (secondRow < 0) + throw new ArgumentOutOfRangeException("secondRow", "The parameter secondRow must be greater than or equal to zero."); + if (secondRow > 3) + throw new ArgumentOutOfRangeException("secondRow", "The parameter secondRow must be less than or equal to three."); + + if (firstRow == secondRow) + return; + + float temp0 = this[secondRow, 0]; + float temp1 = this[secondRow, 1]; + float temp2 = this[secondRow, 2]; + float temp3 = this[secondRow, 3]; + + this[secondRow, 0] = this[firstRow, 0]; + this[secondRow, 1] = this[firstRow, 1]; + this[secondRow, 2] = this[firstRow, 2]; + this[secondRow, 3] = this[firstRow, 3]; + + this[firstRow, 0] = temp0; + this[firstRow, 1] = temp1; + this[firstRow, 2] = temp2; + this[firstRow, 3] = temp3; + } + + /// + /// Exchange columns. + /// + /// The first column to exchange. + /// The second column to exchange. + public void ExchangeColumns(int firstColumn, int secondColumn) + { + if (firstColumn < 0) + throw new ArgumentOutOfRangeException("firstColumn", "The parameter firstColumn must be greater than or equal to zero."); + if (firstColumn > 3) + throw new ArgumentOutOfRangeException("firstColumn", "The parameter firstColumn must be less than or equal to three."); + if (secondColumn < 0) + throw new ArgumentOutOfRangeException("secondColumn", "The parameter secondColumn must be greater than or equal to zero."); + if (secondColumn > 3) + throw new ArgumentOutOfRangeException("secondColumn", "The parameter secondColumn must be less than or equal to three."); + + if (firstColumn == secondColumn) + return; + + float temp0 = this[0, secondColumn]; + float temp1 = this[1, secondColumn]; + float temp2 = this[2, secondColumn]; + float temp3 = this[3, secondColumn]; + + this[0, secondColumn] = this[0, firstColumn]; + this[1, secondColumn] = this[1, firstColumn]; + this[2, secondColumn] = this[2, firstColumn]; + this[3, secondColumn] = this[3, firstColumn]; + + this[0, firstColumn] = temp0; + this[1, firstColumn] = temp1; + this[2, firstColumn] = temp2; + this[3, firstColumn] = temp3; + } + + /// + /// Creates an array containing the elements of the matrix. + /// + /// A sixteen-element array containing the components of the matrix. + public float[] ToArray() + { + return new[] { M11, M12, M13, M14, M21, M22, M23, M24, M31, M32, M33, M34, M41, M42, M43, M44 }; + } + + /// + /// Determines the sum of two matrices. + /// + /// The first matrix to add. + /// The second matrix to add. + /// When the method completes, contains the sum of the two matrices. + public static void Add(ref Matrix left, ref Matrix right, out Matrix result) + { + result.M11 = left.M11 + right.M11; + result.M21 = left.M21 + right.M21; + result.M31 = left.M31 + right.M31; + result.M41 = left.M41 + right.M41; + result.M12 = left.M12 + right.M12; + result.M22 = left.M22 + right.M22; + result.M32 = left.M32 + right.M32; + result.M42 = left.M42 + right.M42; + result.M13 = left.M13 + right.M13; + result.M23 = left.M23 + right.M23; + result.M33 = left.M33 + right.M33; + result.M43 = left.M43 + right.M43; + result.M14 = left.M14 + right.M14; + result.M24 = left.M24 + right.M24; + result.M34 = left.M34 + right.M34; + result.M44 = left.M44 + right.M44; + } + + /// + /// Determines the sum of two matrices. + /// + /// The first matrix to add. + /// The second matrix to add. + /// The sum of the two matrices. + public static Matrix Add(Matrix left, Matrix right) + { + Matrix result; + Add(ref left, ref right, out result); + return result; + } + + /// + /// Determines the difference between two matrices. + /// + /// The first matrix to subtract. + /// The second matrix to subtract. + /// When the method completes, contains the difference between the two matrices. + public static void Subtract(ref Matrix left, ref Matrix right, out Matrix result) + { + result.M11 = left.M11 - right.M11; + result.M21 = left.M21 - right.M21; + result.M31 = left.M31 - right.M31; + result.M41 = left.M41 - right.M41; + result.M12 = left.M12 - right.M12; + result.M22 = left.M22 - right.M22; + result.M32 = left.M32 - right.M32; + result.M42 = left.M42 - right.M42; + result.M13 = left.M13 - right.M13; + result.M23 = left.M23 - right.M23; + result.M33 = left.M33 - right.M33; + result.M43 = left.M43 - right.M43; + result.M14 = left.M14 - right.M14; + result.M24 = left.M24 - right.M24; + result.M34 = left.M34 - right.M34; + result.M44 = left.M44 - right.M44; + } + + /// + /// Determines the difference between two matrices. + /// + /// The first matrix to subtract. + /// The second matrix to subtract. + /// The difference between the two matrices. + public static Matrix Subtract(Matrix left, Matrix right) + { + Matrix result; + Subtract(ref left, ref right, out result); + return result; + } + + /// + /// Scales a matrix by the given value. + /// + /// The matrix to scale. + /// The amount by which to scale. + /// When the method completes, contains the scaled matrix. + public static void Multiply(ref Matrix left, float right, out Matrix result) + { + result.M11 = left.M11 * right; + result.M21 = left.M21 * right; + result.M31 = left.M31 * right; + result.M41 = left.M41 * right; + result.M12 = left.M12 * right; + result.M22 = left.M22 * right; + result.M32 = left.M32 * right; + result.M42 = left.M42 * right; + result.M13 = left.M13 * right; + result.M23 = left.M23 * right; + result.M33 = left.M33 * right; + result.M43 = left.M43 * right; + result.M14 = left.M14 * right; + result.M24 = left.M24 * right; + result.M34 = left.M34 * right; + result.M44 = left.M44 * right; + } + + /// + /// Scales a matrix by the given value. + /// + /// The matrix to scale. + /// The amount by which to scale. + /// The scaled matrix. + public static Matrix Multiply(Matrix left, float right) + { + Matrix result; + Multiply(ref left, right, out result); + return result; + } + + /// + /// Determines the product of two matrices. + /// + /// The first matrix to multiply. + /// The second matrix to multiply. + /// The product of the two matrices. + public static void MultiplyTo(ref Matrix left, ref Matrix right, out Matrix result) + { + result.M11 = (left.M11 * right.M11) + (left.M12 * right.M21) + (left.M13 * right.M31) + (left.M14 * right.M41); + result.M21 = (left.M21 * right.M11) + (left.M22 * right.M21) + (left.M23 * right.M31) + (left.M24 * right.M41); + result.M31 = (left.M31 * right.M11) + (left.M32 * right.M21) + (left.M33 * right.M31) + (left.M34 * right.M41); + result.M41 = (left.M41 * right.M11) + (left.M42 * right.M21) + (left.M43 * right.M31) + (left.M44 * right.M41); + result.M12 = (left.M11 * right.M12) + (left.M12 * right.M22) + (left.M13 * right.M32) + (left.M14 * right.M42); + result.M22 = (left.M21 * right.M12) + (left.M22 * right.M22) + (left.M23 * right.M32) + (left.M24 * right.M42); + result.M32 = (left.M31 * right.M12) + (left.M32 * right.M22) + (left.M33 * right.M32) + (left.M34 * right.M42); + result.M42 = (left.M41 * right.M12) + (left.M42 * right.M22) + (left.M43 * right.M32) + (left.M44 * right.M42); + result.M13 = (left.M11 * right.M13) + (left.M12 * right.M23) + (left.M13 * right.M33) + (left.M14 * right.M43); + result.M23 = (left.M21 * right.M13) + (left.M22 * right.M23) + (left.M23 * right.M33) + (left.M24 * right.M43); + result.M33 = (left.M31 * right.M13) + (left.M32 * right.M23) + (left.M33 * right.M33) + (left.M34 * right.M43); + result.M43 = (left.M41 * right.M13) + (left.M42 * right.M23) + (left.M43 * right.M33) + (left.M44 * right.M43); + result.M14 = (left.M11 * right.M14) + (left.M12 * right.M24) + (left.M13 * right.M34) + (left.M14 * right.M44); + result.M24 = (left.M21 * right.M14) + (left.M22 * right.M24) + (left.M23 * right.M34) + (left.M24 * right.M44); + result.M34 = (left.M31 * right.M14) + (left.M32 * right.M24) + (left.M33 * right.M34) + (left.M34 * right.M44); + result.M44 = (left.M41 * right.M14) + (left.M42 * right.M24) + (left.M43 * right.M34) + (left.M44 * right.M44); + } + + /// + /// Determines the product of two matrices. + /// + /// The first matrix to multiply. + /// The second matrix to multiply. + /// The product of the two matrices. + public static void Multiply(ref Matrix left, ref Matrix right, out Matrix result) + { + result.M11 = (left.M11 * right.M11) + (left.M12 * right.M21) + (left.M13 * right.M31) + (left.M14 * right.M41); + result.M21 = (left.M21 * right.M11) + (left.M22 * right.M21) + (left.M23 * right.M31) + (left.M24 * right.M41); + result.M31 = (left.M31 * right.M11) + (left.M32 * right.M21) + (left.M33 * right.M31) + (left.M34 * right.M41); + result.M41 = (left.M41 * right.M11) + (left.M42 * right.M21) + (left.M43 * right.M31) + (left.M44 * right.M41); + result.M12 = (left.M11 * right.M12) + (left.M12 * right.M22) + (left.M13 * right.M32) + (left.M14 * right.M42); + result.M22 = (left.M21 * right.M12) + (left.M22 * right.M22) + (left.M23 * right.M32) + (left.M24 * right.M42); + result.M32 = (left.M31 * right.M12) + (left.M32 * right.M22) + (left.M33 * right.M32) + (left.M34 * right.M42); + result.M42 = (left.M41 * right.M12) + (left.M42 * right.M22) + (left.M43 * right.M32) + (left.M44 * right.M42); + result.M13 = (left.M11 * right.M13) + (left.M12 * right.M23) + (left.M13 * right.M33) + (left.M14 * right.M43); + result.M23 = (left.M21 * right.M13) + (left.M22 * right.M23) + (left.M23 * right.M33) + (left.M24 * right.M43); + result.M33 = (left.M31 * right.M13) + (left.M32 * right.M23) + (left.M33 * right.M33) + (left.M34 * right.M43); + result.M43 = (left.M41 * right.M13) + (left.M42 * right.M23) + (left.M43 * right.M33) + (left.M44 * right.M43); + result.M14 = (left.M11 * right.M14) + (left.M12 * right.M24) + (left.M13 * right.M34) + (left.M14 * right.M44); + result.M24 = (left.M21 * right.M14) + (left.M22 * right.M24) + (left.M23 * right.M34) + (left.M24 * right.M44); + result.M34 = (left.M31 * right.M14) + (left.M32 * right.M24) + (left.M33 * right.M34) + (left.M34 * right.M44); + result.M44 = (left.M41 * right.M14) + (left.M42 * right.M24) + (left.M43 * right.M34) + (left.M44 * right.M44); + } + + /// + /// Determines the product of two matrices. + /// + /// The first matrix to multiply. + /// The second matrix to multiply. + /// The product of the two matrices. + public static void MultiplyRef(ref Matrix left, ref Matrix right, ref Matrix result) + { + result.M11 = (left.M11 * right.M11) + (left.M12 * right.M21) + (left.M13 * right.M31) + (left.M14 * right.M41); + result.M21 = (left.M21 * right.M11) + (left.M22 * right.M21) + (left.M23 * right.M31) + (left.M24 * right.M41); + result.M31 = (left.M31 * right.M11) + (left.M32 * right.M21) + (left.M33 * right.M31) + (left.M34 * right.M41); + result.M41 = (left.M41 * right.M11) + (left.M42 * right.M21) + (left.M43 * right.M31) + (left.M44 * right.M41); + result.M12 = (left.M11 * right.M12) + (left.M12 * right.M22) + (left.M13 * right.M32) + (left.M14 * right.M42); + result.M22 = (left.M21 * right.M12) + (left.M22 * right.M22) + (left.M23 * right.M32) + (left.M24 * right.M42); + result.M32 = (left.M31 * right.M12) + (left.M32 * right.M22) + (left.M33 * right.M32) + (left.M34 * right.M42); + result.M42 = (left.M41 * right.M12) + (left.M42 * right.M22) + (left.M43 * right.M32) + (left.M44 * right.M42); + result.M13 = (left.M11 * right.M13) + (left.M12 * right.M23) + (left.M13 * right.M33) + (left.M14 * right.M43); + result.M23 = (left.M21 * right.M13) + (left.M22 * right.M23) + (left.M23 * right.M33) + (left.M24 * right.M43); + result.M33 = (left.M31 * right.M13) + (left.M32 * right.M23) + (left.M33 * right.M33) + (left.M34 * right.M43); + result.M43 = (left.M41 * right.M13) + (left.M42 * right.M23) + (left.M43 * right.M33) + (left.M44 * right.M43); + result.M14 = (left.M11 * right.M14) + (left.M12 * right.M24) + (left.M13 * right.M34) + (left.M14 * right.M44); + result.M24 = (left.M21 * right.M14) + (left.M22 * right.M24) + (left.M23 * right.M34) + (left.M24 * right.M44); + result.M34 = (left.M31 * right.M14) + (left.M32 * right.M24) + (left.M33 * right.M34) + (left.M34 * right.M44); + result.M44 = (left.M41 * right.M14) + (left.M42 * right.M24) + (left.M43 * right.M34) + (left.M44 * right.M44); + } + + /// + /// Determines the product of two matrices. + /// + /// The first matrix to multiply. + /// The second matrix to multiply. + /// The product of the two matrices. + public static Matrix Multiply(Matrix left, Matrix right) + { + Matrix result; + Multiply(ref left, ref right, out result); + return result; + } + + /// + /// Scales a matrix by the given value. + /// + /// The matrix to scale. + /// The amount by which to scale. + /// When the method completes, contains the scaled matrix. + public static void Divide(ref Matrix left, float right, out Matrix result) + { + float inv = 1.0f / right; + + result.M11 = left.M11 * inv; + result.M21 = left.M21 * inv; + result.M31 = left.M31 * inv; + result.M41 = left.M41 * inv; + result.M12 = left.M12 * inv; + result.M22 = left.M22 * inv; + result.M32 = left.M32 * inv; + result.M42 = left.M42 * inv; + result.M13 = left.M13 * inv; + result.M23 = left.M23 * inv; + result.M33 = left.M33 * inv; + result.M43 = left.M43 * inv; + result.M14 = left.M14 * inv; + result.M24 = left.M24 * inv; + result.M34 = left.M34 * inv; + result.M44 = left.M44 * inv; + } + + /// + /// Scales a matrix by the given value. + /// + /// The matrix to scale. + /// The amount by which to scale. + /// The scaled matrix. + public static Matrix Divide(Matrix left, float right) + { + Matrix result; + Divide(ref left, right, out result); + return result; + } + + /// + /// Determines the quotient of two matrices. + /// + /// The first matrix to divide. + /// The second matrix to divide. + /// When the method completes, contains the quotient of the two matrices. + public static void Divide(ref Matrix left, ref Matrix right, out Matrix result) + { + result.M11 = left.M11 / right.M11; + result.M21 = left.M21 / right.M21; + result.M31 = left.M31 / right.M31; + result.M41 = left.M41 / right.M41; + result.M12 = left.M12 / right.M12; + result.M22 = left.M22 / right.M22; + result.M32 = left.M32 / right.M32; + result.M42 = left.M42 / right.M42; + result.M13 = left.M13 / right.M13; + result.M23 = left.M23 / right.M23; + result.M33 = left.M33 / right.M33; + result.M43 = left.M43 / right.M43; + result.M14 = left.M14 / right.M14; + result.M24 = left.M24 / right.M24; + result.M34 = left.M34 / right.M34; + result.M44 = left.M44 / right.M44; + } + + /// + /// Determines the quotient of two matrices. + /// + /// The first matrix to divide. + /// The second matrix to divide. + /// The quotient of the two matrices. + public static Matrix Divide(Matrix left, Matrix right) + { + Matrix result; + Divide(ref left, ref right, out result); + return result; + } + + /// + /// Performs the exponential operation on a matrix. + /// + /// The matrix to perform the operation on. + /// The exponent to raise the matrix to. + /// When the method completes, contains the exponential matrix. + /// Thrown when the is negative. + public static void Exponent(ref Matrix value, int exponent, out Matrix result) + { + //Source: http://rosettacode.org + //Refrence: http://rosettacode.org/wiki/Matrix-exponentiation_operator + + if (exponent < 0) + throw new ArgumentOutOfRangeException("exponent", "The exponent can not be negative."); + + if (exponent == 0) + { + result = Matrix.Identity; + return; + } + + if (exponent == 1) + { + result = value; + return; + } + + Matrix identity = Matrix.Identity; + Matrix temp = value; + + while (true) + { + if ((exponent & 1) != 0) + identity = identity * temp; + + exponent /= 2; + + if (exponent > 0) + temp *= temp; + else + break; + } + + result = identity; + } + + /// + /// Performs the exponential operation on a matrix. + /// + /// The matrix to perform the operation on. + /// The exponent to raise the matrix to. + /// The exponential matrix. + /// Thrown when the is negative. + public static Matrix Exponent(Matrix value, int exponent) + { + Matrix result; + Exponent(ref value, exponent, out result); + return result; + } + + /// + /// Negates a matrix. + /// + /// The matrix to be negated. + /// When the method completes, contains the negated matrix. + public static void Negate(ref Matrix value, out Matrix result) + { + result.M11 = -value.M11; + result.M21 = -value.M21; + result.M31 = -value.M31; + result.M41 = -value.M41; + result.M12 = -value.M12; + result.M22 = -value.M22; + result.M32 = -value.M32; + result.M42 = -value.M42; + result.M13 = -value.M13; + result.M23 = -value.M23; + result.M33 = -value.M33; + result.M43 = -value.M43; + result.M14 = -value.M14; + result.M24 = -value.M24; + result.M34 = -value.M34; + result.M44 = -value.M44; + } + + /// + /// Negates a matrix. + /// + /// The matrix to be negated. + /// The negated matrix. + public static Matrix Negate(Matrix value) + { + Matrix result; + Negate(ref value, out result); + return result; + } + + /// + /// Performs a linear interpolation between two matricies. + /// + /// Start matrix. + /// End matrix. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two matricies. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Matrix start, ref Matrix end, float amount, out Matrix result) + { + result.M11 = start.M11 + ((end.M11 - start.M11) * amount); + result.M21 = start.M21 + ((end.M21 - start.M21) * amount); + result.M31 = start.M31 + ((end.M31 - start.M31) * amount); + result.M41 = start.M41 + ((end.M41 - start.M41) * amount); + result.M12 = start.M12 + ((end.M12 - start.M12) * amount); + result.M22 = start.M22 + ((end.M22 - start.M22) * amount); + result.M32 = start.M32 + ((end.M32 - start.M32) * amount); + result.M42 = start.M42 + ((end.M42 - start.M42) * amount); + result.M13 = start.M13 + ((end.M13 - start.M13) * amount); + result.M23 = start.M23 + ((end.M23 - start.M23) * amount); + result.M33 = start.M33 + ((end.M33 - start.M33) * amount); + result.M43 = start.M43 + ((end.M43 - start.M43) * amount); + result.M14 = start.M14 + ((end.M14 - start.M14) * amount); + result.M24 = start.M24 + ((end.M24 - start.M24) * amount); + result.M34 = start.M34 + ((end.M34 - start.M34) * amount); + result.M44 = start.M44 + ((end.M44 - start.M44) * amount); + } + + /// + /// Performs a linear interpolation between two matricies. + /// + /// Start matrix. + /// End matrix. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two matrices. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Matrix Lerp(Matrix start, Matrix end, float amount) + { + Matrix result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two matricies. + /// + /// Start matrix. + /// End matrix. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two matrices. + public static void SmoothStep(ref Matrix start, ref Matrix end, float amount, out Matrix result) + { + amount = (amount > 1.0f) ? 1.0f : ((amount < 0.0f) ? 0.0f : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.M11 = start.M11 + ((end.M11 - start.M11) * amount); + result.M21 = start.M21 + ((end.M21 - start.M21) * amount); + result.M31 = start.M31 + ((end.M31 - start.M31) * amount); + result.M41 = start.M41 + ((end.M41 - start.M41) * amount); + result.M12 = start.M12 + ((end.M12 - start.M12) * amount); + result.M22 = start.M22 + ((end.M22 - start.M22) * amount); + result.M32 = start.M32 + ((end.M32 - start.M32) * amount); + result.M42 = start.M42 + ((end.M42 - start.M42) * amount); + result.M13 = start.M13 + ((end.M13 - start.M13) * amount); + result.M23 = start.M23 + ((end.M23 - start.M23) * amount); + result.M33 = start.M33 + ((end.M33 - start.M33) * amount); + result.M43 = start.M43 + ((end.M43 - start.M43) * amount); + result.M14 = start.M14 + ((end.M14 - start.M14) * amount); + result.M24 = start.M24 + ((end.M24 - start.M24) * amount); + result.M34 = start.M34 + ((end.M34 - start.M34) * amount); + result.M44 = start.M44 + ((end.M44 - start.M44) * amount); + } + + /// + /// Performs a cubic interpolation between two matrices. + /// + /// Start matrix. + /// End matrix. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two matrices. + public static Matrix SmoothStep(Matrix start, Matrix end, float amount) + { + Matrix result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Calculates the transpose of the specified matrix. + /// + /// The matrix whose transpose is to be calculated. + /// When the method completes, contains the transpose of the specified matrix. + public static void Transpose(ref Matrix value, out Matrix result) + { + result = new Matrix( + value.M11, + value.M21, + value.M31, + value.M41, + value.M12, + value.M22, + value.M32, + value.M42, + value.M13, + value.M23, + value.M33, + value.M43, + value.M14, + value.M24, + value.M34, + value.M44); + } + + /// + /// Calculates the transpose of the specified matrix. + /// + /// The matrix whose transpose is to be calculated. + /// The transpose of the specified matrix. + public static Matrix Transpose(Matrix value) + { + value.Transpose(); + return value; + } + + /// + /// Calculates the inverse of the specified matrix. + /// + /// The matrix whose inverse is to be calculated. + /// When the method completes, contains the inverse of the specified matrix. + public static void Invert(ref Matrix value, out Matrix result) + { + float b0 = (value.M31 * value.M42) - (value.M32 * value.M41); + float b1 = (value.M31 * value.M43) - (value.M33 * value.M41); + float b2 = (value.M34 * value.M41) - (value.M31 * value.M44); + float b3 = (value.M32 * value.M43) - (value.M33 * value.M42); + float b4 = (value.M34 * value.M42) - (value.M32 * value.M44); + float b5 = (value.M33 * value.M44) - (value.M34 * value.M43); + + float d11 = value.M22 * b5 + value.M23 * b4 + value.M24 * b3; + float d12 = value.M21 * b5 + value.M23 * b2 + value.M24 * b1; + float d13 = value.M21 * -b4 + value.M22 * b2 + value.M24 * b0; + float d14 = value.M21 * b3 + value.M22 * -b1 + value.M23 * b0; + + float det = value.M11 * d11 - value.M12 * d12 + value.M13 * d13 - value.M14 * d14; + if (Math.Abs(det) == 0.0f) + { + result = Matrix.Zero; + return; + } + + det = 1f / det; + + float a0 = (value.M11 * value.M22) - (value.M12 * value.M21); + float a1 = (value.M11 * value.M23) - (value.M13 * value.M21); + float a2 = (value.M14 * value.M21) - (value.M11 * value.M24); + float a3 = (value.M12 * value.M23) - (value.M13 * value.M22); + float a4 = (value.M14 * value.M22) - (value.M12 * value.M24); + float a5 = (value.M13 * value.M24) - (value.M14 * value.M23); + + float d21 = value.M12 * b5 + value.M13 * b4 + value.M14 * b3; + float d22 = value.M11 * b5 + value.M13 * b2 + value.M14 * b1; + float d23 = value.M11 * -b4 + value.M12 * b2 + value.M14 * b0; + float d24 = value.M11 * b3 + value.M12 * -b1 + value.M13 * b0; + + float d31 = value.M42 * a5 + value.M43 * a4 + value.M44 * a3; + float d32 = value.M41 * a5 + value.M43 * a2 + value.M44 * a1; + float d33 = value.M41 * -a4 + value.M42 * a2 + value.M44 * a0; + float d34 = value.M41 * a3 + value.M42 * -a1 + value.M43 * a0; + + float d41 = value.M32 * a5 + value.M33 * a4 + value.M34 * a3; + float d42 = value.M31 * a5 + value.M33 * a2 + value.M34 * a1; + float d43 = value.M31 * -a4 + value.M32 * a2 + value.M34 * a0; + float d44 = value.M31 * a3 + value.M32 * -a1 + value.M33 * a0; + + result.M11 = +d11 * det; result.M12 = -d21 * det; result.M13 = +d31 * det; result.M14 = -d41 * det; + result.M21 = -d12 * det; result.M22 = +d22 * det; result.M23 = -d32 * det; result.M24 = +d42 * det; + result.M31 = +d13 * det; result.M32 = -d23 * det; result.M33 = +d33 * det; result.M34 = -d43 * det; + result.M41 = -d14 * det; result.M42 = +d24 * det; result.M43 = -d34 * det; result.M44 = +d44 * det; + } + + /// + /// Calculates the inverse of the specified matrix. + /// + /// The matrix whose inverse is to be calculated. + /// The inverse of the specified matrix. + public static Matrix Invert(Matrix value) + { + value.Invert(); + return value; + } + + /// + /// Orthogonalizes the specified matrix. + /// + /// The matrix to orthogonalize. + /// When the method completes, contains the orthogonalized matrix. + /// + /// Orthogonalization is the process of making all rows orthogonal to each other. This + /// means that any given row in the matrix will be orthogonal to any other given row in the + /// matrix. + /// Because this method uses the modified Gram-Schmidt process, the resulting matrix + /// tends to be numerically unstable. The numeric stability decreases according to the rows + /// so that the first row is the most stable and the last row is the least stable. + /// This operation is performed on the rows of the matrix rather than the columns. + /// If you wish for this operation to be performed on the columns, first transpose the + /// input and than transpose the output. + /// + public static void Orthogonalize(ref Matrix value, out Matrix result) + { + //Uses the modified Gram-Schmidt process. + //q1 = m1 + //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 + //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 + //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 + + //By separating the above algorithm into multiple lines, we actually increase accuracy. + result = value; + + result.Row2 = result.Row2 - (Vector4.Dot(result.Row1, result.Row2) / Vector4.Dot(result.Row1, result.Row1)) * result.Row1; + + result.Row3 = result.Row3 - (Vector4.Dot(result.Row1, result.Row3) / Vector4.Dot(result.Row1, result.Row1)) * result.Row1; + result.Row3 = result.Row3 - (Vector4.Dot(result.Row2, result.Row3) / Vector4.Dot(result.Row2, result.Row2)) * result.Row2; + + result.Row4 = result.Row4 - (Vector4.Dot(result.Row1, result.Row4) / Vector4.Dot(result.Row1, result.Row1)) * result.Row1; + result.Row4 = result.Row4 - (Vector4.Dot(result.Row2, result.Row4) / Vector4.Dot(result.Row2, result.Row2)) * result.Row2; + result.Row4 = result.Row4 - (Vector4.Dot(result.Row3, result.Row4) / Vector4.Dot(result.Row3, result.Row3)) * result.Row3; + } + + /// + /// Orthogonalizes the specified matrix. + /// + /// The matrix to orthogonalize. + /// The orthogonalized matrix. + /// + /// Orthogonalization is the process of making all rows orthogonal to each other. This + /// means that any given row in the matrix will be orthogonal to any other given row in the + /// matrix. + /// Because this method uses the modified Gram-Schmidt process, the resulting matrix + /// tends to be numerically unstable. The numeric stability decreases according to the rows + /// so that the first row is the most stable and the last row is the least stable. + /// This operation is performed on the rows of the matrix rather than the columns. + /// If you wish for this operation to be performed on the columns, first transpose the + /// input and than transpose the output. + /// + public static Matrix Orthogonalize(Matrix value) + { + Matrix result; + Orthogonalize(ref value, out result); + return result; + } + + /// + /// Orthonormalizes the specified matrix. + /// + /// The matrix to orthonormalize. + /// When the method completes, contains the orthonormalized matrix. + /// + /// Orthonormalization is the process of making all rows and columns orthogonal to each + /// other and making all rows and columns of unit length. This means that any given row will + /// be orthogonal to any other given row and any given column will be orthogonal to any other + /// given column. Any given row will not be orthogonal to any given column. Every row and every + /// column will be of unit length. + /// Because this method uses the modified Gram-Schmidt process, the resulting matrix + /// tends to be numerically unstable. The numeric stability decreases according to the rows + /// so that the first row is the most stable and the last row is the least stable. + /// This operation is performed on the rows of the matrix rather than the columns. + /// If you wish for this operation to be performed on the columns, first transpose the + /// input and than transpose the output. + /// + public static void Orthonormalize(ref Matrix value, out Matrix result) + { + //Uses the modified Gram-Schmidt process. + //Because we are making unit vectors, we can optimize the math for orthogonalization + //and simplify the projection operation to remove the division. + //q1 = m1 / |m1| + //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| + //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| + //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| + + //By separating the above algorithm into multiple lines, we actually increase accuracy. + result = value; + + result.Row1 = Vector4.Normalize(result.Row1); + + result.Row2 = result.Row2 - Vector4.Dot(result.Row1, result.Row2) * result.Row1; + result.Row2 = Vector4.Normalize(result.Row2); + + result.Row3 = result.Row3 - Vector4.Dot(result.Row1, result.Row3) * result.Row1; + result.Row3 = result.Row3 - Vector4.Dot(result.Row2, result.Row3) * result.Row2; + result.Row3 = Vector4.Normalize(result.Row3); + + result.Row4 = result.Row4 - Vector4.Dot(result.Row1, result.Row4) * result.Row1; + result.Row4 = result.Row4 - Vector4.Dot(result.Row2, result.Row4) * result.Row2; + result.Row4 = result.Row4 - Vector4.Dot(result.Row3, result.Row4) * result.Row3; + result.Row4 = Vector4.Normalize(result.Row4); + } + + /// + /// Orthonormalizes the specified matrix. + /// + /// The matrix to orthonormalize. + /// The orthonormalized matrix. + /// + /// Orthonormalization is the process of making all rows and columns orthogonal to each + /// other and making all rows and columns of unit length. This means that any given row will + /// be orthogonal to any other given row and any given column will be orthogonal to any other + /// given column. Any given row will not be orthogonal to any given column. Every row and every + /// column will be of unit length. + /// Because this method uses the modified Gram-Schmidt process, the resulting matrix + /// tends to be numerically unstable. The numeric stability decreases according to the rows + /// so that the first row is the most stable and the last row is the least stable. + /// This operation is performed on the rows of the matrix rather than the columns. + /// If you wish for this operation to be performed on the columns, first transpose the + /// input and than transpose the output. + /// + public static Matrix Orthonormalize(Matrix value) + { + Matrix result; + Orthonormalize(ref value, out result); + return result; + } + + /// + /// Brings the matrix into upper triangular form using elementry row operations. + /// + /// The matrix to put into upper triangular form. + /// When the method completes, contains the upper triangular matrix. + /// + /// If the matrix is not invertable (i.e. its determinant is zero) than the result of this + /// method may produce Single.Nan and Single.Inf values. When the matrix represents a system + /// of linear equations, than this often means that either no solution exists or an infinite + /// number of solutions exist. + /// + public static void UpperTriangularForm(ref Matrix value, out Matrix result) + { + //Adapted from the row echelon code. + result = value; + int lead = 0; + int rowcount = 4; + int columncount = 4; + + for (int r = 0; r < rowcount; ++r) + { + if (columncount <= lead) + return; + + int i = r; + + while (Math.Abs(result[i, lead]) < MathUtil.ZeroTolerance) + { + i++; + + if (i == rowcount) + { + i = r; + lead++; + + if (lead == columncount) + return; + } + } + + if (i != r) + { + result.ExchangeRows(i, r); + } + + float multiplier = 1f / result[r, lead]; + + for (; i < rowcount; ++i) + { + if (i != r) + { + result[i, 0] -= result[r, 0] * multiplier * result[i, lead]; + result[i, 1] -= result[r, 1] * multiplier * result[i, lead]; + result[i, 2] -= result[r, 2] * multiplier * result[i, lead]; + result[i, 3] -= result[r, 3] * multiplier * result[i, lead]; + } + } + + lead++; + } + } + + /// + /// Brings the matrix into upper triangular form using elementry row operations. + /// + /// The matrix to put into upper triangular form. + /// The upper triangular matrix. + /// + /// If the matrix is not invertable (i.e. its determinant is zero) than the result of this + /// method may produce Single.Nan and Single.Inf values. When the matrix represents a system + /// of linear equations, than this often means that either no solution exists or an infinite + /// number of solutions exist. + /// + public static Matrix UpperTriangularForm(Matrix value) + { + Matrix result; + UpperTriangularForm(ref value, out result); + return result; + } + + /// + /// Brings the matrix into lower triangular form using elementry row operations. + /// + /// The matrix to put into lower triangular form. + /// When the method completes, contains the lower triangular matrix. + /// + /// If the matrix is not invertable (i.e. its determinant is zero) than the result of this + /// method may produce Single.Nan and Single.Inf values. When the matrix represents a system + /// of linear equations, than this often means that either no solution exists or an infinite + /// number of solutions exist. + /// + public static void LowerTriangularForm(ref Matrix value, out Matrix result) + { + //Adapted from the row echelon code. + Matrix temp = value; + Matrix.Transpose(ref temp, out result); + + int lead = 0; + int rowcount = 4; + int columncount = 4; + + for (int r = 0; r < rowcount; ++r) + { + if (columncount <= lead) + return; + + int i = r; + + while (Math.Abs(result[i, lead]) < MathUtil.ZeroTolerance) + { + i++; + + if (i == rowcount) + { + i = r; + lead++; + + if (lead == columncount) + return; + } + } + + if (i != r) + { + result.ExchangeRows(i, r); + } + + float multiplier = 1f / result[r, lead]; + + for (; i < rowcount; ++i) + { + if (i != r) + { + result[i, 0] -= result[r, 0] * multiplier * result[i, lead]; + result[i, 1] -= result[r, 1] * multiplier * result[i, lead]; + result[i, 2] -= result[r, 2] * multiplier * result[i, lead]; + result[i, 3] -= result[r, 3] * multiplier * result[i, lead]; + } + } + + lead++; + } + + Matrix.Transpose(ref result, out result); + } + + /// + /// Brings the matrix into lower triangular form using elementry row operations. + /// + /// The matrix to put into lower triangular form. + /// The lower triangular matrix. + /// + /// If the matrix is not invertable (i.e. its determinant is zero) than the result of this + /// method may produce Single.Nan and Single.Inf values. When the matrix represents a system + /// of linear equations, than this often means that either no solution exists or an infinite + /// number of solutions exist. + /// + public static Matrix LowerTriangularForm(Matrix value) + { + Matrix result; + LowerTriangularForm(ref value, out result); + return result; + } + + /// + /// Brings the matrix into row echelon form using elementry row operations; + /// + /// The matrix to put into row echelon form. + /// When the method completes, contains the row echelon form of the matrix. + public static void RowEchelonForm(ref Matrix value, out Matrix result) + { + //Source: Wikipedia psuedo code + //Reference: http://en.wikipedia.org/wiki/Row_echelon_form#Pseudocode + + result = value; + int lead = 0; + int rowcount = 4; + int columncount = 4; + + for (int r = 0; r < rowcount; ++r) + { + if (columncount <= lead) + return; + + int i = r; + + while (Math.Abs(result[i, lead]) < MathUtil.ZeroTolerance) + { + i++; + + if (i == rowcount) + { + i = r; + lead++; + + if (lead == columncount) + return; + } + } + + if (i != r) + { + result.ExchangeRows(i, r); + } + + float multiplier = 1f / result[r, lead]; + result[r, 0] *= multiplier; + result[r, 1] *= multiplier; + result[r, 2] *= multiplier; + result[r, 3] *= multiplier; + + for (; i < rowcount; ++i) + { + if (i != r) + { + result[i, 0] -= result[r, 0] * result[i, lead]; + result[i, 1] -= result[r, 1] * result[i, lead]; + result[i, 2] -= result[r, 2] * result[i, lead]; + result[i, 3] -= result[r, 3] * result[i, lead]; + } + } + + lead++; + } + } + + /// + /// Brings the matrix into row echelon form using elementry row operations; + /// + /// The matrix to put into row echelon form. + /// When the method completes, contains the row echelon form of the matrix. + public static Matrix RowEchelonForm(Matrix value) + { + Matrix result; + RowEchelonForm(ref value, out result); + return result; + } + + /// + /// Brings the matrix into reduced row echelon form using elementry row operations. + /// + /// The matrix to put into reduced row echelon form. + /// The fifth column of the matrix. + /// When the method completes, contains the resultant matrix after the operation. + /// When the method completes, contains the resultant fifth column of the matrix. + /// + /// The fifth column is often called the agumented part of the matrix. This is because the fifth + /// column is really just an extension of the matrix so that there is a place to put all of the + /// non-zero components after the operation is complete. + /// Often times the resultant matrix will the identity matrix or a matrix similar to the identity + /// matrix. Sometimes, however, that is not possible and numbers other than zero and one may appear. + /// This method can be used to solve systems of linear equations. Upon completion of this method, + /// the will contain the solution for the system. It is up to the user + /// to analyze both the input and the result to determine if a solution really exists. + /// + public static void ReducedRowEchelonForm(ref Matrix value, ref Vector4 augment, out Matrix result, out Vector4 augmentResult) + { + //Source: http://rosettacode.org + //Reference: http://rosettacode.org/wiki/Reduced_row_echelon_form + + float[,] matrix = new float[4, 5]; + + matrix[0, 0] = value[0, 0]; + matrix[0, 1] = value[0, 1]; + matrix[0, 2] = value[0, 2]; + matrix[0, 3] = value[0, 3]; + matrix[0, 4] = augment[0]; + + matrix[1, 0] = value[1, 0]; + matrix[1, 1] = value[1, 1]; + matrix[1, 2] = value[1, 2]; + matrix[1, 3] = value[1, 3]; + matrix[1, 4] = augment[1]; + + matrix[2, 0] = value[2, 0]; + matrix[2, 1] = value[2, 1]; + matrix[2, 2] = value[2, 2]; + matrix[2, 3] = value[2, 3]; + matrix[2, 4] = augment[2]; + + matrix[3, 0] = value[3, 0]; + matrix[3, 1] = value[3, 1]; + matrix[3, 2] = value[3, 2]; + matrix[3, 3] = value[3, 3]; + matrix[3, 4] = augment[3]; + + int lead = 0; + int rowcount = 4; + int columncount = 5; + + for (int r = 0; r < rowcount; r++) + { + if (columncount <= lead) + break; + + int i = r; + + while (matrix[i, lead] == 0) + { + i++; + + if (i == rowcount) + { + i = r; + lead++; + + if (columncount == lead) + break; + } + } + + for (int j = 0; j < columncount; j++) + { + float temp = matrix[r, j]; + matrix[r, j] = matrix[i, j]; + matrix[i, j] = temp; + } + + float div = matrix[r, lead]; + + for (int j = 0; j < columncount; j++) + { + matrix[r, j] /= div; + } + + for (int j = 0; j < rowcount; j++) + { + if (j != r) + { + float sub = matrix[j, lead]; + for (int k = 0; k < columncount; k++) matrix[j, k] -= (sub * matrix[r, k]); + } + } + + lead++; + } + + result.M11 = matrix[0, 0]; + result.M12 = matrix[0, 1]; + result.M13 = matrix[0, 2]; + result.M14 = matrix[0, 3]; + + result.M21 = matrix[1, 0]; + result.M22 = matrix[1, 1]; + result.M23 = matrix[1, 2]; + result.M24 = matrix[1, 3]; + + result.M31 = matrix[2, 0]; + result.M32 = matrix[2, 1]; + result.M33 = matrix[2, 2]; + result.M34 = matrix[2, 3]; + + result.M41 = matrix[3, 0]; + result.M42 = matrix[3, 1]; + result.M43 = matrix[3, 2]; + result.M44 = matrix[3, 3]; + + augmentResult.X = matrix[0, 4]; + augmentResult.Y = matrix[1, 4]; + augmentResult.Z = matrix[2, 4]; + augmentResult.W = matrix[3, 4]; + } + + /// + /// Creates a spherical billboard that rotates around a specified object position. + /// + /// The position of the object around which the billboard will rotate. + /// The position of the camera. + /// The up vector of the camera. + /// The forward vector of the camera. + /// When the method completes, contains the created billboard matrix. + public static void Billboard(ref Vector3 objectPosition, ref Vector3 cameraPosition, ref Vector3 cameraUpVector, ref Vector3 cameraForwardVector, out Matrix result) + { + Vector3 crossed; + Vector3 final; + Vector3 difference = objectPosition - cameraPosition; + + float lengthSq = difference.LengthSquared(); + if (lengthSq < MathUtil.ZeroTolerance) + difference = -cameraForwardVector; + else + difference *= (float)(1.0 / Math.Sqrt(lengthSq)); + + Vector3.Cross(ref cameraUpVector, ref difference, out crossed); + crossed.Normalize(); + Vector3.Cross(ref difference, ref crossed, out final); + + result.M11 = crossed.X; + result.M12 = crossed.Y; + result.M13 = crossed.Z; + result.M14 = 0.0f; + result.M21 = final.X; + result.M22 = final.Y; + result.M23 = final.Z; + result.M24 = 0.0f; + result.M31 = difference.X; + result.M32 = difference.Y; + result.M33 = difference.Z; + result.M34 = 0.0f; + result.M41 = objectPosition.X; + result.M42 = objectPosition.Y; + result.M43 = objectPosition.Z; + result.M44 = 1.0f; + } + + /// + /// Creates a spherical billboard that rotates around a specified object position. + /// + /// The position of the object around which the billboard will rotate. + /// The position of the camera. + /// The up vector of the camera. + /// The forward vector of the camera. + /// The created billboard matrix. + public static Matrix Billboard(Vector3 objectPosition, Vector3 cameraPosition, Vector3 cameraUpVector, Vector3 cameraForwardVector) + { + Matrix result; + Billboard(ref objectPosition, ref cameraPosition, ref cameraUpVector, ref cameraForwardVector, out result); + return result; + } + + /// + /// Creates a left-handed, look-at matrix. + /// + /// The position of the viewer's eye. + /// The camera look-at target. + /// The camera's up vector. + /// When the method completes, contains the created look-at matrix. + public static void LookAtLH(ref Vector3 eye, ref Vector3 target, ref Vector3 up, out Matrix result) + { + Vector3 xaxis, yaxis, zaxis; + Vector3.Subtract(ref target, ref eye, out zaxis); zaxis.Normalize(); + Vector3.Cross(ref up, ref zaxis, out xaxis); xaxis.Normalize(); + Vector3.Cross(ref zaxis, ref xaxis, out yaxis); + + result = Matrix.Identity; + result.M11 = xaxis.X; result.M21 = xaxis.Y; result.M31 = xaxis.Z; + result.M12 = yaxis.X; result.M22 = yaxis.Y; result.M32 = yaxis.Z; + result.M13 = zaxis.X; result.M23 = zaxis.Y; result.M33 = zaxis.Z; + + Vector3.Dot(ref xaxis, ref eye, out result.M41); + Vector3.Dot(ref yaxis, ref eye, out result.M42); + Vector3.Dot(ref zaxis, ref eye, out result.M43); + + result.M41 = -result.M41; + result.M42 = -result.M42; + result.M43 = -result.M43; + } + + /// + /// Creates a left-handed, look-at matrix. + /// + /// The position of the viewer's eye. + /// The camera look-at target. + /// The camera's up vector. + /// The created look-at matrix. + public static Matrix LookAtLH(Vector3 eye, Vector3 target, Vector3 up) + { + Matrix result; + LookAtLH(ref eye, ref target, ref up, out result); + return result; + } + + /// + /// Creates a right-handed, look-at matrix. + /// + /// The position of the viewer's eye. + /// The camera look-at target. + /// The camera's up vector. + /// When the method completes, contains the created look-at matrix. + public static void LookAtRH(ref Vector3 eye, ref Vector3 target, ref Vector3 up, out Matrix result) + { + Vector3 xaxis, yaxis, zaxis; + Vector3.Subtract(ref eye, ref target, out zaxis); zaxis.Normalize(); + Vector3.Cross(ref up, ref zaxis, out xaxis); xaxis.Normalize(); + Vector3.Cross(ref zaxis, ref xaxis, out yaxis); + + result = Matrix.Identity; + result.M11 = xaxis.X; result.M21 = xaxis.Y; result.M31 = xaxis.Z; + result.M12 = yaxis.X; result.M22 = yaxis.Y; result.M32 = yaxis.Z; + result.M13 = zaxis.X; result.M23 = zaxis.Y; result.M33 = zaxis.Z; + + Vector3.Dot(ref xaxis, ref eye, out result.M41); + Vector3.Dot(ref yaxis, ref eye, out result.M42); + Vector3.Dot(ref zaxis, ref eye, out result.M43); + + result.M41 = -result.M41; + result.M42 = -result.M42; + result.M43 = -result.M43; + } + + /// + /// Creates a right-handed, look-at matrix. + /// + /// The position of the viewer's eye. + /// The camera look-at target. + /// The camera's up vector. + /// The created look-at matrix. + public static Matrix LookAtRH(Vector3 eye, Vector3 target, Vector3 up) + { + Matrix result; + LookAtRH(ref eye, ref target, ref up, out result); + return result; + } + + /// + /// Creates a left-handed, orthographic projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void OrthoLH(float width, float height, float znear, float zfar, out Matrix result) + { + float halfWidth = width * 0.5f; + float halfHeight = height * 0.5f; + + OrthoOffCenterLH(-halfWidth, halfWidth, -halfHeight, halfHeight, znear, zfar, out result); + } + + /// + /// Creates a left-handed, orthographic projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix OrthoLH(float width, float height, float znear, float zfar) + { + Matrix result; + OrthoLH(width, height, znear, zfar, out result); + return result; + } + + /// + /// Creates a right-handed, orthographic projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void OrthoRH(float width, float height, float znear, float zfar, out Matrix result) + { + float halfWidth = width * 0.5f; + float halfHeight = height * 0.5f; + + OrthoOffCenterRH(-halfWidth, halfWidth, -halfHeight, halfHeight, znear, zfar, out result); + } + + /// + /// Creates a right-handed, orthographic projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix OrthoRH(float width, float height, float znear, float zfar) + { + Matrix result; + OrthoRH(width, height, znear, zfar, out result); + return result; + } + + /// + /// Creates a left-handed, customized orthographic projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void OrthoOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result) + { + float zRange = 1.0f / (zfar - znear); + + result = Matrix.Identity; + result.M11 = 2.0f / (right - left); + result.M22 = 2.0f / (top - bottom); + result.M33 = zRange; + result.M41 = (left + right) / (left - right); + result.M42 = (top + bottom) / (bottom - top); + result.M43 = -znear * zRange; + } + + /// + /// Creates a left-handed, customized orthographic projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix OrthoOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar) + { + Matrix result; + OrthoOffCenterLH(left, right, bottom, top, znear, zfar, out result); + return result; + } + + /// + /// Creates a right-handed, customized orthographic projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void OrthoOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result) + { + OrthoOffCenterLH(left, right, bottom, top, znear, zfar, out result); + result.M33 *= -1.0f; + } + + /// + /// Creates a right-handed, customized orthographic projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix OrthoOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar) + { + Matrix result; + OrthoOffCenterRH(left, right, bottom, top, znear, zfar, out result); + return result; + } + + /// + /// Creates a left-handed, perspective projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void PerspectiveLH(float width, float height, float znear, float zfar, out Matrix result) + { + float halfWidth = width * 0.5f; + float halfHeight = height * 0.5f; + + PerspectiveOffCenterLH(-halfWidth, halfWidth, -halfHeight, halfHeight, znear, zfar, out result); + } + + /// + /// Creates a left-handed, perspective projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix PerspectiveLH(float width, float height, float znear, float zfar) + { + Matrix result; + PerspectiveLH(width, height, znear, zfar, out result); + return result; + } + + /// + /// Creates a right-handed, perspective projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void PerspectiveRH(float width, float height, float znear, float zfar, out Matrix result) + { + float halfWidth = width * 0.5f; + float halfHeight = height * 0.5f; + + PerspectiveOffCenterRH(-halfWidth, halfWidth, -halfHeight, halfHeight, znear, zfar, out result); + } + + /// + /// Creates a right-handed, perspective projection matrix. + /// + /// Width of the viewing volume. + /// Height of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix PerspectiveRH(float width, float height, float znear, float zfar) + { + Matrix result; + PerspectiveRH(width, height, znear, zfar, out result); + return result; + } + + /// + /// Creates a left-handed, perspective projection matrix based on a field of view. + /// + /// Field of view in the y direction, in radians. + /// Aspect ratio, defined as view space width divided by height. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void PerspectiveFovLH(float fov, float aspect, float znear, float zfar, out Matrix result) + { + float yScale = (float)(1.0 / Math.Tan(fov * 0.5f)); + float xScale = yScale / aspect; + + float halfWidth = znear / xScale; + float halfHeight = znear / yScale; + + PerspectiveOffCenterLH(-halfWidth, halfWidth, -halfHeight, halfHeight, znear, zfar, out result); + } + + /// + /// Creates a left-handed, perspective projection matrix based on a field of view. + /// + /// Field of view in the y direction, in radians. + /// Aspect ratio, defined as view space width divided by height. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix PerspectiveFovLH(float fov, float aspect, float znear, float zfar) + { + Matrix result; + PerspectiveFovLH(fov, aspect, znear, zfar, out result); + return result; + } + + /// + /// Creates a right-handed, perspective projection matrix based on a field of view. + /// + /// Field of view in the y direction, in radians. + /// Aspect ratio, defined as view space width divided by height. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void PerspectiveFovRH(float fov, float aspect, float znear, float zfar, out Matrix result) + { + float yScale = (float)(1.0 / Math.Tan(fov * 0.5f)); + float xScale = yScale / aspect; + + float halfWidth = znear / xScale; + float halfHeight = znear / yScale; + + PerspectiveOffCenterRH(-halfWidth, halfWidth, -halfHeight, halfHeight, znear, zfar, out result); + } + + /// + /// Creates a right-handed, perspective projection matrix based on a field of view. + /// + /// Field of view in the y direction, in radians. + /// Aspect ratio, defined as view space width divided by height. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix PerspectiveFovRH(float fov, float aspect, float znear, float zfar) + { + Matrix result; + PerspectiveFovRH(fov, aspect, znear, zfar, out result); + return result; + } + + /// + /// Creates a left-handed, customized perspective projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void PerspectiveOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result) + { + float zRange = zfar / (zfar - znear); + + result = new Matrix(); + result.M11 = 2.0f * znear / (right - left); + result.M22 = 2.0f * znear / (top - bottom); + result.M31 = (left + right) / (left - right); + result.M32 = (top + bottom) / (bottom - top); + result.M33 = zRange; + result.M34 = 1.0f; + result.M43 = -znear * zRange; + } + + /// + /// Creates a left-handed, customized perspective projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix PerspectiveOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar) + { + Matrix result; + PerspectiveOffCenterLH(left, right, bottom, top, znear, zfar, out result); + return result; + } + + /// + /// Creates a right-handed, customized perspective projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// When the method completes, contains the created projection matrix. + public static void PerspectiveOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result) + { + PerspectiveOffCenterLH(left, right, bottom, top, znear, zfar, out result); + result.M31 *= -1.0f; + result.M32 *= -1.0f; + result.M33 *= -1.0f; + result.M34 *= -1.0f; + } + + /// + /// Creates a right-handed, customized perspective projection matrix. + /// + /// Minimum x-value of the viewing volume. + /// Maximum x-value of the viewing volume. + /// Minimum y-value of the viewing volume. + /// Maximum y-value of the viewing volume. + /// Minimum z-value of the viewing volume. + /// Maximum z-value of the viewing volume. + /// The created projection matrix. + public static Matrix PerspectiveOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar) + { + Matrix result; + PerspectiveOffCenterRH(left, right, bottom, top, znear, zfar, out result); + return result; + } + + /// + /// Builds a matrix that can be used to reflect vectors about a plane. + /// + /// The plane for which the reflection occurs. This parameter is assumed to be normalized. + /// When the method completes, contains the reflection matrix. + public static void Reflection(ref Plane plane, out Matrix result) + { + float x = plane.Normal.X; + float y = plane.Normal.Y; + float z = plane.Normal.Z; + float x2 = -2.0f * x; + float y2 = -2.0f * y; + float z2 = -2.0f * z; + + result.M11 = (x2 * x) + 1.0f; + result.M12 = y2 * x; + result.M13 = z2 * x; + result.M14 = 0.0f; + result.M21 = x2 * y; + result.M22 = (y2 * y) + 1.0f; + result.M23 = z2 * y; + result.M24 = 0.0f; + result.M31 = x2 * z; + result.M32 = y2 * z; + result.M33 = (z2 * z) + 1.0f; + result.M34 = 0.0f; + result.M41 = x2 * plane.D; + result.M42 = y2 * plane.D; + result.M43 = z2 * plane.D; + result.M44 = 1.0f; + } + + /// + /// Builds a matrix that can be used to reflect vectors about a plane. + /// + /// The plane for which the reflection occurs. This parameter is assumed to be normalized. + /// The reflection matrix. + public static Matrix Reflection(Plane plane) + { + Matrix result; + Reflection(ref plane, out result); + return result; + } + + /// + /// Creates a matrix that flattens geometry into a shadow. + /// + /// The light direction. If the W component is 0, the light is directional light; if the + /// W component is 1, the light is a point light. + /// The plane onto which to project the geometry as a shadow. This parameter is assumed to be normalized. + /// When the method completes, contains the shadow matrix. + public static void Shadow(ref Vector4 light, ref Plane plane, out Matrix result) + { + float dot = (plane.Normal.X * light.X) + (plane.Normal.Y * light.Y) + (plane.Normal.Z * light.Z) + (plane.D * light.W); + float x = -plane.Normal.X; + float y = -plane.Normal.Y; + float z = -plane.Normal.Z; + float d = -plane.D; + + result.M11 = (x * light.X) + dot; + result.M21 = y * light.X; + result.M31 = z * light.X; + result.M41 = d * light.X; + result.M12 = x * light.Y; + result.M22 = (y * light.Y) + dot; + result.M32 = z * light.Y; + result.M42 = d * light.Y; + result.M13 = x * light.Z; + result.M23 = y * light.Z; + result.M33 = (z * light.Z) + dot; + result.M43 = d * light.Z; + result.M14 = x * light.W; + result.M24 = y * light.W; + result.M34 = z * light.W; + result.M44 = (d * light.W) + dot; + } + + /// + /// Creates a matrix that flattens geometry into a shadow. + /// + /// The light direction. If the W component is 0, the light is directional light; if the + /// W component is 1, the light is a point light. + /// The plane onto which to project the geometry as a shadow. This parameter is assumed to be normalized. + /// The shadow matrix. + public static Matrix Shadow(Vector4 light, Plane plane) + { + Matrix result; + Shadow(ref light, ref plane, out result); + return result; + } + + /// + /// Creates a matrix that scales along the x-axis, y-axis, and y-axis. + /// + /// Scaling factor for all three axes. + /// When the method completes, contains the created scaling matrix. + public static void Scaling(ref Vector3 scale, out Matrix result) + { + Scaling(scale.X, scale.Y, scale.Z, out result); + } + + /// + /// Creates a matrix that scales along the x-axis, y-axis, and y-axis. + /// + /// Scaling factor for all three axes. + /// The created scaling matrix. + public static Matrix Scaling(Vector3 scale) + { + Matrix result; + Scaling(ref scale, out result); + return result; + } + + /// + /// Creates a matrix that scales along the x-axis, y-axis, and y-axis. + /// + /// Scaling factor that is applied along the x-axis. + /// Scaling factor that is applied along the y-axis. + /// Scaling factor that is applied along the z-axis. + /// When the method completes, contains the created scaling matrix. + public static void Scaling(float x, float y, float z, out Matrix result) + { + result = Matrix.Identity; + result.M11 = x; + result.M22 = y; + result.M33 = z; + } + + /// + /// Creates a matrix that scales along the x-axis, y-axis, and y-axis. + /// + /// Scaling factor that is applied along the x-axis. + /// Scaling factor that is applied along the y-axis. + /// Scaling factor that is applied along the z-axis. + /// The created scaling matrix. + public static Matrix Scaling(float x, float y, float z) + { + Matrix result; + Scaling(x, y, z, out result); + return result; + } + + /// + /// Creates a matrix that uniformally scales along all three axis. + /// + /// The uniform scale that is applied along all axis. + /// When the method completes, contains the created scaling matrix. + public static void Scaling(float scale, out Matrix result) + { + result = Matrix.Identity; + result.M11 = result.M22 = result.M33 = scale; + } + + /// + /// Creates a matrix that uniformally scales along all three axis. + /// + /// The uniform scale that is applied along all axis. + /// The created scaling matrix. + public static Matrix Scaling(float scale) + { + Matrix result; + Scaling(scale, out result); + return result; + } + + /// + /// Creates a matrix that rotates around the x-axis. + /// + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// When the method completes, contains the created rotation matrix. + public static void RotationX(float angle, out Matrix result) + { + float cos = (float)Math.Cos(angle); + float sin = (float)Math.Sin(angle); + + result = Matrix.Identity; + result.M22 = cos; + result.M23 = sin; + result.M32 = -sin; + result.M33 = cos; + } + + /// + /// Creates a matrix that rotates around the x-axis. + /// + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// The created rotation matrix. + public static Matrix RotationX(float angle) + { + Matrix result; + RotationX(angle, out result); + return result; + } + + /// + /// Creates a matrix that rotates around the y-axis. + /// + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// When the method completes, contains the created rotation matrix. + public static void RotationY(float angle, out Matrix result) + { + float cos = (float)Math.Cos(angle); + float sin = (float)Math.Sin(angle); + + result = Matrix.Identity; + result.M11 = cos; + result.M13 = -sin; + result.M31 = sin; + result.M33 = cos; + } + + /// + /// Creates a matrix that rotates around the y-axis. + /// + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// The created rotation matrix. + public static Matrix RotationY(float angle) + { + Matrix result; + RotationY(angle, out result); + return result; + } + + /// + /// Creates a matrix that rotates around the z-axis. + /// + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// When the method completes, contains the created rotation matrix. + public static void RotationZ(float angle, out Matrix result) + { + float cos = (float)Math.Cos(angle); + float sin = (float)Math.Sin(angle); + + result = Matrix.Identity; + result.M11 = cos; + result.M12 = sin; + result.M21 = -sin; + result.M22 = cos; + } + + /// + /// Creates a matrix that rotates around the z-axis. + /// + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// The created rotation matrix. + public static Matrix RotationZ(float angle) + { + Matrix result; + RotationZ(angle, out result); + return result; + } + + /// + /// Creates a matrix that rotates around an arbitary axis. + /// + /// The axis around which to rotate. This parameter is assumed to be normalized. + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// When the method completes, contains the created rotation matrix. + public static void RotationAxis(ref Vector3 axis, float angle, out Matrix result) + { + float x = axis.X; + float y = axis.Y; + float z = axis.Z; + float cos = (float)Math.Cos(angle); + float sin = (float)Math.Sin(angle); + float xx = x * x; + float yy = y * y; + float zz = z * z; + float xy = x * y; + float xz = x * z; + float yz = y * z; + + result = Matrix.Identity; + result.M11 = xx + (cos * (1.0f - xx)); + result.M12 = (xy - (cos * xy)) + (sin * z); + result.M13 = (xz - (cos * xz)) - (sin * y); + result.M21 = (xy - (cos * xy)) - (sin * z); + result.M22 = yy + (cos * (1.0f - yy)); + result.M23 = (yz - (cos * yz)) + (sin * x); + result.M31 = (xz - (cos * xz)) + (sin * y); + result.M32 = (yz - (cos * yz)) - (sin * x); + result.M33 = zz + (cos * (1.0f - zz)); + } + + /// + /// Creates a matrix that rotates around an arbitary axis. + /// + /// The axis around which to rotate. This parameter is assumed to be normalized. + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// The created rotation matrix. + public static Matrix RotationAxis(Vector3 axis, float angle) + { + Matrix result; + RotationAxis(ref axis, angle, out result); + return result; + } + + /// + /// Creates a rotation matrix from a quaternion. + /// + /// The quaternion to use to build the matrix. + /// The created rotation matrix. + public static void RotationQuaternion(ref Quaternion rotation, out Matrix result) + { + float xx = rotation.X * rotation.X; + float yy = rotation.Y * rotation.Y; + float zz = rotation.Z * rotation.Z; + float xy = rotation.X * rotation.Y; + float zw = rotation.Z * rotation.W; + float zx = rotation.Z * rotation.X; + float yw = rotation.Y * rotation.W; + float yz = rotation.Y * rotation.Z; + float xw = rotation.X * rotation.W; + + result = Matrix.Identity; + result.M11 = 1.0f - (2.0f * (yy + zz)); + result.M12 = 2.0f * (xy + zw); + result.M13 = 2.0f * (zx - yw); + result.M21 = 2.0f * (xy - zw); + result.M22 = 1.0f - (2.0f * (zz + xx)); + result.M23 = 2.0f * (yz + xw); + result.M31 = 2.0f * (zx + yw); + result.M32 = 2.0f * (yz - xw); + result.M33 = 1.0f - (2.0f * (yy + xx)); + } + + /// + /// Creates a matrix that contains both the X, Y and Z rotation, as well as scaling and translation. Note: This function is NOT thead safe. + /// + /// The scaling. + /// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin. + /// The translation. + /// When the method completes, contains the created rotation matrix. + public static void Transformation(ref Vector3 scaling, ref Quaternion rotation, ref Vector3 translation, out Matrix result) + { + // Equivalent to: + //result = + // Matrix.Scaling(scaling) + // *Matrix.RotationX(rotation.X) + // *Matrix.RotationY(rotation.Y) + // *Matrix.RotationZ(rotation.Z) + // *Matrix.Position(translation); + + // Rotation + float xx = rotation.X * rotation.X; + float yy = rotation.Y * rotation.Y; + float zz = rotation.Z * rotation.Z; + float xy = rotation.X * rotation.Y; + float zw = rotation.Z * rotation.W; + float zx = rotation.Z * rotation.X; + float yw = rotation.Y * rotation.W; + float yz = rotation.Y * rotation.Z; + float xw = rotation.X * rotation.W; + + result.M11 = 1.0f - (2.0f * (yy + zz)); + result.M12 = 2.0f * (xy + zw); + result.M13 = 2.0f * (zx - yw); + result.M21 = 2.0f * (xy - zw); + result.M22 = 1.0f - (2.0f * (zz + xx)); + result.M23 = 2.0f * (yz + xw); + result.M31 = 2.0f * (zx + yw); + result.M32 = 2.0f * (yz - xw); + result.M33 = 1.0f - (2.0f * (yy + xx)); + + // Position + result.M41 = translation.X; + result.M42 = translation.Y; + result.M43 = translation.Z; + + // Scale + if (scaling.X != 1.0f) + { + result.M11 *= scaling.X; + result.M12 *= scaling.X; + result.M13 *= scaling.X; + } + if (scaling.Y != 1.0f) + { + result.M21 *= scaling.Y; + result.M22 *= scaling.Y; + result.M23 *= scaling.Y; + } + if (scaling.Z != 1.0f) + { + result.M31 *= scaling.Z; + result.M32 *= scaling.Z; + result.M33 *= scaling.Z; + } + + result.M14 = 0.0f; + result.M24 = 0.0f; + result.M34 = 0.0f; + result.M44 = 1.0f; + } + + /// + /// Creates a rotation matrix from a quaternion. + /// + /// The quaternion to use to build the matrix. + /// The created rotation matrix. + public static Matrix RotationQuaternion(Quaternion rotation) + { + Matrix result; + RotationQuaternion(ref rotation, out result); + return result; + } + + /// + /// Creates a rotation matrix with a specified yaw, pitch, and roll. + /// + /// Yaw around the y-axis, in radians. + /// Pitch around the x-axis, in radians. + /// Roll around the z-axis, in radians. + /// When the method completes, contains the created rotation matrix. + public static void RotationYawPitchRoll(float yaw, float pitch, float roll, out Matrix result) + { + Quaternion quaternion = new Quaternion(); + Quaternion.RotationYawPitchRoll(yaw, pitch, roll, out quaternion); + RotationQuaternion(ref quaternion, out result); + } + + /// + /// Creates a rotation matrix with a specified yaw, pitch, and roll. + /// + /// Yaw around the y-axis, in radians. + /// Pitch around the x-axis, in radians. + /// Roll around the z-axis, in radians. + /// The created rotation matrix. + public static Matrix RotationYawPitchRoll(float yaw, float pitch, float roll) + { + Matrix result; + RotationYawPitchRoll(yaw, pitch, roll, out result); + return result; + } + + /// + /// Creates a translation matrix using the specified offsets. + /// + /// The offset for all three coordinate planes. + /// When the method completes, contains the created translation matrix. + public static void Translation(ref Vector3 value, out Matrix result) + { + Translation(value.X, value.Y, value.Z, out result); + } + + /// + /// Creates a translation matrix using the specified offsets. + /// + /// The offset for all three coordinate planes. + /// The created translation matrix. + public static Matrix Translation(Vector3 value) + { + Matrix result; + Translation(ref value, out result); + return result; + } + + /// + /// Creates a translation matrix using the specified offsets. + /// + /// X-coordinate offset. + /// Y-coordinate offset. + /// Z-coordinate offset. + /// When the method completes, contains the created translation matrix. + public static void Translation(float x, float y, float z, out Matrix result) + { + result = Matrix.Identity; + result.M41 = x; + result.M42 = y; + result.M43 = z; + } + + /// + /// Creates a translation matrix using the specified offsets. + /// + /// X-coordinate offset. + /// Y-coordinate offset. + /// Z-coordinate offset. + /// The created translation matrix. + public static Matrix Translation(float x, float y, float z) + { + Matrix result; + Translation(x, y, z, out result); + return result; + } + + /// + /// Creates a 3D affine transformation matrix. + /// + /// Scaling factor. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// When the method completes, contains the created affine transformation matrix. + public static void AffineTransformation(float scaling, ref Quaternion rotation, ref Vector3 translation, out Matrix result) + { + result = Scaling(scaling) * RotationQuaternion(rotation) * Translation(translation); + } + + /// + /// Creates a 3D affine transformation matrix. + /// + /// Scaling factor. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// The created affine transformation matrix. + public static Matrix AffineTransformation(float scaling, Quaternion rotation, Vector3 translation) + { + Matrix result; + AffineTransformation(scaling, ref rotation, ref translation, out result); + return result; + } + + /// + /// Creates a 3D affine transformation matrix. + /// + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// When the method completes, contains the created affine transformation matrix. + public static void AffineTransformation(float scaling, ref Vector3 rotationCenter, ref Quaternion rotation, ref Vector3 translation, out Matrix result) + { + result = Scaling(scaling) * Translation(-rotationCenter) * RotationQuaternion(rotation) * + Translation(rotationCenter) * Translation(translation); + } + + /// + /// Creates a 3D affine transformation matrix. + /// + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// The created affine transformation matrix. + public static Matrix AffineTransformation(float scaling, Vector3 rotationCenter, Quaternion rotation, Vector3 translation) + { + Matrix result; + AffineTransformation(scaling, ref rotationCenter, ref rotation, ref translation, out result); + return result; + } + + /// + /// Creates a 2D affine transformation matrix. + /// + /// Scaling factor. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// When the method completes, contains the created affine transformation matrix. + public static void AffineTransformation2D(float scaling, float rotation, ref Vec2 translation, out Matrix result) + { + result = Scaling(scaling, scaling, 1.0f) * RotationZ(rotation) * Translation((Vector3)translation); + } + + /// + /// Creates a 2D affine transformation matrix. + /// + /// Scaling factor. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// The created affine transformation matrix. + public static Matrix AffineTransformation2D(float scaling, float rotation, Vec2 translation) + { + Matrix result; + AffineTransformation2D(scaling, rotation, ref translation, out result); + return result; + } + + /// + /// Creates a 2D affine transformation matrix. + /// + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// When the method completes, contains the created affine transformation matrix. + public static void AffineTransformation2D(float scaling, ref Vec2 rotationCenter, float rotation, ref Vec2 translation, out Matrix result) + { + result = Scaling(scaling, scaling, 1.0f) * Translation((Vector3)(-rotationCenter)) * RotationZ(rotation) * + Translation((Vector3)rotationCenter) * Translation((Vector3)translation); + } + + /// + /// Creates a 2D affine transformation matrix. + /// + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// The created affine transformation matrix. + public static Matrix AffineTransformation2D(float scaling, Vec2 rotationCenter, float rotation, Vec2 translation) + { + Matrix result; + AffineTransformation2D(scaling, ref rotationCenter, rotation, ref translation, out result); + return result; + } + + /// + /// Creates a transformation matrix. + /// + /// Center point of the scaling operation. + /// Scaling rotation amount. + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// When the method completes, contains the created transformation matrix. + public static void Transformation(ref Vector3 scalingCenter, ref Quaternion scalingRotation, ref Vector3 scaling, ref Vector3 rotationCenter, ref Quaternion rotation, ref Vector3 translation, out Matrix result) + { + Matrix sr = RotationQuaternion(scalingRotation); + + result = Translation(-scalingCenter) * Transpose(sr) * Scaling(scaling) * sr * Translation(scalingCenter) * Translation(-rotationCenter) * + RotationQuaternion(rotation) * Translation(rotationCenter) * Translation(translation); + } + + /// + /// Creates a transformation matrix. + /// + /// Center point of the scaling operation. + /// Scaling rotation amount. + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// The created transformation matrix. + public static Matrix Transformation(Vector3 scalingCenter, Quaternion scalingRotation, Vector3 scaling, Vector3 rotationCenter, Quaternion rotation, Vector3 translation) + { + Matrix result; + Transformation(ref scalingCenter, ref scalingRotation, ref scaling, ref rotationCenter, ref rotation, ref translation, out result); + return result; + } + + /// + /// Creates a 2D transformation matrix. + /// + /// Center point of the scaling operation. + /// Scaling rotation amount. + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// When the method completes, contains the created transformation matrix. + public static void Transformation2D(ref Vec2 scalingCenter, float scalingRotation, ref Vec2 scaling, ref Vec2 rotationCenter, float rotation, ref Vec2 translation, out Matrix result) + { + result = Translation((Vector3)(-scalingCenter)) * RotationZ(-scalingRotation) * Scaling((Vector3)scaling) * RotationZ(scalingRotation) * Translation((Vector3)scalingCenter) * + Translation((Vector3)(-rotationCenter)) * RotationZ(rotation) * Translation((Vector3)rotationCenter) * Translation((Vector3)translation); + + result.M33 = 1f; + result.M44 = 1f; + } + + /// + /// Creates a 2D transformation matrix. + /// + /// Center point of the scaling operation. + /// Scaling rotation amount. + /// Scaling factor. + /// The center of the rotation. + /// The rotation of the transformation. + /// The translation factor of the transformation. + /// The created transformation matrix. + public static Matrix Transformation2D(Vec2 scalingCenter, float scalingRotation, Vec2 scaling, Vec2 rotationCenter, float rotation, Vec2 translation) + { + Matrix result; + Transformation2D(ref scalingCenter, scalingRotation, ref scaling, ref rotationCenter, rotation, ref translation, out result); + return result; + } + + /// + /// Copies a nxm matrix to this instance. + /// + /// The source matrix. + /// The number of columns. + /// The number of rows. + public unsafe void CopyMatrixFrom(float* src, int columns, int rows) + { + fixed (void* pDest = &this) + { + var dest = (float*)pDest; + for (int i = 0; i < rows; ++i) + { + for (int j = 0; j < columns; ++j) + { + dest[j] = src[j]; + } + dest += 4; + src += rows; + } + } + } + + /// + /// Transposes a nmx matrix to this instance. + /// + /// The SRC. + /// The columns. + /// The rows. + public unsafe void TransposeMatrixFrom(float* src, int columns, int rows) + { + fixed (void* pDest = &this) + { + var dest = (float*)pDest; + for (int i = 0; i < rows; ++i) + { + int sourceIndex = i; + for (int j = 0; j < columns; ++j) + { + dest[j] = src[sourceIndex]; + sourceIndex += rows; + } + dest += 4; + } + } + } + + /// + /// Adds two matricies. + /// + /// The first matrix to add. + /// The second matrix to add. + /// The sum of the two matricies. + public static Matrix operator +(Matrix left, Matrix right) + { + Matrix result; + Add(ref left, ref right, out result); + return result; + } + + /// + /// Assert a matrix (return it unchanged). + /// + /// The matrix to assert (unchange). + /// The asserted (unchanged) matrix. + public static Matrix operator +(Matrix value) + { + return value; + } + + /// + /// Subtracts two matricies. + /// + /// The first matrix to subtract. + /// The second matrix to subtract. + /// The difference between the two matricies. + public static Matrix operator -(Matrix left, Matrix right) + { + Matrix result; + Subtract(ref left, ref right, out result); + return result; + } + + /// + /// Negates a matrix. + /// + /// The matrix to negate. + /// The negated matrix. + public static Matrix operator -(Matrix value) + { + Matrix result; + Negate(ref value, out result); + return result; + } + + /// + /// Scales a matrix by a given value. + /// + /// The matrix to scale. + /// The amount by which to scale. + /// The scaled matrix. + public static Matrix operator *(float left, Matrix right) + { + Matrix result; + Multiply(ref right, left, out result); + return result; + } + + /// + /// Scales a matrix by a given value. + /// + /// The matrix to scale. + /// The amount by which to scale. + /// The scaled matrix. + public static Matrix operator *(Matrix left, float right) + { + Matrix result; + Multiply(ref left, right, out result); + return result; + } + + /// + /// Multiplies two matrices. + /// + /// The first matrix to multiply. + /// The second matrix to multiply. + /// The product of the two matricies. + public static Matrix operator *(Matrix left, Matrix right) + { + Matrix result; + Multiply(ref left, ref right, out result); + return result; + } + + /// + /// Scales a matrix by a given value. + /// + /// The matrix to scale. + /// The amount by which to scale. + /// The scaled matrix. + public static Matrix operator /(Matrix left, float right) + { + Matrix result; + Divide(ref left, right, out result); + return result; + } + + /// + /// Divides two matricies. + /// + /// The first matrix to divide. + /// The second matrix to divide. + /// The quotient of the two matricies. + public static Matrix operator /(Matrix left, Matrix right) + { + Matrix result; + Divide(ref left, ref right, out result); + return result; + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Matrix left, Matrix right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Matrix left, Matrix right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "[M11:{0} M12:{1} M13:{2} M14:{3}] [M21:{4} M22:{5} M23:{6} M24:{7}] [M31:{8} M32:{9} M33:{10} M34:{11}] [M41:{12} M42:{13} M43:{14} M44:{15}]", + M11, M12, M13, M14, M21, M22, M23, M24, M31, M32, M33, M34, M41, M42, M43, M44); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(format, CultureInfo.CurrentCulture, "[M11:{0} M12:{1} M13:{2} M14:{3}] [M21:{4} M22:{5} M23:{6} M24:{7}] [M31:{8} M32:{9} M33:{10} M34:{11}] [M41:{12} M42:{13} M43:{14} M44:{15}]", + M11.ToString(format, CultureInfo.CurrentCulture), M12.ToString(format, CultureInfo.CurrentCulture), M13.ToString(format, CultureInfo.CurrentCulture), M14.ToString(format, CultureInfo.CurrentCulture), + M21.ToString(format, CultureInfo.CurrentCulture), M22.ToString(format, CultureInfo.CurrentCulture), M23.ToString(format, CultureInfo.CurrentCulture), M24.ToString(format, CultureInfo.CurrentCulture), + M31.ToString(format, CultureInfo.CurrentCulture), M32.ToString(format, CultureInfo.CurrentCulture), M33.ToString(format, CultureInfo.CurrentCulture), M34.ToString(format, CultureInfo.CurrentCulture), + M41.ToString(format, CultureInfo.CurrentCulture), M42.ToString(format, CultureInfo.CurrentCulture), M43.ToString(format, CultureInfo.CurrentCulture), M44.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "[M11:{0} M12:{1} M13:{2} M14:{3}] [M21:{4} M22:{5} M23:{6} M24:{7}] [M31:{8} M32:{9} M33:{10} M34:{11}] [M41:{12} M42:{13} M43:{14} M44:{15}]", + M11.ToString(formatProvider), M12.ToString(formatProvider), M13.ToString(formatProvider), M14.ToString(formatProvider), + M21.ToString(formatProvider), M22.ToString(formatProvider), M23.ToString(formatProvider), M24.ToString(formatProvider), + M31.ToString(formatProvider), M32.ToString(formatProvider), M33.ToString(formatProvider), M34.ToString(formatProvider), + M41.ToString(formatProvider), M42.ToString(formatProvider), M43.ToString(formatProvider), M44.ToString(formatProvider)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(format, formatProvider, "[M11:{0} M12:{1} M13:{2} M14:{3}] [M21:{4} M22:{5} M23:{6} M24:{7}] [M31:{8} M32:{9} M33:{10} M34:{11}] [M41:{12} M42:{13} M43:{14} M44:{15}]", + M11.ToString(format, formatProvider), M12.ToString(format, formatProvider), M13.ToString(format, formatProvider), M14.ToString(format, formatProvider), + M21.ToString(format, formatProvider), M22.ToString(format, formatProvider), M23.ToString(format, formatProvider), M24.ToString(format, formatProvider), + M31.ToString(format, formatProvider), M32.ToString(format, formatProvider), M33.ToString(format, formatProvider), M34.ToString(format, formatProvider), + M41.ToString(format, formatProvider), M42.ToString(format, formatProvider), M43.ToString(format, formatProvider), M44.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return M11.GetHashCode() + M12.GetHashCode() + M13.GetHashCode() + M14.GetHashCode() + + M21.GetHashCode() + M22.GetHashCode() + M23.GetHashCode() + M24.GetHashCode() + + M31.GetHashCode() + M32.GetHashCode() + M33.GetHashCode() + M34.GetHashCode() + + M41.GetHashCode() + M42.GetHashCode() + M43.GetHashCode() + M44.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Matrix other) + { + return (Math.Abs(other.M11 - M11) < MathUtil.ZeroTolerance && + Math.Abs(other.M12 - M12) < MathUtil.ZeroTolerance && + Math.Abs(other.M13 - M13) < MathUtil.ZeroTolerance && + Math.Abs(other.M14 - M14) < MathUtil.ZeroTolerance && + + Math.Abs(other.M21 - M21) < MathUtil.ZeroTolerance && + Math.Abs(other.M22 - M22) < MathUtil.ZeroTolerance && + Math.Abs(other.M23 - M23) < MathUtil.ZeroTolerance && + Math.Abs(other.M24 - M24) < MathUtil.ZeroTolerance && + + Math.Abs(other.M31 - M31) < MathUtil.ZeroTolerance && + Math.Abs(other.M32 - M32) < MathUtil.ZeroTolerance && + Math.Abs(other.M33 - M33) < MathUtil.ZeroTolerance && + Math.Abs(other.M34 - M34) < MathUtil.ZeroTolerance && + + Math.Abs(other.M41 - M41) < MathUtil.ZeroTolerance && + Math.Abs(other.M42 - M42) < MathUtil.ZeroTolerance && + Math.Abs(other.M43 - M43) < MathUtil.ZeroTolerance && + Math.Abs(other.M44 - M44) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Matrix)value); + } + +#if SlimDX1xInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator SlimDX.Matrix(Matrix value) + { + return new SlimDX.Matrix() + { + M11 = value.M11, M12 = value.M12, M13 = value.M13, M14 = value.M14, + M21 = value.M21, M22 = value.M22, M23 = value.M23, M24 = value.M24, + M31 = value.M31, M32 = value.M32, M33 = value.M33, M34 = value.M34, + M41 = value.M41, M42 = value.M42, M43 = value.M43, M44 = value.M44 + }; + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Matrix(SlimDX.Matrix value) + { + return new Matrix() + { + M11 = value.M11, M12 = value.M12, M13 = value.M13, M14 = value.M14, + M21 = value.M21, M22 = value.M22, M23 = value.M23, M24 = value.M24, + M31 = value.M31, M32 = value.M32, M33 = value.M33, M34 = value.M34, + M41 = value.M41, M42 = value.M42, M43 = value.M43, M44 = value.M44 + }; + } +#endif + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Matrix3D(Matrix value) + { + return new System.Windows.Media.Media3D.Matrix3D() + { + M11 = value.M11, M12 = value.M12, M13 = value.M13, M14 = value.M14, + M21 = value.M21, M22 = value.M22, M23 = value.M23, M24 = value.M24, + M31 = value.M31, M32 = value.M32, M33 = value.M33, M34 = value.M34, + OffsetX = value.M41, OffsetY = value.M42, OffsetZ = value.M43, M44 = value.M44 + }; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Matrix(System.Windows.Media.Media3D.Matrix3D value) + { + return new Matrix() + { + M11 = (float)value.M11, M12 = (float)value.M12, M13 = (float)value.M13, M14 = (float)value.M14, + M21 = (float)value.M21, M22 = (float)value.M22, M23 = (float)value.M23, M24 = (float)value.M24, + M31 = (float)value.M31, M32 = (float)value.M32, M33 = (float)value.M33, M34 = (float)value.M34, + M41 = (float)value.OffsetX, M42 = (float)value.OffsetY, M43 = (float)value.OffsetZ, M44 = (float)value.M44 + }; + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Matrix(Matrix value) + { + return new Microsoft.Xna.Framework.Matrix() + { + M11 = value.M11, M12 = value.M12, M13 = value.M13, M14 = value.M14, + M21 = value.M21, M22 = value.M22, M23 = value.M23, M24 = value.M24, + M31 = value.M31, M32 = value.M32, M33 = value.M33, M34 = value.M34, + M41 = value.M41, M42 = value.M42, M43 = value.M43, M44 = value.M44 + }; + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Matrix(Microsoft.Xna.Framework.Matrix value) + { + return new Matrix() + { + M11 = value.M11, M12 = value.M12, M13 = value.M13, M14 = value.M14, + M21 = value.M21, M22 = value.M22, M23 = value.M23, M24 = value.M24, + M31 = value.M31, M32 = value.M32, M33 = value.M33, M34 = value.M34, + M41 = value.M41, M42 = value.M42, M43 = value.M43, M44 = value.M44 + }; + } +#endif + } +} diff --git a/math/Module.cs b/math/Module.cs new file mode 100644 index 0000000..9271236 --- /dev/null +++ b/math/Module.cs @@ -0,0 +1,22 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System.Reflection; +using Xenko.Core.Reflection; + +namespace math +{ + /// + /// Module initializer. + /// + internal class Module + { + /// + /// Module initializer. + /// + [ModuleInitializer] + public static void Initialize() + { + AssemblyRegistry.Register(typeof(Module).GetTypeInfo().Assembly, AssemblyCommonCategories.Assets); + } + } +} diff --git a/math/NamespaceDoc.cs b/math/NamespaceDoc.cs new file mode 100644 index 0000000..b9871ff --- /dev/null +++ b/math/NamespaceDoc.cs @@ -0,0 +1,12 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +namespace math +{ + /// + /// The namespace contains types that allows to manipulate mathematical objects, such as vectors, points, quaternions, planes, etc. + /// + [System.Runtime.CompilerServices.CompilerGenerated] + internal class NamespaceDoc + { + } +} diff --git a/math/Plane.cs b/math/Plane.cs new file mode 100644 index 0000000..d117854 --- /dev/null +++ b/math/Plane.cs @@ -0,0 +1,838 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a plane in three dimensional space. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Plane : IEquatable, IFormattable + { + /// + /// The normal vector of the plane. + /// + public Vector3 Normal; + + /// + /// The distance of the plane along its normal from the origin. + /// + public float D; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Plane(float value) + { + Normal.X = Normal.Y = Normal.Z = D = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The X component of the normal. + /// The Y component of the normal. + /// The Z component of the normal. + /// The distance of the plane along its normal from the origin. + public Plane(float a, float b, float c, float d) + { + Normal.X = a; + Normal.Y = b; + Normal.Z = c; + D = d; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Any point that lies along the plane. + /// The normal vector to the plane. + public Plane(Vector3 point, Vector3 normal) + { + this.Normal = normal; + this.D = Vector3.Dot(normal, point); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The normal of the plane. + /// The distance of the plane along its normal from the origin + public Plane(Vector3 value, float d) + { + Normal = value; + D = d; + } + + /// + /// Initializes a new instance of the struct. + /// + /// First point of a triangle defining the plane. + /// Second point of a triangle defining the plane. + /// Third point of a triangle defining the plane. + public Plane(Vector3 point1, Vector3 point2, Vector3 point3) + { + float x1 = point2.X - point1.X; + float y1 = point2.Y - point1.Y; + float z1 = point2.Z - point1.Z; + float x2 = point3.X - point1.X; + float y2 = point3.Y - point1.Y; + float z2 = point3.Z - point1.Z; + float yz = (y1 * z2) - (z1 * y2); + float xz = (z1 * x2) - (x1 * z2); + float xy = (x1 * y2) - (y1 * x2); + float invPyth = 1.0f / (float)(Math.Sqrt((yz * yz) + (xz * xz) + (xy * xy))); + + Normal.X = yz * invPyth; + Normal.Y = xz * invPyth; + Normal.Z = xy * invPyth; + D = -((Normal.X * point1.X) + (Normal.Y * point1.Y) + (Normal.Z * point1.Z)); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the A, B, C, and D components of the plane. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Plane(float[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 4) + throw new ArgumentOutOfRangeException("values", "There must be four and only four input values for Plane."); + + Normal.X = values[0]; + Normal.Y = values[1]; + Normal.Z = values[2]; + D = values[3]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the A, B, C, or D component, depending on the index. + /// The index of the component to access. Use 0 for the A component, 1 for the B component, 2 for the C component, and 3 for the D component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return Normal.X; + case 1: return Normal.Y; + case 2: return Normal.Z; + case 3: return D; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Plane run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: Normal.X = value; break; + case 1: Normal.Y = value; break; + case 2: Normal.Z = value; break; + case 3: D = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Plane run from 0 to 3, inclusive."); + } + } + } + + /// + /// Negates a plane by negating all its coefficients, which result in a plane in opposite direction. + /// + public void Negate() + { + Normal.X = -Normal.X; + Normal.Y = -Normal.Y; + Normal.Z = -Normal.Z; + D = -D; + } + + /// + /// Changes the coefficients of the normal vector of the plane to make it of unit length. + /// + public void Normalize() + { + float magnitude = 1.0f / (float)(Math.Sqrt((Normal.X * Normal.X) + (Normal.Y * Normal.Y) + (Normal.Z * Normal.Z))); + + Normal.X *= magnitude; + Normal.Y *= magnitude; + Normal.Z *= magnitude; + D *= magnitude; + } + + /// + /// Creates an array containing the elements of the plane. + /// + /// A four-element array containing the components of the plane. + public float[] ToArray() + { + return new float[] { Normal.X, Normal.Y, Normal.Z, D }; + } + + /// + /// Determines if there is an intersection between the current object and a point. + /// + /// The point to test. + /// Whether the two objects intersected. + public PlaneIntersectionType Intersects(ref Vector3 point) + { + return CollisionHelper.PlaneIntersectsPoint(ref this, ref point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray) + { + float distance; + return CollisionHelper.RayIntersectsPlane(ref ray, ref this, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray, out float distance) + { + return CollisionHelper.RayIntersectsPlane(ref ray, ref this, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray, out Vector3 point) + { + return CollisionHelper.RayIntersectsPlane(ref ray, ref this, out point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The plane to test. + /// Whether the two objects intersected. + public bool Intersects(ref Plane plane) + { + return CollisionHelper.PlaneIntersectsPlane(ref this, ref plane); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The plane to test. + /// When the method completes, contains the line of intersection + /// as a , or a zero ray if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Plane plane, out Ray line) + { + return CollisionHelper.PlaneIntersectsPlane(ref this, ref plane, out line); + } + + /// + /// Determines if there is an intersection between the current object and a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triagnle to test. + /// The third vertex of the triangle to test. + /// Whether the two objects intersected. + public PlaneIntersectionType Intersects(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + return CollisionHelper.PlaneIntersectsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The box to test. + /// Whether the two objects intersected. + public PlaneIntersectionType Intersects(ref BoundingBox box) + { + return CollisionHelper.PlaneIntersectsBox(ref this, ref box); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The sphere to test. + /// Whether the two objects intersected. + public PlaneIntersectionType Intersects(ref BoundingSphere sphere) + { + return CollisionHelper.PlaneIntersectsSphere(ref this, ref sphere); + } + + /// + /// Scales the plane by the given scaling factor. + /// + /// The plane to scale. + /// The amount by which to scale the plane. + /// When the method completes, contains the scaled plane. + public static void Multiply(ref Plane value, float scale, out Plane result) + { + result.Normal.X = value.Normal.X * scale; + result.Normal.Y = value.Normal.Y * scale; + result.Normal.Z = value.Normal.Z * scale; + result.D = value.D * scale; + } + + /// + /// Scales the plane by the given scaling factor. + /// + /// The plane to scale. + /// The amount by which to scale the plane. + /// The scaled plane. + public static Plane Multiply(Plane value, float scale) + { + return new Plane(value.Normal.X * scale, value.Normal.Y * scale, value.Normal.Z * scale, value.D * scale); + } + + /// + /// Calculates the dot product of the specified vector and plane. + /// + /// The source plane. + /// The source vector. + /// When the method completes, contains the dot product of the specified plane and vector. + public static void Dot(ref Plane left, ref Vector4 right, out float result) + { + result = (left.Normal.X * right.X) + (left.Normal.Y * right.Y) + (left.Normal.Z * right.Z) + (left.D * right.W); + } + + /// + /// Calculates the dot product of the specified vector and plane. + /// + /// The source plane. + /// The source vector. + /// The dot product of the specified plane and vector. + public static float Dot(Plane left, Vector4 right) + { + return (left.Normal.X * right.X) + (left.Normal.Y * right.Y) + (left.Normal.Z * right.Z) + (left.D * right.W); + } + + /// + /// Calculates the dot product of a specified vector and the normal of the plane plus the distance value of the plane. + /// + /// The source plane. + /// The source vector. + /// When the method completes, contains the dot product of a specified vector and the normal of the Plane plus the distance value of the plane. + public static void DotCoordinate(ref Plane left, ref Vector3 right, out float result) + { + result = (left.Normal.X * right.X) + (left.Normal.Y * right.Y) + (left.Normal.Z * right.Z) + left.D; + } + + /// + /// Calculates the dot product of a specified vector and the normal of the plane plus the distance value of the plane. + /// + /// The source plane. + /// The source vector. + /// The dot product of a specified vector and the normal of the Plane plus the distance value of the plane. + public static float DotCoordinate(Plane left, Vector3 right) + { + return (left.Normal.X * right.X) + (left.Normal.Y * right.Y) + (left.Normal.Z * right.Z) + left.D; + } + + /// + /// Calculates the dot product of the specified vector and the normal of the plane. + /// + /// The source plane. + /// The source vector. + /// When the method completes, contains the dot product of the specified vector and the normal of the plane. + public static void DotNormal(ref Plane left, ref Vector3 right, out float result) + { + result = (left.Normal.X * right.X) + (left.Normal.Y * right.Y) + (left.Normal.Z * right.Z); + } + + /// + /// Calculates the dot product of the specified vector and the normal of the plane. + /// + /// The source plane. + /// The source vector. + /// The dot product of the specified vector and the normal of the plane. + public static float DotNormal(Plane left, Vector3 right) + { + return (left.Normal.X * right.X) + (left.Normal.Y * right.Y) + (left.Normal.Z * right.Z); + } + + /// + /// Projects a point onto a plane. + /// + /// The plane to project the point to. + /// The point to project. + /// The projected point. + public static void Project(ref Plane plane, ref Vector3 point, out Vector3 result) + { + float distance; + DotCoordinate(ref plane, ref point, out distance); + + // compute: point - distance * plane.Normal + Vector3.Multiply(ref plane.Normal, distance, out result); + Vector3.Subtract(ref point, ref result, out result); + } + + /// + /// Projects a point onto a plane. + /// + /// The plane to project the point to. + /// The point to project. + /// The projected point. + public static Vector3 Project(Plane plane, Vector3 point) + { + Vector3 result; + Project(ref plane, ref point, out result); + return result; + } + + /// + /// Changes the coefficients of the normal vector of the plane to make it of unit length. + /// + /// The source plane. + /// When the method completes, contains the normalized plane. + public static void Normalize(ref Plane plane, out Plane result) + { + float magnitude = 1.0f / (float)(Math.Sqrt((plane.Normal.X * plane.Normal.X) + (plane.Normal.Y * plane.Normal.Y) + (plane.Normal.Z * plane.Normal.Z))); + + result.Normal.X = plane.Normal.X * magnitude; + result.Normal.Y = plane.Normal.Y * magnitude; + result.Normal.Z = plane.Normal.Z * magnitude; + result.D = plane.D * magnitude; + } + + /// + /// Changes the coefficients of the normal vector of the plane to make it of unit length. + /// + /// The source plane. + /// The normalized plane. + public static Plane Normalize(Plane plane) + { + float magnitude = 1.0f / (float)(Math.Sqrt((plane.Normal.X * plane.Normal.X) + (plane.Normal.Y * plane.Normal.Y) + (plane.Normal.Z * plane.Normal.Z))); + return new Plane(plane.Normal.X * magnitude, plane.Normal.Y * magnitude, plane.Normal.Z * magnitude, plane.D * magnitude); + } + + /// + /// Negates a plane by negating all its coefficients, which result in a plane in opposite direction. + /// + /// The source plane. + /// When the method completes, contains the flipped plane. + public static void Negate(ref Plane plane, out Plane result) + { + result.Normal.X = -plane.Normal.X; + result.Normal.Y = -plane.Normal.Y; + result.Normal.Z = -plane.Normal.Z; + result.D = -plane.D; + } + + /// + /// Negates a plane by negating all its coefficients, which result in a plane in opposite direction. + /// + /// The source plane. + /// The flipped plane. + public static Plane Negate(Plane plane) + { + float magnitude = 1.0f / (float)(Math.Sqrt((plane.Normal.X * plane.Normal.X) + (plane.Normal.Y * plane.Normal.Y) + (plane.Normal.Z * plane.Normal.Z))); + return new Plane(plane.Normal.X * magnitude, plane.Normal.Y * magnitude, plane.Normal.Z * magnitude, plane.D * magnitude); + } + + /// + /// Transforms a normalized plane by a quaternion rotation. + /// + /// The normalized source plane. + /// The quaternion rotation. + /// When the method completes, contains the transformed plane. + public static void Transform(ref Plane plane, ref Quaternion rotation, out Plane result) + { + float x2 = rotation.X + rotation.X; + float y2 = rotation.Y + rotation.Y; + float z2 = rotation.Z + rotation.Z; + float wx = rotation.W * x2; + float wy = rotation.W * y2; + float wz = rotation.W * z2; + float xx = rotation.X * x2; + float xy = rotation.X * y2; + float xz = rotation.X * z2; + float yy = rotation.Y * y2; + float yz = rotation.Y * z2; + float zz = rotation.Z * z2; + + float x = plane.Normal.X; + float y = plane.Normal.Y; + float z = plane.Normal.Z; + + result.Normal.X = ((x * ((1.0f - yy) - zz)) + (y * (xy - wz))) + (z * (xz + wy)); + result.Normal.Y = ((x * (xy + wz)) + (y * ((1.0f - xx) - zz))) + (z * (yz - wx)); + result.Normal.Z = ((x * (xz - wy)) + (y * (yz + wx))) + (z * ((1.0f - xx) - yy)); + result.D = plane.D; + } + + /// + /// Transforms a normalized plane by a quaternion rotation. + /// + /// The normalized source plane. + /// The quaternion rotation. + /// The transformed plane. + public static Plane Transform(Plane plane, Quaternion rotation) + { + Plane result; + float x2 = rotation.X + rotation.X; + float y2 = rotation.Y + rotation.Y; + float z2 = rotation.Z + rotation.Z; + float wx = rotation.W * x2; + float wy = rotation.W * y2; + float wz = rotation.W * z2; + float xx = rotation.X * x2; + float xy = rotation.X * y2; + float xz = rotation.X * z2; + float yy = rotation.Y * y2; + float yz = rotation.Y * z2; + float zz = rotation.Z * z2; + + float x = plane.Normal.X; + float y = plane.Normal.Y; + float z = plane.Normal.Z; + + result.Normal.X = ((x * ((1.0f - yy) - zz)) + (y * (xy - wz))) + (z * (xz + wy)); + result.Normal.Y = ((x * (xy + wz)) + (y * ((1.0f - xx) - zz))) + (z * (yz - wx)); + result.Normal.Z = ((x * (xz - wy)) + (y * (yz + wx))) + (z * ((1.0f - xx) - yy)); + result.D = plane.D; + + return result; + } + + /// + /// Transforms an array of normalized planes by a quaternion rotation. + /// + /// The array of normalized planes to transform. + /// The quaternion rotation. + /// Thrown when is null. + public static void Transform(Plane[] planes, ref Quaternion rotation) + { + if (planes == null) + throw new ArgumentNullException("planes"); + + float x2 = rotation.X + rotation.X; + float y2 = rotation.Y + rotation.Y; + float z2 = rotation.Z + rotation.Z; + float wx = rotation.W * x2; + float wy = rotation.W * y2; + float wz = rotation.W * z2; + float xx = rotation.X * x2; + float xy = rotation.X * y2; + float xz = rotation.X * z2; + float yy = rotation.Y * y2; + float yz = rotation.Y * z2; + float zz = rotation.Z * z2; + + for (int i = 0; i < planes.Length; ++i) + { + float x = planes[i].Normal.X; + float y = planes[i].Normal.Y; + float z = planes[i].Normal.Z; + + /* + * Note: + * Factor common arithmetic out of loop. + */ + planes[i].Normal.X = ((x * ((1.0f - yy) - zz)) + (y * (xy - wz))) + (z * (xz + wy)); + planes[i].Normal.Y = ((x * (xy + wz)) + (y * ((1.0f - xx) - zz))) + (z * (yz - wx)); + planes[i].Normal.Z = ((x * (xz - wy)) + (y * (yz + wx))) + (z * ((1.0f - xx) - yy)); + } + } + + /// + /// Transforms a normalized plane by a matrix. + /// + /// The normalized source plane. + /// The transformation matrix. + /// When the method completes, contains the transformed plane. + public static void Transform(ref Plane plane, ref Matrix transformation, out Plane result) + { + float x = plane.Normal.X; + float y = plane.Normal.Y; + float z = plane.Normal.Z; + float d = plane.D; + + Matrix inverse; + Matrix.Invert(ref transformation, out inverse); + + result.Normal.X = (((x * inverse.M11) + (y * inverse.M12)) + (z * inverse.M13)) + (d * inverse.M14); + result.Normal.Y = (((x * inverse.M21) + (y * inverse.M22)) + (z * inverse.M23)) + (d * inverse.M24); + result.Normal.Z = (((x * inverse.M31) + (y * inverse.M32)) + (z * inverse.M33)) + (d * inverse.M34); + result.D = (((x * inverse.M41) + (y * inverse.M42)) + (z * inverse.M43)) + (d * inverse.M44); + } + + /// + /// Transforms a normalized plane by a matrix. + /// + /// The normalized source plane. + /// The transformation matrix. + /// When the method completes, contains the transformed plane. + public static Plane Transform(Plane plane, Matrix transformation) + { + Plane result; + float x = plane.Normal.X; + float y = plane.Normal.Y; + float z = plane.Normal.Z; + float d = plane.D; + + transformation.Invert(); + result.Normal.X = (((x * transformation.M11) + (y * transformation.M12)) + (z * transformation.M13)) + (d * transformation.M14); + result.Normal.Y = (((x * transformation.M21) + (y * transformation.M22)) + (z * transformation.M23)) + (d * transformation.M24); + result.Normal.Z = (((x * transformation.M31) + (y * transformation.M32)) + (z * transformation.M33)) + (d * transformation.M34); + result.D = (((x * transformation.M41) + (y * transformation.M42)) + (z * transformation.M43)) + (d * transformation.M44); + + return result; + } + + /// + /// Transforms an array of normalized planes by a matrix. + /// + /// The array of normalized planes to transform. + /// The transformation matrix. + /// Thrown when is null. + public static void Transform(Plane[] planes, ref Matrix transformation) + { + if (planes == null) + throw new ArgumentNullException("planes"); + + Matrix inverse; + Matrix.Invert(ref transformation, out inverse); + + for (int i = 0; i < planes.Length; ++i) + { + Transform(ref planes[i], ref transformation, out planes[i]); + } + } + + /// + /// Scales a plane by the given value. + /// + /// The amount by which to scale the plane. + /// The plane to scale. + /// The scaled plane. + public static Plane operator *(float scale, Plane plane) + { + return new Plane(plane.Normal.X * scale, plane.Normal.Y * scale, plane.Normal.Z * scale, plane.D * scale); + } + + /// + /// Scales a plane by the given value. + /// + /// The plane to scale. + /// The amount by which to scale the plane. + /// The scaled plane. + public static Plane operator *(Plane plane, float scale) + { + return new Plane(plane.Normal.X * scale, plane.Normal.Y * scale, plane.Normal.Z * scale, plane.D * scale); + } + + /// + /// Negates a plane by negating all its coefficients, which result in a plane in opposite direction. + /// + /// The negated plane. + public static Plane operator -(Plane plane) + { + return new Plane(-plane.Normal.X, -plane.Normal.Y, -plane.Normal.Z, -plane.D); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Plane left, Plane right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Plane left, Plane right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "A:{0} B:{1} C:{2} D:{3}", Normal.X, Normal.Y, Normal.Z, D); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return string.Format(CultureInfo.CurrentCulture, "A:{0} B:{1} C:{2} D:{3}", Normal.X.ToString(format, CultureInfo.CurrentCulture), + Normal.Y.ToString(format, CultureInfo.CurrentCulture), Normal.Z.ToString(format, CultureInfo.CurrentCulture), D.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "A:{0} B:{1} C:{2} D:{3}", Normal.X, Normal.Y, Normal.Z, D); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + return string.Format(formatProvider, "A:{0} B:{1} C:{2} D:{3}", Normal.X.ToString(format, formatProvider), + Normal.Y.ToString(format, formatProvider), Normal.Z.ToString(format, formatProvider), D.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return Normal.GetHashCode() + D.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Plane value) + { + return Normal == value.Normal && D == value.D; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Plane)value); + } + +#if SlimDX1xInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator SlimDX.Plane(Plane value) + { + return new SlimDX.Plane(value.Normal, value.D); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Plane(SlimDX.Plane value) + { + return new Plane(value.Normal, value.D); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Plane(Plane value) + { + return new Microsoft.Xna.Framework.Plane(value.Normal, value.D); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Plane(Microsoft.Xna.Framework.Plane value) + { + return new Plane(value.Normal, value.D); + } +#endif + } +} diff --git a/math/PlaneIntersectionType.cs b/math/PlaneIntersectionType.cs new file mode 100644 index 0000000..c863bed --- /dev/null +++ b/math/PlaneIntersectionType.cs @@ -0,0 +1,57 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +namespace math +{ + /* + * The enumerations defined in this file are in alphabetical order. When + * adding new enumerations or renaming existing ones, please make sure + * the ordering is maintained. + */ + + /// + /// Describes the result of an intersection with a plane in three dimensions. + /// + public enum PlaneIntersectionType + { + /// + /// The object is behind the plane. + /// + Back, + + /// + /// The object is in front of the plane. + /// + Front, + + /// + /// The object is intersecting the plane. + /// + Intersecting, + } +} diff --git a/math/Point.cs b/math/Point.cs new file mode 100644 index 0000000..f862f12 --- /dev/null +++ b/math/Point.cs @@ -0,0 +1,147 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.Runtime.InteropServices; +using Xenko.Core.Serialization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// A 2D point. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Point : IEquatable + { + /// + /// A point with (0,0) coordinates. + /// + public static readonly Point Zero = new Point(0, 0); + + /// + /// Initializes a new instance of the struct. + /// + /// The x. + /// The y. + public Point(int x, int y) + { + X = x; + Y = y; + } + + /// + /// Left coordinate. + /// + [DataMember( Order = 0 )] + public int X; + + /// + /// Top coordinate. + /// + [DataMember( Order = 1 )] + public int Y; + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Point other) + { + return other.X == X && other.Y == Y; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof(Point)) return false; + return Equals((Point)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + return (X * 397) ^ Y; + } + } + + /// + /// Implements the operator ==. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static bool operator ==(Point left, Point right) + { + return left.Equals(right); + } + + /// + /// Implements the operator !=. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static bool operator !=(Point left, Point right) + { + return !left.Equals(right); + } + + /// + public override string ToString() + { + return string.Format("({0},{1})", X, Y); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Point(Vec2 value) + { + return new Point((int)value.X, (int)value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vec2(Point value) + { + return new Vec2(value.X, value.Y); + } + } +} diff --git a/math/Properties/AssemblyInfo.cs b/math/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a507540 --- /dev/null +++ b/math/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System.Reflection; +using System.Runtime.CompilerServices; + +#pragma warning disable 436 // Xenko.PublicKeys is defined in multiple assemblies + +[assembly: InternalsVisibleTo("math.Serializers" + Xenko.PublicKeys.Default)] diff --git a/math/Quaternion.cs b/math/Quaternion.cs new file mode 100644 index 0000000..e2f5839 --- /dev/null +++ b/math/Quaternion.cs @@ -0,0 +1,1424 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a four dimensional mathematical quaternion. + /// + [DataContract( Name = "quaternion")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Quaternion : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Quaternion Zero = new Quaternion(); + + /// + /// A with all of its components set to one. + /// + public static readonly Quaternion One = new Quaternion(1.0f, 1.0f, 1.0f, 1.0f); + + /// + /// The identity (0, 0, 0, 1). + /// + public static readonly Quaternion Identity = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); + + /// + /// The X component of the quaternion. + /// + public float X; + + /// + /// The Y component of the quaternion. + /// + public float Y; + + /// + /// The Z component of the quaternion. + /// + public float Z; + + /// + /// The W component of the quaternion. + /// + public float W; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Quaternion(float value) + { + X = value; + Y = value; + Z = value; + W = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the components. + public Quaternion(Vector4 value) + { + X = value.X; + Y = value.Y; + Z = value.Z; + W = value.W; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X, Y, and Z components. + /// Initial value for the W component of the quaternion. + public Quaternion(Vector3 value, float w) + { + X = value.X; + Y = value.Y; + Z = value.Z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + /// Initial value for the Z component of the quaternion. + /// Initial value for the W component of the quaternion. + public Quaternion(Vec2 value, float z, float w) + { + X = value.X; + Y = value.Y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the quaternion. + /// Initial value for the Y component of the quaternion. + /// Initial value for the Z component of the quaternion. + /// Initial value for the W component of the quaternion. + public Quaternion(float x, float y, float z, float w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, Z, and W components of the quaternion. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Quaternion(float[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 4) + throw new ArgumentOutOfRangeException("values", "There must be four and only four input values for Quaternion."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + W = values[3]; + } + + /// + /// Gets a value indicating whether this instance is equivalent to the identity quaternion. + /// + /// + /// true if this instance is an identity quaternion; otherwise, false. + /// + public bool IsIdentity + { + get { return this.Equals(Identity); } + } + + /// + /// Gets a value indicting whether this instance is normalized. + /// + public bool IsNormalized + { + get { return Math.Abs((X * X) + (Y * Y) + (Z * Z) + (W * W) - 1f) < MathUtil.ZeroTolerance; } + } + + /// + /// Gets the angle of the quaternion. + /// + /// The quaternion's angle. + public float Angle + { + get + { + float length = (X * X) + (Y * Y) + (Z * Z); + if (length < MathUtil.ZeroTolerance) + return 0.0f; + + return (float)(2.0 * Math.Acos(W)); + } + } + + /// + /// Gets the axis components of the quaternion. + /// + /// The axis components of the quaternion. + public Vector3 Axis + { + get + { + float length = (X * X) + (Y * Y) + (Z * Z); + if (length < MathUtil.ZeroTolerance) + return Vector3.UnitX; + + float inv = 1.0f / length; + return new Vector3(X * inv, Y * inv, Z * inv); + } + } + + /// + /// Gets yaw/pitch/roll equivalent of the quaternion + /// + public Vector3 YawPitchRoll + { + get + { + Vector3 yawPitchRoll; + RotationYawPitchRoll(ref this, out yawPitchRoll.X, out yawPitchRoll.Y, out yawPitchRoll.Z); + return yawPitchRoll; + } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, Z, or W component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + case 3: return W; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Quaternion run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + case 3: W = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Quaternion run from 0 to 3, inclusive."); + } + } + } + + /// + /// Conjugates the quaternion. + /// + public void Conjugate() + { + X = -X; + Y = -Y; + Z = -Z; + } + + /// + /// Conjugates and renormalizes the quaternion. + /// + public void Invert() + { + float lengthSq = LengthSquared(); + if (lengthSq > MathUtil.ZeroTolerance) + { + lengthSq = 1.0f / lengthSq; + + X = -X * lengthSq; + Y = -Y * lengthSq; + Z = -Z * lengthSq; + W = W * lengthSq; + } + } + + /// + /// Calculates the length of the quaternion. + /// + /// The length of the quaternion. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + public float Length() + { + return (float)Math.Sqrt((X * X) + (Y * Y) + (Z * Z) + (W * W)); + } + + /// + /// Calculates the squared length of the quaternion. + /// + /// The squared length of the quaternion. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + public float LengthSquared() + { + return (X * X) + (Y * Y) + (Z * Z) + (W * W); + } + + /// + /// Converts the quaternion into a unit quaternion. + /// + public void Normalize() + { + float length = Length(); + if (length > MathUtil.ZeroTolerance) + { + float inverse = 1.0f / length; + X *= inverse; + Y *= inverse; + Z *= inverse; + W *= inverse; + } + } + + /// + /// Creates an array containing the elements of the quaternion. + /// + /// A four-element array containing the components of the quaternion. + public float[] ToArray() + { + return new float[] { X, Y, Z, W }; + } + + /// + /// Adds two quaternions. + /// + /// The first quaternion to add. + /// The second quaternion to add. + /// When the method completes, contains the sum of the two quaternions. + public static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result) + { + result.X = left.X + right.X; + result.Y = left.Y + right.Y; + result.Z = left.Z + right.Z; + result.W = left.W + right.W; + } + + /// + /// Adds two quaternions. + /// + /// The first quaternion to add. + /// The second quaternion to add. + /// The sum of the two quaternions. + public static Quaternion Add(Quaternion left, Quaternion right) + { + Quaternion result; + Add(ref left, ref right, out result); + return result; + } + + /// + /// Subtracts two quaternions. + /// + /// The first quaternion to subtract. + /// The second quaternion to subtract. + /// When the method completes, contains the difference of the two quaternions. + public static void Subtract(ref Quaternion left, ref Quaternion right, out Quaternion result) + { + result.X = left.X - right.X; + result.Y = left.Y - right.Y; + result.Z = left.Z - right.Z; + result.W = left.W - right.W; + } + + /// + /// Subtracts two quaternions. + /// + /// The first quaternion to subtract. + /// The second quaternion to subtract. + /// The difference of the two quaternions. + public static Quaternion Subtract(Quaternion left, Quaternion right) + { + Quaternion result; + Subtract(ref left, ref right, out result); + return result; + } + + /// + /// Scales a quaternion by the given value. + /// + /// The quaternion to scale. + /// The amount by which to scale the quaternion. + /// When the method completes, contains the scaled quaternion. + public static void Multiply(ref Quaternion value, float scale, out Quaternion result) + { + result.X = value.X * scale; + result.Y = value.Y * scale; + result.Z = value.Z * scale; + result.W = value.W * scale; + } + + /// + /// Scales a quaternion by the given value. + /// + /// The quaternion to scale. + /// The amount by which to scale the quaternion. + /// The scaled quaternion. + public static Quaternion Multiply(Quaternion value, float scale) + { + Quaternion result; + Multiply(ref value, scale, out result); + return result; + } + + /// + /// Modulates a quaternion by another. + /// + /// The first quaternion to modulate. + /// The second quaternion to modulate. + /// When the moethod completes, contains the modulated quaternion. + public static void Multiply(ref Quaternion left, ref Quaternion right, out Quaternion result) + { + float lx = left.X; + float ly = left.Y; + float lz = left.Z; + float lw = left.W; + float rx = right.X; + float ry = right.Y; + float rz = right.Z; + float rw = right.W; + + result.X = (rx * lw + lx * rw + ry * lz) - (rz * ly); + result.Y = (ry * lw + ly * rw + rz * lx) - (rx * lz); + result.Z = (rz * lw + lz * rw + rx * ly) - (ry * lx); + result.W = (rw * lw) - (rx * lx + ry * ly + rz * lz); + } + + /// + /// Modulates a quaternion by another. + /// + /// The first quaternion to modulate. + /// The second quaternion to modulate. + /// The modulated quaternion. + public static Quaternion Multiply(Quaternion left, Quaternion right) + { + Quaternion result; + Multiply(ref left, ref right, out result); + return result; + } + + /// + /// Reverses the direction of a given quaternion. + /// + /// The quaternion to negate. + /// When the method completes, contains a quaternion facing in the opposite direction. + public static void Negate(ref Quaternion value, out Quaternion result) + { + result.X = -value.X; + result.Y = -value.Y; + result.Z = -value.Z; + result.W = -value.W; + } + + /// + /// Reverses the direction of a given quaternion. + /// + /// The quaternion to negate. + /// A quaternion facing in the opposite direction. + public static Quaternion Negate(Quaternion value) + { + Quaternion result; + Negate(ref value, out result); + return result; + } + + /// + /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. + /// + /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// When the method completes, contains a new containing the 4D Cartesian coordinates of the specified point. + public static void Barycentric(ref Quaternion value1, ref Quaternion value2, ref Quaternion value3, float amount1, float amount2, out Quaternion result) + { + Quaternion start, end; + Slerp(ref value1, ref value2, amount1 + amount2, out start); + Slerp(ref value1, ref value3, amount1 + amount2, out end); + Slerp(ref start, ref end, amount2 / (amount1 + amount2), out result); + } + + /// + /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. + /// + /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// A new containing the 4D Cartesian coordinates of the specified point. + public static Quaternion Barycentric(Quaternion value1, Quaternion value2, Quaternion value3, float amount1, float amount2) + { + Quaternion result; + Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); + return result; + } + + /// + /// Conjugates a quaternion. + /// + /// The quaternion to conjugate. + /// When the method completes, contains the conjugated quaternion. + public static void Conjugate(ref Quaternion value, out Quaternion result) + { + result.X = -value.X; + result.Y = -value.Y; + result.Z = -value.Z; + result.W = value.W; + } + + /// + /// Conjugates a quaternion. + /// + /// The quaternion to conjugate. + /// The conjugated quaternion. + public static Quaternion Conjugate(Quaternion value) + { + Quaternion result; + Conjugate(ref value, out result); + return result; + } + + /// + /// Calculates the dot product of two quaternions. + /// + /// First source quaternion. + /// Second source quaternion. + /// When the method completes, contains the dot product of the two quaternions. + public static void Dot(ref Quaternion left, ref Quaternion right, out float result) + { + result = (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z) + (left.W * right.W); + } + + /// + /// Calculates the dot product of two quaternions. + /// + /// First source quaternion. + /// Second source quaternion. + /// The dot product of the two quaternions. + public static float Dot(Quaternion left, Quaternion right) + { + return (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z) + (left.W * right.W); + } + + /// + /// Exponentiates a quaternion. + /// + /// The quaternion to exponentiate. + /// When the method completes, contains the exponentiated quaternion. + public static void Exponential(ref Quaternion value, out Quaternion result) + { + float angle = (float)Math.Sqrt((value.X * value.X) + (value.Y * value.Y) + (value.Z * value.Z)); + float sin = (float)Math.Sin(angle); + + if (Math.Abs(sin) >= MathUtil.ZeroTolerance) + { + float coeff = sin / angle; + result.X = coeff * value.X; + result.Y = coeff * value.Y; + result.Z = coeff * value.Z; + } + else + { + result = value; + } + + result.W = (float)Math.Cos(angle); + } + + /// + /// Exponentiates a quaternion. + /// + /// The quaternion to exponentiate. + /// The exponentiated quaternion. + public static Quaternion Exponential(Quaternion value) + { + Quaternion result; + Exponential(ref value, out result); + return result; + } + + /// + /// Conjugates and renormalizes the quaternion. + /// + /// The quaternion to conjugate and renormalize. + /// When the method completes, contains the conjugated and renormalized quaternion. + public static void Invert(ref Quaternion value, out Quaternion result) + { + result = value; + result.Invert(); + } + + /// + /// Conjugates and renormalizes the quaternion. + /// + /// The quaternion to conjugate and renormalize. + /// The conjugated and renormalized quaternion. + public static Quaternion Invert(Quaternion value) + { + Quaternion result; + Invert(ref value, out result); + return result; + } + + /// + /// Performs a linear interpolation between two quaternions. + /// + /// Start quaternion. + /// End quaternion. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two quaternions. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Quaternion start, ref Quaternion end, float amount, out Quaternion result) + { + float inverse = 1.0f - amount; + + if (Dot(start, end) >= 0.0f) + { + result.X = (inverse * start.X) + (amount * end.X); + result.Y = (inverse * start.Y) + (amount * end.Y); + result.Z = (inverse * start.Z) + (amount * end.Z); + result.W = (inverse * start.W) + (amount * end.W); + } + else + { + result.X = (inverse * start.X) - (amount * end.X); + result.Y = (inverse * start.Y) - (amount * end.Y); + result.Z = (inverse * start.Z) - (amount * end.Z); + result.W = (inverse * start.W) - (amount * end.W); + } + + result.Normalize(); + } + + /// + /// Performs a linear interpolation between two quaternion. + /// + /// Start quaternion. + /// End quaternion. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two quaternions. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Quaternion Lerp(Quaternion start, Quaternion end, float amount) + { + Quaternion result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Calculates the natural logarithm of the specified quaternion. + /// + /// The quaternion whose logarithm will be calculated. + /// When the method completes, contains the natural logarithm of the quaternion. + public static void Logarithm(ref Quaternion value, out Quaternion result) + { + if (Math.Abs(value.W) < 1.0) + { + float angle = (float)Math.Acos(value.W); + float sin = (float)Math.Sin(angle); + + if (Math.Abs(sin) >= MathUtil.ZeroTolerance) + { + float coeff = angle / sin; + result.X = value.X * coeff; + result.Y = value.Y * coeff; + result.Z = value.Z * coeff; + } + else + { + result = value; + } + } + else + { + result = value; + } + + result.W = 0.0f; + } + + /// + /// Calculates the natural logarithm of the specified quaternion. + /// + /// The quaternion whose logarithm will be calculated. + /// The natural logarithm of the quaternion. + public static Quaternion Logarithm(Quaternion value) + { + Quaternion result; + Logarithm(ref value, out result); + return result; + } + + /// + /// Converts the quaternion into a unit quaternion. + /// + /// The quaternion to normalize. + /// When the method completes, contains the normalized quaternion. + public static void Normalize(ref Quaternion value, out Quaternion result) + { + Quaternion temp = value; + result = temp; + result.Normalize(); + } + + /// + /// Converts the quaternion into a unit quaternion. + /// + /// The quaternion to normalize. + /// The normalized quaternion. + public static Quaternion Normalize(Quaternion value) + { + value.Normalize(); + return value; + } + + /// + /// Rotates a Vector3 by the specified quaternion rotation. + /// + /// The vector to rotate. + public void Rotate(ref Vector3 vector) + { + var pureQuaternion = new Quaternion(vector, 0); + pureQuaternion = Conjugate(this) * pureQuaternion * this; + + vector.X = pureQuaternion.X; + vector.Y = pureQuaternion.Y; + vector.Z = pureQuaternion.Z; + } + + /// + /// Creates a quaternion given a rotation and an axis. + /// + /// The axis of rotation. + /// The angle of rotation. + /// When the method completes, contains the newly created quaternion. + public static void RotationAxis(ref Vector3 axis, float angle, out Quaternion result) + { + Vector3 normalized; + Vector3.Normalize(ref axis, out normalized); + + float half = angle * 0.5f; + float sin = (float)Math.Sin(half); + float cos = (float)Math.Cos(half); + + result.X = normalized.X * sin; + result.Y = normalized.Y * sin; + result.Z = normalized.Z * sin; + result.W = cos; + } + + /// + /// Creates a quaternion given a rotation and an axis. + /// + /// The axis of rotation. + /// The angle of rotation. + /// The newly created quaternion. + public static Quaternion RotationAxis(Vector3 axis, float angle) + { + Quaternion result; + RotationAxis(ref axis, angle, out result); + return result; + } + + /// + /// Creates a quaternion given a rotation matrix. + /// + /// The rotation matrix. + /// When the method completes, contains the newly created quaternion. + public static void RotationMatrix(ref Matrix matrix, out Quaternion result) + { + float sqrt; + float half; + float scale = matrix.M11 + matrix.M22 + matrix.M33; + + if (scale > 0.0f) + { + sqrt = (float)Math.Sqrt(scale + 1.0f); + result.W = sqrt * 0.5f; + sqrt = 0.5f / sqrt; + + result.X = (matrix.M23 - matrix.M32) * sqrt; + result.Y = (matrix.M31 - matrix.M13) * sqrt; + result.Z = (matrix.M12 - matrix.M21) * sqrt; + } + else if ((matrix.M11 >= matrix.M22) && (matrix.M11 >= matrix.M33)) + { + sqrt = (float)Math.Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33); + half = 0.5f / sqrt; + + result.X = 0.5f * sqrt; + result.Y = (matrix.M12 + matrix.M21) * half; + result.Z = (matrix.M13 + matrix.M31) * half; + result.W = (matrix.M23 - matrix.M32) * half; + } + else if (matrix.M22 > matrix.M33) + { + sqrt = (float)Math.Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33); + half = 0.5f / sqrt; + + result.X = (matrix.M21 + matrix.M12) * half; + result.Y = 0.5f * sqrt; + result.Z = (matrix.M32 + matrix.M23) * half; + result.W = (matrix.M31 - matrix.M13) * half; + } + else + { + sqrt = (float)Math.Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22); + half = 0.5f / sqrt; + + result.X = (matrix.M31 + matrix.M13) * half; + result.Y = (matrix.M32 + matrix.M23) * half; + result.Z = 0.5f * sqrt; + result.W = (matrix.M12 - matrix.M21) * half; + } + } + + /// + /// Creates a quaternion given a rotation matrix. + /// + /// The rotation matrix. + /// The newly created quaternion. + public static Quaternion RotationMatrix(Matrix matrix) + { + Quaternion result; + RotationMatrix(ref matrix, out result); + return result; + } + + /// + /// Creates a quaternion that rotates around the x-axis. + /// + /// Angle of rotation in radians. + /// When the method completes, contains the newly created quaternion. + public static void RotationX(float angle, out Quaternion result) + { + float halfAngle = angle * 0.5f; + result = new Quaternion((float)Math.Sin(halfAngle), 0.0f, 0.0f, (float)Math.Cos(halfAngle)); + } + + /// + /// Creates a quaternion that rotates around the x-axis. + /// + /// Angle of rotation in radians. + /// The created rotation quaternion. + public static Quaternion RotationX(float angle) + { + Quaternion result; + RotationX(angle, out result); + return result; + } + + /// + /// Creates a quaternion that rotates around the y-axis. + /// + /// Angle of rotation in radians. + /// When the method completes, contains the newly created quaternion. + public static void RotationY(float angle, out Quaternion result) + { + float halfAngle = angle * 0.5f; + result = new Quaternion(0.0f, (float)Math.Sin(halfAngle), 0.0f, (float)Math.Cos(halfAngle)); + } + + /// + /// Creates a quaternion that rotates around the y-axis. + /// + /// Angle of rotation in radians. + /// The created rotation quaternion. + public static Quaternion RotationY(float angle) + { + Quaternion result; + RotationY(angle, out result); + return result; + } + + /// + /// Creates a quaternion that rotates around the z-axis. + /// + /// Angle of rotation in radians. + /// When the method completes, contains the newly created quaternion. + public static void RotationZ(float angle, out Quaternion result) + { + float halfAngle = angle * 0.5f; + result = new Quaternion(0.0f, 0.0f, (float)Math.Sin(halfAngle), (float)Math.Cos(halfAngle)); + } + + /// + /// Creates a quaternion that rotates around the z-axis. + /// + /// Angle of rotation in radians. + /// The created rotation quaternion. + public static Quaternion RotationZ(float angle) + { + Quaternion result; + RotationZ(angle, out result); + return result; + } + + /// + /// Calculate the yaw/pitch/roll rotation equivalent to the provided quaternion. + /// + /// The input quaternion + /// The yaw component + /// The pitch component + /// The roll component + public static void RotationYawPitchRoll(ref Quaternion rotation, out float yaw, out float pitch, out float roll) + { + // Equivalent to: + // Matrix rotationMatrix; + // Matrix.Rotation(ref cachedRotation, out rotationMatrix); + // rotationMatrix.DecomposeXYZ(out rotationEuler); + + var xx = rotation.X * rotation.X; + var yy = rotation.Y * rotation.Y; + var zz = rotation.Z * rotation.Z; + var xy = rotation.X * rotation.Y; + var zw = rotation.Z * rotation.W; + var zx = rotation.Z * rotation.X; + var yw = rotation.Y * rotation.W; + var yz = rotation.Y * rotation.Z; + var xw = rotation.X * rotation.W; + + pitch = (float)Math.Asin(2.0f * (xw - yz)); + double test = Math.Cos(pitch); + if (test > MathUtil.ZeroTolerance) + { + roll = (float)Math.Atan2(2.0f * (xy + zw), 1.0f - (2.0f * (zz + xx))); + yaw = (float)Math.Atan2(2.0f * (zx + yw), 1.0f - (2.0f * (yy + xx))); + } + else + { + roll = (float)Math.Atan2(-2.0f * (xy - zw), 1.0f - (2.0f * (yy + zz))); + yaw = 0.0f; + } + } + + /// + /// Creates a quaternion given a yaw, pitch, and roll value (angles in radians). + /// + /// The yaw of rotation in radians. + /// The pitch of rotation in radians. + /// The roll of rotation in radians. + /// When the method completes, contains the newly created quaternion. + public static void RotationYawPitchRoll(float yaw, float pitch, float roll, out Quaternion result) + { + var halfRoll = roll * 0.5f; + var halfPitch = pitch * 0.5f; + var halfYaw = yaw * 0.5f; + + var sinRoll = (float)Math.Sin(halfRoll); + var cosRoll = (float)Math.Cos(halfRoll); + var sinPitch = (float)Math.Sin(halfPitch); + var cosPitch = (float)Math.Cos(halfPitch); + var sinYaw = (float)Math.Sin(halfYaw); + var cosYaw = (float)Math.Cos(halfYaw); + + var cosYawPitch = cosYaw * cosPitch; + var sinYawPitch = sinYaw * sinPitch; + + result.X = (cosYaw * sinPitch * cosRoll) + (sinYaw * cosPitch * sinRoll); + result.Y = (sinYaw * cosPitch * cosRoll) - (cosYaw * sinPitch * sinRoll); + result.Z = (cosYawPitch * sinRoll) - (sinYawPitch * cosRoll); + result.W = (cosYawPitch * cosRoll) + (sinYawPitch * sinRoll); + } + + /// + /// Creates a quaternion given a yaw, pitch, and roll value. + /// + /// The yaw of rotation. + /// The pitch of rotation. + /// The roll of rotation. + /// The newly created quaternion. + public static Quaternion RotationYawPitchRoll(float yaw, float pitch, float roll) + { + Quaternion result; + RotationYawPitchRoll(yaw, pitch, roll, out result); + return result; + } + + /// + /// Computes a quaternion corresponding to the rotation transforming the vector to the vector . + /// + /// The source vector of the transformation. + /// The target vector of the transformation. + /// The resulting quaternion corresponding to the transformation of the source vector to the target vector. + public static Quaternion BetweenDirections(Vector3 source, Vector3 target) + { + Quaternion result; + BetweenDirections(ref source, ref target, out result); + return result; + } + + /// + /// Computes a quaternion corresponding to the rotation transforming the vector to the vector . + /// + /// The source vector of the transformation. + /// The target vector of the transformation. + /// The resulting quaternion corresponding to the transformation of the source vector to the target vector. + public static void BetweenDirections(ref Vector3 source, ref Vector3 target, out Quaternion result) + { + var norms = (float)Math.Sqrt(source.LengthSquared() * target.LengthSquared()); + var real = norms + Vector3.Dot(source, target); + if (real < MathUtil.ZeroTolerance * norms) + { + // If source and target are exactly opposite, rotate 180 degrees around an arbitrary orthogonal axis. + // Axis normalisation can happen later, when we normalise the quaternion. + result = Math.Abs(source.X) > Math.Abs(source.Z) + ? new Quaternion(-source.Y, source.X, 0.0f, 0.0f) + : new Quaternion(0.0f, -source.Z, source.Y, 0.0f); + } + else + { + // Otherwise, build quaternion the standard way. + var axis = Vector3.Cross(source, target); + result = new Quaternion(axis, real); + } + result.Normalize(); + } + + /// + /// Interpolates between two quaternions, using spherical linear interpolation. + /// + /// Start quaternion. + /// End quaternion. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the spherical linear interpolation of the two quaternions. + public static void Slerp(ref Quaternion start, ref Quaternion end, float amount, out Quaternion result) + { + float opposite; + float inverse; + float dot = Dot(start, end); + + if (Math.Abs(dot) > 1.0f - MathUtil.ZeroTolerance) + { + inverse = 1.0f - amount; + opposite = amount * Math.Sign(dot); + } + else + { + float acos = (float)Math.Acos(Math.Abs(dot)); + float invSin = (float)(1.0 / Math.Sin(acos)); + + inverse = (float)Math.Sin((1.0f - amount) * acos) * invSin; + opposite = (float)Math.Sin(amount * acos) * invSin * Math.Sign(dot); + } + + result.X = (inverse * start.X) + (opposite * end.X); + result.Y = (inverse * start.Y) + (opposite * end.Y); + result.Z = (inverse * start.Z) + (opposite * end.Z); + result.W = (inverse * start.W) + (opposite * end.W); + } + + /// + /// Interpolates between two quaternions, using spherical linear interpolation. + /// + /// Start quaternion. + /// End quaternion. + /// Value between 0 and 1 indicating the weight of . + /// The spherical linear interpolation of the two quaternions. + public static Quaternion Slerp(Quaternion start, Quaternion end, float amount) + { + Quaternion result; + Slerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Interpolates between quaternions, using spherical quadrangle interpolation. + /// + /// First source quaternion. + /// Second source quaternion. + /// Thrid source quaternion. + /// Fourth source quaternion. + /// Value between 0 and 1 indicating the weight of interpolation. + /// When the method completes, contains the spherical quadrangle interpolation of the quaternions. + public static void Squad(ref Quaternion value1, ref Quaternion value2, ref Quaternion value3, ref Quaternion value4, float amount, out Quaternion result) + { + Quaternion start, end; + Slerp(ref value1, ref value4, amount, out start); + Slerp(ref value2, ref value3, amount, out end); + Slerp(ref start, ref end, 2.0f * amount * (1.0f - amount), out result); + } + + /// + /// Interpolates between quaternions, using spherical quadrangle interpolation. + /// + /// First source quaternion. + /// Second source quaternion. + /// Thrid source quaternion. + /// Fourth source quaternion. + /// Value between 0 and 1 indicating the weight of interpolation. + /// The spherical quadrangle interpolation of the quaternions. + public static Quaternion Squad(Quaternion value1, Quaternion value2, Quaternion value3, Quaternion value4, float amount) + { + Quaternion result; + Squad(ref value1, ref value2, ref value3, ref value4, amount, out result); + return result; + } + + /// + /// Sets up control points for spherical quadrangle interpolation. + /// + /// First source quaternion. + /// Second source quaternion. + /// Third source quaternion. + /// Fourth source quaternion. + /// An array of three quaternions that represent control points for spherical quadrangle interpolation. + public static Quaternion[] SquadSetup(Quaternion value1, Quaternion value2, Quaternion value3, Quaternion value4) + { + Quaternion q0 = (value1 + value2).LengthSquared() < (value1 - value2).LengthSquared() ? -value1 : value1; + Quaternion q2 = (value2 + value3).LengthSquared() < (value2 - value3).LengthSquared() ? -value3 : value3; + Quaternion q3 = (value3 + value4).LengthSquared() < (value3 - value4).LengthSquared() ? -value4 : value4; + Quaternion q1 = value2; + + Quaternion q1Exp, q2Exp; + Exponential(ref q1, out q1Exp); + Exponential(ref q2, out q2Exp); + + Quaternion[] results = new Quaternion[3]; + results[0] = q1 * Exponential(-0.25f * (Logarithm(q1Exp * q2) + Logarithm(q1Exp * q0))); + results[1] = q2 * Exponential(-0.25f * (Logarithm(q2Exp * q3) + Logarithm(q2Exp * q1))); + results[2] = q2; + + return results; + } + + /// + /// Adds two quaternions. + /// + /// The first quaternion to add. + /// The second quaternion to add. + /// The sum of the two quaternions. + public static Quaternion operator +(Quaternion left, Quaternion right) + { + Quaternion result; + Add(ref left, ref right, out result); + return result; + } + + /// + /// Subtracts two quaternions. + /// + /// The first quaternion to subtract. + /// The second quaternion to subtract. + /// The difference of the two quaternions. + public static Quaternion operator -(Quaternion left, Quaternion right) + { + Quaternion result; + Subtract(ref left, ref right, out result); + return result; + } + + /// + /// Reverses the direction of a given quaternion. + /// + /// The quaternion to negate. + /// A quaternion facing in the opposite direction. + public static Quaternion operator -(Quaternion value) + { + Quaternion result; + Negate(ref value, out result); + return result; + } + + /// + /// Scales a quaternion by the given value. + /// + /// The quaternion to scale. + /// The amount by which to scale the quaternion. + /// The scaled quaternion. + public static Quaternion operator *(float scale, Quaternion value) + { + Quaternion result; + Multiply(ref value, scale, out result); + return result; + } + + /// + /// Scales a quaternion by the given value. + /// + /// The quaternion to scale. + /// The amount by which to scale the quaternion. + /// The scaled quaternion. + public static Quaternion operator *(Quaternion value, float scale) + { + Quaternion result; + Multiply(ref value, scale, out result); + return result; + } + + /// + /// Multiplies a quaternion by another. + /// + /// The first quaternion to multiply. + /// The second quaternion to multiply. + /// The multiplied quaternion. + public static Quaternion operator *(Quaternion left, Quaternion right) + { + Quaternion result; + Multiply(ref left, ref right, out result); + return result; + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Quaternion left, Quaternion right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Quaternion left, Quaternion right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture), W.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider), W.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Quaternion other) + { + return ((float)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Z - Z) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.W - W) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Quaternion)value); + } + +#if SlimDX1xInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator SlimDX.Quaternion(Quaternion value) + { + return new SlimDX.Quaternion(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Quaternion(SlimDX.Quaternion value) + { + return new Quaternion(value.X, value.Y, value.Z, value.W); + } +#endif + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Quaternion(Quaternion value) + { + return new System.Windows.Media.Media3D.Quaternion(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Quaternion(System.Windows.Media.Media3D.Quaternion value) + { + return new Quaternion((float)value.X, (float)value.Y, (float)value.Z, (float)value.W); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Quaternion(Quaternion value) + { + return new Microsoft.Xna.Framework.Quaternion(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Quaternion(Microsoft.Xna.Framework.Quaternion value) + { + return new Quaternion(value.X, value.Y, value.Z, value.W); + } +#endif + } +} diff --git a/math/RandomSeed.cs b/math/RandomSeed.cs new file mode 100644 index 0000000..b14b01e --- /dev/null +++ b/math/RandomSeed.cs @@ -0,0 +1,61 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. + +// Random numbers which also allow creation of random values in the shaders and are deterministic +// Based on this article: +// http://martindevans.me/game-development/2015/02/22/Random-Gibberish/ + +using System; + +namespace math +{ + /// + /// The is a structure for deterministically acquiring random values. + /// One should be able to reproduce the same pseudo-random value for a fixed offset, but + /// provide enough random distribution for different offsets or different random seeds + /// Although other methods exist, the current implementation can easily be replicated in the shaders if required + /// + public struct RandomSeed + { + private const double GelfondConst = 23.1406926327792690; // e to the power of Pi = (-1) to the power of -i + private const double GelfondSchneiderConst = 2.6651441426902251; // 2 to the power of sqrt(2) + private const double Numerator = 123456789; + + // When casting uint to double it works fine, but when casting it to float it might cause underflow errors (loss of precision) + // We want to limit the maximum settable value to prevent such errors. + private const uint UnderflowGuard = 0xFFFF; + + private readonly uint seed; + + /// + /// Initializes a new instance of the struct from a target uint. + /// + /// The seed value to initialize the deterministic random generator. + public RandomSeed(uint seed) + { + this.seed = (seed & UnderflowGuard); + } + + /// + /// Get a deterministic double value between 0 and 1 based on the seed + /// + /// Deterministic pseudo-random value between 0 and 1 + public double GetDouble(uint offset) + { + var dRand = (double)(unchecked(seed + offset)); // We want it to overflow + + var dotProduct = Math.Cos(dRand) * GelfondConst + Math.Sin(dRand) * GelfondSchneiderConst; + var denominator = 1e-7 + 256 * dotProduct; + var remainder = Numerator % denominator; + + return (remainder - Math.Floor(remainder)); + } + + /// + /// Get a deterministic float value between 0 and 1 based on the seed + /// The calculations are still made as doubles to prevent underflow errors. + /// + /// Deterministic pseudo-random value between 0 and 1 + public float GetFloat(uint offset) => (float)GetDouble(offset); + } +} diff --git a/math/Ray.cs b/math/Ray.cs new file mode 100644 index 0000000..77b1934 --- /dev/null +++ b/math/Ray.cs @@ -0,0 +1,400 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a three dimensional line based on a point in space and a direction. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Ray : IEquatable, IFormattable + { + /// + /// The position in three dimensional space where the ray starts. + /// + public Vector3 Position; + + /// + /// The normalized direction in which the ray points. + /// + public Vector3 Direction; + + /// + /// Initializes a new instance of the struct. + /// + /// The position in three dimensional space of the origin of the ray. + /// The normalized direction of the ray. + public Ray(Vector3 position, Vector3 direction) + { + this.Position = position; + this.Direction = direction; + } + + /// + /// Determines if there is an intersection between the current object and a point. + /// + /// The point to test. + /// Whether the two objects intersected. + public bool Intersects(ref Vector3 point) + { + return CollisionHelper.RayIntersectsPoint(ref this, ref point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray) + { + Vector3 point; + return CollisionHelper.RayIntersectsRay(ref this, ref ray, out point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The ray to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Ray ray, out Vector3 point) + { + return CollisionHelper.RayIntersectsRay(ref this, ref ray, out point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The plane to test + /// Whether the two objects intersected. + public bool Intersects(ref Plane plane) + { + float distance; + return CollisionHelper.RayIntersectsPlane(ref this, ref plane, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The plane to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Plane plane, out float distance) + { + return CollisionHelper.RayIntersectsPlane(ref this, ref plane, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The plane to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Plane plane, out Vector3 point) + { + return CollisionHelper.RayIntersectsPlane(ref this, ref plane, out point); + } + + /// + /// Determines if there is an intersection between the current object and a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triangle to test. + /// The third vertex of the triangle to test. + /// Whether the two objects intersected. + public bool Intersects(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3) + { + float distance; + return CollisionHelper.RayIntersectsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triangle to test. + /// The third vertex of the triangle to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out float distance) + { + return CollisionHelper.RayIntersectsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a triangle. + /// + /// The first vertex of the triangle to test. + /// The second vertex of the triangle to test. + /// The third vertex of the triangle to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref Vector3 vertex1, ref Vector3 vertex2, ref Vector3 vertex3, out Vector3 point) + { + return CollisionHelper.RayIntersectsTriangle(ref this, ref vertex1, ref vertex2, ref vertex3, out point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The box to test. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingBox box) + { + float distance; + return CollisionHelper.RayIntersectsBox(ref this, ref box, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The box to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingBox box, out float distance) + { + return CollisionHelper.RayIntersectsBox(ref this, ref box, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The box to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingBox box, out Vector3 point) + { + return CollisionHelper.RayIntersectsBox(ref this, ref box, out point); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The sphere to test. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingSphere sphere) + { + float distance; + return CollisionHelper.RayIntersectsSphere(ref this, ref sphere, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The sphere to test. + /// When the method completes, contains the distance of the intersection, + /// or 0 if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingSphere sphere, out float distance) + { + return CollisionHelper.RayIntersectsSphere(ref this, ref sphere, out distance); + } + + /// + /// Determines if there is an intersection between the current object and a . + /// + /// The sphere to test. + /// When the method completes, contains the point of intersection, + /// or if there was no intersection. + /// Whether the two objects intersected. + public bool Intersects(ref BoundingSphere sphere, out Vector3 point) + { + return CollisionHelper.RayIntersectsSphere(ref this, ref sphere, out point); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Ray left, Ray right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Ray left, Ray right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "Position:{0} Direction:{1}", Position.ToString(), Direction.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return string.Format(CultureInfo.CurrentCulture, "Position:{0} Direction:{1}", Position.ToString(format, CultureInfo.CurrentCulture), + Direction.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "Position:{0} Direction:{1}", Position.ToString(), Direction.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + return string.Format(formatProvider, "Position:{0} Direction:{1}", Position.ToString(format, formatProvider), + Direction.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return Position.GetHashCode() + Direction.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Ray value) + { + return Position == value.Position && Direction == value.Direction; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Ray)value); + } + +#if SlimDX1xInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator SlimDX.Ray(Ray value) + { + return new SlimDX.Ray(value.Position, value.Direction); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Ray(SlimDX.Ray value) + { + return new Ray(value.Position, value.Direction); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Ray(Ray value) + { + return new Microsoft.Xna.Framework.Ray(value.Position, value.Direction); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Ray(Microsoft.Xna.Framework.Ray value) + { + return new Ray(value.Position, value.Direction); + } +#endif + } +} diff --git a/math/Rectangle.cs b/math/Rectangle.cs new file mode 100644 index 0000000..3010acd --- /dev/null +++ b/math/Rectangle.cs @@ -0,0 +1,502 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// A rectangle structure defining X,Y,Width,Height. + /// + [DataContract( Name = "Rectangle")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Rectangle : IEquatable + { + /// + /// An empty rectangle. + /// + public static readonly Rectangle Empty; + + static Rectangle() + { + Empty = new Rectangle(); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The left. + /// The top. + /// The width. + /// The height. + public Rectangle(int x, int y, int width, int height) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + } + + /// + /// Gets or sets the left. + /// + /// The left. + [DataMemberIgnore] + public int Left + { + get { return X; } + set { X = value; } + } + + /// + /// Gets or sets the top. + /// + /// The top. + [DataMemberIgnore] + public int Top + { + get { return Y; } + set { Y = value; } + } + + /// + /// Gets or sets the right. + /// + /// The right. + [DataMemberIgnore] + public int Right + { + get { return X + Width; } + } + + /// + /// Gets or sets the bottom. + /// + /// The bottom. + [DataMemberIgnore] + public int Bottom + { + get { return Y + Height; } + } + + /// + /// Gets or sets the X position. + /// + /// The X position. + [DataMember( Order = 0 )] + public int X; + + /// + /// Gets or sets the Y position. + /// + /// The Y position. + [DataMember( Order = 1 )] + public int Y; + + /// + /// Gets or sets the width. + /// + /// The width. + [DataMember( Order = 2 )] + public int Width; + + /// + /// Gets or sets the height. + /// + /// The height. + [DataMember( Order = 3 )] + public int Height; + + /// + /// Gets or sets the location. + /// + /// + /// The location. + /// + [DataMemberIgnore] + public Point Location + { + get + { + return new Point(X, Y); + } + set + { + X = value.X; + Y = value.Y; + } + } + + /// + /// Gets the Point that specifies the center of the rectangle. + /// + /// + /// The center. + /// + [DataMemberIgnore] + public Point Center + { + get + { + return new Point(X + (Width / 2), Y + (Height / 2)); + } + } + + /// + /// Gets a value that indicates whether the rectangle is empty. + /// + /// + /// true if [is empty]; otherwise, false. + /// + public bool IsEmpty + { + get + { + return (Width == 0) && (Height == 0) && (X == 0) && (Y == 0); + } + } + + /// + /// Gets or sets the size of the rectangle. + /// + /// The size of the rectangle. + [DataMemberIgnore] + public Size2 Size + { + get + { + return new Size2(Width, Height); + } + set + { + Width = value.Width; + Height = value.Height; + } + } + + /// + /// Gets the position of the top-left corner of the rectangle. + /// + /// The top-left corner of the rectangle. + public Point TopLeft { get { return new Point(Left, Top); } } + + /// + /// Gets the position of the top-right corner of the rectangle. + /// + /// The top-right corner of the rectangle. + public Point TopRight { get { return new Point(Right, Top); } } + + /// + /// Gets the position of the bottom-left corner of the rectangle. + /// + /// The bottom-left corner of the rectangle. + public Point BottomLeft { get { return new Point(Left, Bottom); } } + + /// + /// Gets the position of the bottom-right corner of the rectangle. + /// + /// The bottom-right corner of the rectangle. + public Point BottomRight { get { return new Point(Right, Bottom); } } + + /// Changes the position of the rectangle. + /// The values to adjust the position of the rectangle by. + public void Offset(Point amount) + { + Offset(amount.X, amount.Y); + } + + /// Changes the position of the rectangle. + /// Change in the x-position. + /// Change in the y-position. + public void Offset(int offsetX, int offsetY) + { + X += offsetX; + Y += offsetY; + } + + /// Pushes the edges of the rectangle out by the horizontal and vertical values specified. + /// Value to push the sides out by. + /// Value to push the top and bottom out by. + public void Inflate(int horizontalAmount, int verticalAmount) + { + X -= horizontalAmount; + Y -= verticalAmount; + Width += horizontalAmount * 2; + Height += verticalAmount * 2; + } + + /// Determines whether this rectangle contains a specified point represented by its x- and y-coordinates. + /// The x-coordinate of the specified point. + /// The y-coordinate of the specified point. + public bool Contains(int x, int y) + { + return (X <= x) && (x < Right) && (Y <= y) && (y < Bottom); + } + + /// Determines whether this rectangle contains a specified Point. + /// The Point to evaluate. + public bool Contains(Point value) + { + bool result; + Contains(ref value, out result); + return result; + } + + /// Determines whether this rectangle contains a specified Point. + /// The Point to evaluate. + /// [OutAttribute] true if the specified Point is contained within this rectangle; false otherwise. + public void Contains(ref Point value, out bool result) + { + result = (X <= value.X) && (value.X < Right) && (Y <= value.Y) && (value.Y < Bottom); + } + + /// Determines whether this rectangle entirely contains a specified rectangle. + /// The rectangle to evaluate. + public bool Contains(Rectangle value) + { + bool result; + Contains(ref value, out result); + return result; + } + + /// Determines whether this rectangle entirely contains a specified rectangle. + /// The rectangle to evaluate. + /// [OutAttribute] On exit, is true if this rectangle entirely contains the specified rectangle, or false if not. + public void Contains(ref Rectangle value, out bool result) + { + result = (X <= value.X) && (value.Right <= Right) && (Y <= value.Y) && (value.Bottom <= Bottom); + } + + /// + /// Checks, if specified point is inside . + /// + /// X point coordinate. + /// Y point coordinate. + /// true if point is inside , otherwise false. + public bool Contains(float x, float y) + { + return (x >= this.X && x <= Right && y >= this.Y && y <= Bottom); + } + + /// + /// Checks, if specified is inside . + /// + /// Coordinate . + /// true if is inside , otherwise false. + public bool Contains(Vec2 vector2D) + { + return Contains(vector2D.X, vector2D.Y); + } + + /// + /// Checks, if specified is inside . + /// + /// Coordinate . + /// true if is inside , otherwise false. + public bool Contains(Int2 int2) + { + return Contains(int2.X, int2.Y); + } + + /// Determines whether a specified rectangle intersects with this rectangle. + /// The rectangle to evaluate. + public bool Intersects(Rectangle value) + { + bool result; + Intersects(ref value, out result); + return result; + } + + /// + /// Determines whether a specified rectangle intersects with this rectangle. + /// + /// The rectangle to evaluate + /// [OutAttribute] true if the specified rectangle intersects with this one; false otherwise. + public void Intersects(ref Rectangle value, out bool result) + { + result = (value.X < Right) && (X < value.Right) && (value.Y < Bottom) && (Y < value.Bottom); + } + + /// + /// Creates a rectangle defining the area where one rectangle overlaps with another rectangle. + /// + /// The first rectangle to compare. + /// The second rectangle to compare. + /// The intersection rectangle. + public static Rectangle Intersect(Rectangle value1, Rectangle value2) + { + Rectangle result; + Intersect(ref value1, ref value2, out result); + return result; + } + + /// Creates a rectangle defining the area where one rectangle overlaps with another rectangle. + /// The first rectangle to compare. + /// The second rectangle to compare. + /// [OutAttribute] The area where the two first parameters overlap. + public static void Intersect(ref Rectangle value1, ref Rectangle value2, out Rectangle result) + { + int newLeft = (value1.X > value2.X) ? value1.X : value2.X; + int newTop = (value1.Y > value2.Y) ? value1.Y : value2.Y; + int newRight = (value1.Right < value2.Right) ? value1.Right : value2.Right; + int newBottom = (value1.Bottom < value2.Bottom) ? value1.Bottom : value2.Bottom; + if ((newRight > newLeft) && (newBottom > newTop)) + { + result = new Rectangle(newLeft, newTop, newRight - newLeft, newBottom - newTop); + } + else + { + result = Empty; + } + } + + /// + /// Creates a new rectangle that incorporate the provided point to the given rectangle. + /// + /// The original rectangle. + /// The point to incorporate. + /// The union rectangle. + public static Rectangle Union(Rectangle rectangle, Int2 point) + { + Rectangle result; + var rect = new Rectangle(point.X, point.Y, 1, 1); + Union(ref rectangle, ref rect, out result); + return result; + } + + /// + /// Creates a new rectangle that exactly contains two other rectangles. + /// + /// The first rectangle to contain. + /// The second rectangle to contain. + /// The union rectangle. + public static Rectangle Union(Rectangle value1, Rectangle value2) + { + Rectangle result; + Union(ref value1, ref value2, out result); + return result; + } + + /// + /// Creates a new rectangle that exactly contains two other rectangles. + /// + /// The first rectangle to contain. + /// The second rectangle to contain. + /// [OutAttribute] The rectangle that must be the union of the first two rectangles. + public static void Union(ref Rectangle value1, ref Rectangle value2, out Rectangle result) + { + var left = Math.Min(value1.Left, value2.Left); + var right = Math.Max(value1.Right, value2.Right); + var top = Math.Min(value1.Top, value2.Top); + var bottom = Math.Max(value1.Bottom, value2.Bottom); + result = new Rectangle(left, top, right - left, bottom - top); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof(Rectangle)) return false; + return Equals((Rectangle)obj); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Rectangle other) + { + return other.X == this.X && other.Y == this.Y && other.Width == Width && other.Height == Height; + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + int result = X; + result = (result * 397) ^ Y; + result = (result * 397) ^ Width; + result = (result * 397) ^ Height; + return result; + } + } + + /// + /// Implements the operator ==. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator ==(Rectangle left, Rectangle right) + { + return left.Equals(right); + } + + /// + /// Implements the operator !=. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator !=(Rectangle left, Rectangle right) + { + return !(left == right); + } + + /// + /// Performs an implicit conversion to the structure. + /// + /// Performs direct converstion from int to float. + /// The source value. + /// The converted structure. + public static implicit operator RectangleF(Rectangle value) + { + return new RectangleF(value.X, value.Y, value.Width, value.Height); + } + + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Width:{2} Height:{3}", X, Y, Width, Height); + } + } +} diff --git a/math/RectangleF.cs b/math/RectangleF.cs new file mode 100644 index 0000000..3714884 --- /dev/null +++ b/math/RectangleF.cs @@ -0,0 +1,492 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Define a RectangleF. + /// + [DataContract( Name = "RectangleF")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct RectangleF : IEquatable + { + /// + /// An empty rectangle + /// + public static readonly RectangleF Empty; + + static RectangleF() + { + Empty = new RectangleF(); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The left. + /// The top. + /// The width. + /// The height. + public RectangleF(float x, float y, float width, float height) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + } + + /// + /// Gets or sets the X position of the left edge. + /// + /// The left. + [DataMemberIgnore] + public float Left + { + get { return X; } + set { X = value; } + } + + /// + /// Gets or sets the top. + /// + /// The top. + [DataMemberIgnore] + public float Top + { + get { return Y; } + set { Y = value; } + } + + /// + /// Gets the right. + /// + /// The right. + [DataMemberIgnore] + public float Right + { + get + { + return X + Width; + } + } + + /// + /// Gets the bottom. + /// + /// The bottom. + public float Bottom + { + get + { + return Y + Height; + } + } + + /// + /// Gets or sets the X position. + /// + /// The X position. + /// The beginning of the rectangle along the Ox axis. + [DataMember( Order = 0 )] + public float X; + + /// + /// Gets or sets the Y position. + /// + /// The Y position. + /// The beginning of the rectangle along the Oy axis. + [DataMember( Order = 1 )] + public float Y; + + /// + /// Gets or sets the width. + /// + /// The width. + /// The width of the rectangle. + [DataMember( Order = 2 )] + public float Width; + + /// + /// Gets or sets the height. + /// + /// The height. + /// The height of the rectangle. + [DataMember( Order = 3 )] + public float Height; + + /// + /// Gets or sets the location. + /// + /// + /// The location. + /// + [DataMemberIgnore] + public Vec2 Location + { + get + { + return new Vec2(X, Y); + } + set + { + X = value.X; + Y = value.Y; + } + } + + /// + /// Gets the Point that specifies the center of the rectangle. + /// + /// + /// The center. + /// + [DataMemberIgnore] + public Vec2 Center + { + get + { + return new Vec2(X + (Width / 2), Y + (Height / 2)); + } + } + + /// + /// Gets a value that indicates whether the rectangle is empty. + /// + /// + /// true if [is empty]; otherwise, false. + /// + public bool IsEmpty + { + get + { + return (Width == 0.0f) && (Height == 0.0f) && (X == 0.0f) && (Y == 0.0f); + } + } + + /// + /// Gets or sets the size of the rectangle. + /// + /// The size of the rectangle. + [DataMemberIgnore] + public Size2F Size + { + get + { + return new Size2F(Width, Height); + } + set + { + Width = value.Width; + Height = value.Height; + } + } + + /// + /// Gets the position of the top-left corner of the rectangle. + /// + /// The top-left corner of the rectangle. + public Vec2 TopLeft { get { return new Vec2(X, Y); } } + + /// + /// Gets the position of the top-right corner of the rectangle. + /// + /// The top-right corner of the rectangle. + public Vec2 TopRight { get { return new Vec2(Right, Y); } } + + /// + /// Gets the position of the bottom-left corner of the rectangle. + /// + /// The bottom-left corner of the rectangle. + public Vec2 BottomLeft { get { return new Vec2(X, Bottom); } } + + /// + /// Gets the position of the bottom-right corner of the rectangle. + /// + /// The bottom-right corner of the rectangle. + public Vec2 BottomRight { get { return new Vec2(Right, Bottom); } } + + /// Changes the position of the rectangle. + /// The values to adjust the position of the rectangle by. + public void Offset(Point amount) + { + Offset(amount.X, amount.Y); + } + + /// Changes the position of the rectangle. + /// The values to adjust the position of the rectangle by. + public void Offset(Vec2 amount) + { + Offset(amount.X, amount.Y); + } + + /// Changes the position of the rectangle. + /// Change in the x-position. + /// Change in the y-position. + public void Offset(float offsetX, float offsetY) + { + X += offsetX; + Y += offsetY; + } + + /// Pushes the edges of the rectangle out by the horizontal and vertical values specified. + /// Value to push the sides out by. + /// Value to push the top and bottom out by. + public void Inflate(float horizontalAmount, float verticalAmount) + { + X -= horizontalAmount; + Y -= verticalAmount; + Width += horizontalAmount * 2; + Height += verticalAmount * 2; + } + + /// Determines whether this rectangle contains a specified Point. + /// The Point to evaluate. + /// [OutAttribute] true if the specified Point is contained within this rectangle; false otherwise. + public void Contains(ref Vec2 value, out bool result) + { + result = (X <= value.X) && (value.X < Right) && (Y <= value.Y) && (value.Y < Bottom); + } + + /// Determines whether this rectangle entirely contains a specified rectangle. + /// The rectangle to evaluate. + public bool Contains(Rectangle value) + { + return (X <= value.X) && (value.Right <= Right) && (Y <= value.Y) && (value.Bottom <= Bottom); + } + + /// Determines whether this rectangle entirely contains a specified rectangle. + /// The rectangle to evaluate. + /// [OutAttribute] On exit, is true if this rectangle entirely contains the specified rectangle, or false if not. + public void Contains(ref RectangleF value, out bool result) + { + result = (X <= value.X) && (value.Right <= Right) && (Y <= value.Y) && (value.Bottom <= Bottom); + } + + /// + /// Checks, if specified point is inside . + /// + /// X point coordinate. + /// Y point coordinate. + /// true if point is inside , otherwise false. + public bool Contains(float x, float y) + { + return (x >= this.X && x <= Right && y >= this.Y && y <= Bottom); + } + + /// + /// Checks, if specified is inside . + /// + /// Coordinate . + /// true if is inside , otherwise false. + public bool Contains(Vec2 vector2D) + { + return Contains(vector2D.X, vector2D.Y); + } + + /// + /// Checks, if specified is inside . + /// + /// Coordinate . + /// true if is inside , otherwise false. + public bool Contains(Int2 int2) + { + return Contains(int2.X, int2.Y); + } + + /// + /// Checks, if specified is inside . + /// + /// Coordinate . + /// true if is inside , otherwise false. + public bool Contains(Point point) + { + return Contains(point.X, point.Y); + } + + /// Determines whether a specified rectangle intersects with this rectangle. + /// The rectangle to evaluate. + public bool Intersects(RectangleF value) + { + bool result; + Intersects(ref value, out result); + return result; + } + + /// + /// Determines whether a specified rectangle intersects with this rectangle. + /// + /// The rectangle to evaluate + /// [OutAttribute] true if the specified rectangle intersects with this one; false otherwise. + public void Intersects(ref RectangleF value, out bool result) + { + result = (value.X < Right) && (X < value.Right) && (value.Y < Bottom) && (Y < value.Bottom); + } + + /// + /// Creates a rectangle defining the area where one rectangle overlaps with another rectangle. + /// + /// The first Rectangle to compare. + /// The second Rectangle to compare. + /// The intersection rectangle. + public static RectangleF Intersect(RectangleF value1, RectangleF value2) + { + RectangleF result; + Intersect(ref value1, ref value2, out result); + return result; + } + + /// Creates a rectangle defining the area where one rectangle overlaps with another rectangle. + /// The first rectangle to compare. + /// The second rectangle to compare. + /// [OutAttribute] The area where the two first parameters overlap. + public static void Intersect(ref RectangleF value1, ref RectangleF value2, out RectangleF result) + { + float newLeft = (value1.X > value2.X) ? value1.X : value2.X; + float newTop = (value1.Y > value2.Y) ? value1.Y : value2.Y; + float newRight = (value1.Right < value2.Right) ? value1.Right : value2.Right; + float newBottom = (value1.Bottom < value2.Bottom) ? value1.Bottom : value2.Bottom; + if ((newRight > newLeft) && (newBottom > newTop)) + { + result = new RectangleF(newLeft, newTop, newRight - newLeft, newBottom - newTop); + } + else + { + result = Empty; + } + } + + /// + /// Creates a new rectangle that exactly contains two other rectangles. + /// + /// The first rectangle to contain. + /// The second rectangle to contain. + /// The union rectangle. + public static RectangleF Union(RectangleF value1, RectangleF value2) + { + RectangleF result; + Union(ref value1, ref value2, out result); + return result; + } + + /// + /// Creates a new rectangle that exactly contains two other rectangles. + /// + /// The first rectangle to contain. + /// The second rectangle to contain. + /// [OutAttribute] The rectangle that must be the union of the first two rectangles. + public static void Union(ref RectangleF value1, ref RectangleF value2, out RectangleF result) + { + var left = Math.Min(value1.Left, value2.Left); + var right = Math.Max(value1.Right, value2.Right); + var top = Math.Min(value1.Top, value2.Top); + var bottom = Math.Max(value1.Bottom, value2.Bottom); + result = new RectangleF(left, top, right - left, bottom - top); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof(RectangleF)) return false; + return Equals((RectangleF)obj); + } + + /// + public bool Equals(RectangleF other) + { + return MathUtil.NearEqual(other.Left, Left) && + MathUtil.NearEqual(other.Right, Right) && + MathUtil.NearEqual(other.Top, Top) && + MathUtil.NearEqual(other.Bottom, Bottom); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + int result = X.GetHashCode(); + result = (result * 397) ^ Y.GetHashCode(); + result = (result * 397) ^ Width.GetHashCode(); + result = (result * 397) ^ Height.GetHashCode(); + return result; + } + } + + /// + /// Implements the operator ==. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator ==(RectangleF left, RectangleF right) + { + return left.Equals(right); + } + + /// + /// Implements the operator !=. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator !=(RectangleF left, RectangleF right) + { + return !(left == right); + } + + /// + /// Performs an explicit conversion to structure. + /// + /// Performs direct float to int conversion, any fractional data is truncated. + /// The source value. + /// A converted structure. + public static explicit operator Rectangle(RectangleF value) + { + return new Rectangle((int)value.X, (int)value.Y, (int)value.Width, (int)value.Height); + } + + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Width:{2} Height:{3}", X, Y, Width, Height); + } + } +} diff --git a/math/Size2.cs b/math/Size2.cs new file mode 100644 index 0000000..8fe8c6a --- /dev/null +++ b/math/Size2.cs @@ -0,0 +1,132 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Defines a 2D rectangular size (width,height). + /// + [DataContract( Name = "!Size2")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Size2 : IEquatable + { + /// + /// A zero size with (width, height) = (0,0) + /// + public static readonly Size2 Zero = new Size2(0, 0); + + /// + /// A zero size with (width, height) = (0,0) + /// + public static readonly Size2 Empty = Zero; + + /// + /// Initializes a new instance of the struct. + /// + /// The x. + /// The y. + public Size2(int width, int height) + { + Width = width; + Height = height; + } + + /// + /// Width. + /// + [DataMember( Order = 0 )] + public int Width; + + /// + /// Height. + /// + [DataMember( Order = 1 )] + public int Height; + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Size2 other) + { + return other.Width == Width && other.Height == Height; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof(Size2)) return false; + return Equals((Size2)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + return (Width * 397) ^ Height; + } + } + + /// + /// Implements the operator ==. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static bool operator ==(Size2 left, Size2 right) + { + return left.Equals(right); + } + + /// + /// Implements the operator !=. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static bool operator !=(Size2 left, Size2 right) + { + return !left.Equals(right); + } + + /// + public override string ToString() + { + return string.Format("({0},{1})", Width, Height); + } + } +} diff --git a/math/Size2F.cs b/math/Size2F.cs new file mode 100644 index 0000000..3045958 --- /dev/null +++ b/math/Size2F.cs @@ -0,0 +1,132 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Defines a 2D rectangular size (width,height). + /// + [DataContract( Name = "Size2F")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Size2F : IEquatable + { + /// + /// A zero size with (width, height) = (0,0) + /// + public static readonly Size2F Zero = new Size2F(0, 0); + + /// + /// A zero size with (width, height) = (0,0) + /// + public static readonly Size2F Empty = Zero; + + /// + /// Initializes a new instance of the struct. + /// + /// The x. + /// The y. + public Size2F(float width, float height) + { + Width = width; + Height = height; + } + + /// + /// Width. + /// + [DataMember( Order = 0 )] + public float Width; + + /// + /// Height. + /// + [DataMember( Order = 1 )] + public float Height; + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Size2F other) + { + return other.Width == Width && other.Height == Height; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof(Size2F)) return false; + return Equals((Size2F)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + return (Width.GetHashCode() * 397) ^ Height.GetHashCode(); + } + } + + /// + /// Implements the operator ==. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static bool operator ==(Size2F left, Size2F right) + { + return left.Equals(right); + } + + /// + /// Implements the operator !=. + /// + /// The left. + /// The right. + /// + /// The result of the operator. + /// + public static bool operator !=(Size2F left, Size2F right) + { + return !left.Equals(right); + } + + /// + public override string ToString() + { + return string.Format("({0},{1})", Width, Height); + } + } +} diff --git a/math/Size3.cs b/math/Size3.cs new file mode 100644 index 0000000..8eb40b3 --- /dev/null +++ b/math/Size3.cs @@ -0,0 +1,237 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// 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. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Structure providing Width, Height and Depth. + /// + [DataContract( Name = "!Size3")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential)] + public struct Size3 : IEquatable, IComparable + { + /// + /// A zero size with (width, height, depth) = (0,0,0) + /// + public static readonly Size3 Zero = new Size3(0, 0, 0); + + /// + /// A one size with (width, height, depth) = (1,1,1) + /// + public static readonly Size3 One = new Size3(1, 1, 1); + + /// + /// A zero size with (width, height, depth) = (0,0,0) + /// + public static readonly Size3 Empty = Zero; + + /// + /// Initializes a new instance of the struct. + /// + /// The x. + /// The y. + /// The depth. + public Size3(int width, int height, int depth) + { + Width = width; + Height = height; + Depth = depth; + } + + /// + /// Width. + /// + [DataMember( Order = 0 )] + public int Width; + + /// + /// Height. + /// + [DataMember( Order = 1 )] + public int Height; + + /// + /// Height. + /// + [DataMember( Order = 2 )] + public int Depth; + + /// + /// Gets a volume size. + /// + private long VolumeSize + { + get + { + return (long)Width * Height * Depth; + } + } + + /// + public bool Equals(Size3 other) + { + return Width == other.Width && Height == other.Height && Depth == other.Depth; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is Size3 && Equals((Size3)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = Width; + hashCode = (hashCode * 397) ^ Height; + hashCode = (hashCode * 397) ^ Depth; + return hashCode; + } + } + + /// + public int CompareTo(Size3 other) + { + return Math.Sign(this.VolumeSize - other.VolumeSize); + } + + /// + public override string ToString() + { + return string.Format("({0},{1},{2})", Width, Height, Depth); + } + + /// + /// Implements the <. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator <(Size3 left, Size3 right) + { + return left.CompareTo(right) < 0; + } + + /// + /// Implements the <. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator <=(Size3 left, Size3 right) + { + return left.CompareTo(right) <= 0; + } + + /// + /// Implements the < or ==. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator >(Size3 left, Size3 right) + { + return left.CompareTo(right) > 0; + } + + /// + /// Implements the > or ==. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator >=(Size3 left, Size3 right) + { + return left.CompareTo(right) >= 0; + } + + /// + /// Implements the ==. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator ==(Size3 left, Size3 right) + { + return left.Equals(right); + } + + /// + /// Implements the !=. + /// + /// The left. + /// The right. + /// The result of the operator. + public static bool operator !=(Size3 left, Size3 right) + { + return !left.Equals(right); + } + + /// + /// Calculates the next up mip-level (*2) of this size. + /// + /// A next up mip-level Size3. + public Size3 Up2(int count = 1) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException("count", "Must be >= 0"); + } + + return new Size3(Math.Max(1, Width << count), Math.Max(1, Height << count), Math.Max(1, Depth << count)); + } + + /// + /// Calculates the next down mip-level (/2) of this size. + /// + /// The count. + /// A next down mip-level Size3. + public Size3 Down2(int count = 1) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException("count", "Must be >= 0"); + } + + return new Size3(Math.Max(1, Width >> count), Math.Max(1, Height >> count), Math.Max(1, Depth >> count)); + } + + /// + /// Calculates the mip size based on a direction. + /// + /// The direction < 0 then , > 0 then , else this unchanged. + /// Size3. + public Size3 Mip(int direction) + { + return direction == 0 ? this : direction < 0 ? Down2() : Up2(); + } + } +} diff --git a/math/SphericalHarmonics.cs b/math/SphericalHarmonics.cs new file mode 100644 index 0000000..9698943 --- /dev/null +++ b/math/SphericalHarmonics.cs @@ -0,0 +1,250 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +#pragma warning disable SA1003 // Symbols must be spaced correctly +#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly +#pragma warning disable SA1009 // Closing parenthesis must be spaced correctly +#pragma warning disable SA1010 // Opening square brackets must be spaced correctly +#pragma warning disable SA1025 // Code must not contain multiple whitespace in a row +#pragma warning disable SA1119 // Statement must not use unnecessary parenthesis +#pragma warning disable SA1402 // File may only contain a single class +using System; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// A representation of a sphere of values via Spherical Harmonics (SH). + /// + /// The type of data contained by the sphere + [DataContract( Name = "SphericalHarmonicsGeneric")] + public abstract class SphericalHarmonics + { + /// + /// The maximum order supported. + /// + public const int MaximumOrder = 5; + + private int order; + + /// + /// The order of calculation of the spherical harmonic. + /// + [DataMember( Order = 0 )] + public int Order + { + get { return order; } + internal set + { + if (order>5) + throw new NotSupportedException("Only orders inferior or equal to 5 are supported"); + + order = Math.Max(1, value); + } + } + + /// + /// Get the coefficients defining the spherical harmonics (the spherical coordinates x{l,m} multiplying the spherical base Y{l,m}). + /// + [DataMember( Order = 1 )] + public TDataType[] Coefficients { get; internal set; } + + /// + /// Initializes a new instance of the class (null, for serialization). + /// + internal SphericalHarmonics() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The order of the harmonics + protected SphericalHarmonics(int order) + { + this.order = order; + Coefficients = new TDataType[order * order]; + } + + /// + /// Evaluate the value of the spherical harmonics in the provided direction. + /// + /// The direction + /// The value of the spherical harmonics in the direction + public abstract TDataType Evaluate(Vector3 direction); + + /// + /// Returns the coefficient x{l,m} of the spherical harmonics (the {l,m} spherical coordinate corresponding to the spherical base Y{l,m}). + /// + /// the l index of the coefficient + /// the m index of the coefficient + /// the value of the coefficient + public TDataType this[int l, int m] + { + get + { + CheckIndicesValidity(l, m, order); + return Coefficients[LmToCoefficientIndex(l, m)]; + } + set + { + CheckIndicesValidity(l, m, order); + Coefficients[LmToCoefficientIndex(l, m)] = value; + } + } + + // ReSharper disable UnusedParameter.Local + private static void CheckIndicesValidity(int l, int m, int maxOrder) + // ReSharper restore UnusedParameter.Local + { + if (l > maxOrder - 1) + throw new IndexOutOfRangeException("'l' parameter should be between '0' and '{0}' (order-1).".ToFormat(maxOrder-1)); + + if (Math.Abs(m) > l) + throw new IndexOutOfRangeException("'m' parameter should be between '-l' and '+l'."); + } + + private static int LmToCoefficientIndex(int l, int m) + { + return l * l + l + m; + } + } + + /// + /// A spherical harmonics representation of a cubemap. + /// + [DataContract( Name = "SphericalHarmonics")] + public class SphericalHarmonics : SphericalHarmonics + { + private readonly float[] baseValues; + + private const float Pi4 = 4 * MathUtil.Pi; + private const float Pi16 = 16 * MathUtil.Pi; + private const float Pi64 = 64 * MathUtil.Pi; + private static readonly float SqrtPi = (float)Math.Sqrt(MathUtil.Pi); + + /// + /// Base coefficients for SH. + /// + public static readonly float[] BaseCoefficients = + { + (float)(1.0/(2.0*SqrtPi)), + + (float)(-Math.Sqrt(3.0/Pi4)), + (float)(Math.Sqrt(3.0/Pi4)), + (float)(-Math.Sqrt(3.0/Pi4)), + + (float)(Math.Sqrt(15.0/Pi4)), + (float)(-Math.Sqrt(15.0/Pi4)), + (float)(Math.Sqrt(5.0/Pi16)), + (float)(-Math.Sqrt(15.0/Pi4)), + (float)(Math.Sqrt(15.0/Pi16)), + + -(float)Math.Sqrt(70/Pi64), + (float)Math.Sqrt(105/Pi4), + -(float)Math.Sqrt(42/Pi64), + (float)Math.Sqrt(7/Pi16), + -(float)Math.Sqrt(42/Pi64), + (float)Math.Sqrt(105/Pi16), + -(float)Math.Sqrt(70/Pi64), + + 3*(float)Math.Sqrt(35/Pi16), + -3*(float)Math.Sqrt(70/Pi64), + 3*(float)Math.Sqrt(5/Pi16), + -3*(float)Math.Sqrt(10/Pi64), + (float)(1.0/(16.0*SqrtPi)), + -3*(float)Math.Sqrt(10/Pi64), + 3*(float)Math.Sqrt(5/Pi64), + -3*(float)Math.Sqrt(70/Pi64), + 3*(float)Math.Sqrt(35/(4*Pi64)), + }; + + /// + /// Initializes a new instance of the class (null, for serialization). + /// + internal SphericalHarmonics() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The order of the harmonics + public SphericalHarmonics(int order) + : base(order) + { + baseValues = new float[order * order]; + } + + /// + /// Evaluates the color for the specified direction. + /// + /// The direction to evaluate. + /// The color computed for this direction. + public override Color3 Evaluate(Vector3 direction) + { + var x = direction.X; + var y = direction.Y; + var z = direction.Z; + + var x2 = x*x; + var y2 = y*y; + var z2 = z*z; + + var z3 = (float)Math.Pow(z, 3.0); + + var x4 = (float)Math.Pow(x, 4.0); + var y4 = (float)Math.Pow(y, 4.0); + var z4 = (float)Math.Pow(z, 4.0); + + //Equations based on data from: http://ppsloan.org/publications/StupidSH36.pdf + baseValues[ 0] = 1/(2*SqrtPi); + + if (Order > 1) + { + baseValues[ 1] = -(float)Math.Sqrt(3/Pi4)*y; + baseValues[ 2] = (float)Math.Sqrt(3/Pi4)*z; + baseValues[ 3] = -(float)Math.Sqrt(3/Pi4)*x; + + if (Order > 2) + { + baseValues[ 4] = (float)Math.Sqrt(15/Pi4)*y*x; + baseValues[ 5] = -(float)Math.Sqrt(15/Pi4)*y*z; + baseValues[ 6] = (float)Math.Sqrt(5/Pi16)*(3*z2-1); + baseValues[ 7] = -(float)Math.Sqrt(15/Pi4)*x*z; + baseValues[ 8] = (float)Math.Sqrt(15/Pi16)*(x2-y2); + + if (Order > 3) + { + baseValues[ 9] = -(float)Math.Sqrt( 70/Pi64)*y*(3*x2-y2); + baseValues[10] = (float)Math.Sqrt(105/ Pi4)*y*x*z; + baseValues[11] = -(float)Math.Sqrt( 42/Pi64)*y*(-1+5*z2); + baseValues[12] = (float)Math.Sqrt( 7/Pi16)*(5*z3-3*z); + baseValues[13] = -(float)Math.Sqrt( 42/Pi64)*x*(-1+5*z2); + baseValues[14] = (float)Math.Sqrt(105/Pi16)*(x2-y2)*z; + baseValues[15] = -(float)Math.Sqrt( 70/Pi64)*x*(x2-3*y2); + + if (Order > 4) + { + baseValues[16] = 3*(float)Math.Sqrt(35/Pi16)*x*y*(x2-y2); + baseValues[17] = -3*(float)Math.Sqrt(70/Pi64)*y*z*(3*x2-y2); + baseValues[18] = 3*(float)Math.Sqrt( 5/Pi16)*y*x*(-1+7*z2); + baseValues[19] = -3*(float)Math.Sqrt(10/Pi64)*y*z*(-3+7*z2); + baseValues[20] = (105*z4-90*z2+9)/(16*SqrtPi); + baseValues[21] = -3*(float)Math.Sqrt(10/Pi64)*x*z*(-3+7*z2); + baseValues[22] = 3*(float)Math.Sqrt( 5/Pi64)*(x2-y2)*(-1+7*z2); + baseValues[23] = -3*(float)Math.Sqrt(70/Pi64)*x*z*(x2-3*y2); + baseValues[24] = 3*(float)Math.Sqrt(35/(4*Pi64))*(x4-6*y2*x2+y4); + } + } + } + } + + var data = new Color3(); + + for (int i = 0; i < baseValues.Length; i++) + data += Coefficients[i] * baseValues[i]; + + return data; + } + } +} diff --git a/math/UInt4.cs b/math/UInt4.cs new file mode 100644 index 0000000..7ba3da3 --- /dev/null +++ b/math/UInt4.cs @@ -0,0 +1,640 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// Copyright (c) 2010-2011 SharpDX - Alexandre Mutel +// +// 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. +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using Xenko.Core.Serialization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a four dimensional mathematical vector. + /// + [DataContract] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct UInt4 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly UInt4 Zero = new UInt4(); + + /// + /// The X unit (1, 0, 0, 0). + /// + public static readonly UInt4 UnitX = new UInt4(1, 0, 0, 0); + + /// + /// The Y unit (0, 1, 0, 0). + /// + public static readonly UInt4 UnitY = new UInt4(0, 1, 0, 0); + + /// + /// The Z unit (0, 0, 1, 0). + /// + public static readonly UInt4 UnitZ = new UInt4(0, 0, 1, 0); + + /// + /// The W unit (0, 0, 0, 1). + /// + public static readonly UInt4 UnitW = new UInt4(0, 0, 0, 1); + + /// + /// A with all of its components set to one. + /// + public static readonly UInt4 One = new UInt4(1, 1, 1, 1); + + /// + /// The X component of the vector. + /// + public uint X; + + /// + /// The Y component of the vector. + /// + public uint Y; + + /// + /// The Z component of the vector. + /// + public uint Z; + + /// + /// The W component of the vector. + /// + public uint W; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public UInt4(uint value) + { + X = value; + Y = value; + Z = value; + W = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + /// Initial value for the Z component of the vector. + /// Initial value for the W component of the vector. + public UInt4(uint x, uint y, uint z, uint w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, Z, and W components of the vector. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public UInt4(uint[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 4) + throw new ArgumentOutOfRangeException("values", "There must be four and only four input values for UInt4."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + W = values[3]; + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, Z, or W component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public uint this[uint index] + { + get + { + switch (index) + { + case 0: + return X; + case 1: + return Y; + case 2: + return Z; + case 3: + return W; + } + + throw new ArgumentOutOfRangeException("index", "Indices for UInt4 run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: + X = value; + break; + case 1: + Y = value; + break; + case 2: + Z = value; + break; + case 3: + W = value; + break; + default: + throw new ArgumentOutOfRangeException("index", "Indices for UInt4 run from 0 to 3, inclusive."); + } + } + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A four-element array containing the components of the vector. + public uint[] ToArray() + { + return new uint[] { X, Y, Z, W }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + public static void Add(ref UInt4 left, ref UInt4 right, out UInt4 result) + { + result = new UInt4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static UInt4 Add(UInt4 left, UInt4 right) + { + return new UInt4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + public static void Subtract(ref UInt4 left, ref UInt4 right, out UInt4 result) + { + result = new UInt4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static UInt4 Subtract(UInt4 left, UInt4 right) + { + return new UInt4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Multiply(ref UInt4 value, uint scale, out UInt4 result) + { + result = new UInt4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static UInt4 Multiply(UInt4 value, uint scale) + { + return new UInt4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + public static void Modulate(ref UInt4 left, ref UInt4 right, out UInt4 result) + { + result = new UInt4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + public static UInt4 Modulate(UInt4 left, UInt4 right) + { + return new UInt4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + public static void Divide(ref UInt4 value, uint scale, out UInt4 result) + { + result = new UInt4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static UInt4 Divide(UInt4 value, uint scale) + { + return new UInt4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref UInt4 value, ref UInt4 min, ref UInt4 max, out UInt4 result) + { + uint x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + uint y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + uint z = value.Z; + z = (z > max.Z) ? max.Z : z; + z = (z < min.Z) ? min.Z : z; + + uint w = value.W; + w = (w > max.W) ? max.W : w; + w = (w < min.W) ? min.W : w; + + result = new UInt4(x, y, z, w); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static UInt4 Clamp(UInt4 value, UInt4 min, UInt4 max) + { + UInt4 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + public static void Max(ref UInt4 left, ref UInt4 right, out UInt4 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + result.Z = (left.Z > right.Z) ? left.Z : right.Z; + result.W = (left.W > right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + public static UInt4 Max(UInt4 left, UInt4 right) + { + UInt4 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + public static void Min(ref UInt4 left, ref UInt4 right, out UInt4 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + result.Z = (left.Z < right.Z) ? left.Z : right.Z; + result.W = (left.W < right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + public static UInt4 Min(UInt4 left, UInt4 right) + { + UInt4 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + public static UInt4 operator +(UInt4 left, UInt4 right) + { + return new UInt4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + public static UInt4 operator +(UInt4 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + public static UInt4 operator -(UInt4 left, UInt4 right) + { + return new UInt4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static UInt4 operator *(uint scale, UInt4 value) + { + return new UInt4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static UInt4 operator *(UInt4 value, uint scale) + { + return new UInt4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + public static UInt4 operator /(UInt4 value, uint scale) + { + return new UInt4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(UInt4 left, UInt4 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(UInt4 left, UInt4 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(UInt4 value) + { + return new Vec2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(UInt4 value) + { + return new Vector3(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(UInt4 value) + { + return new Vector4(value.X, value.Y, value.Z, value.W); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", + X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), + Z.ToString(format, CultureInfo.CurrentCulture), + W.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider), + W.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(UInt4 other) + { + return other.X == X && other.Y == Y && other.Z == Z && other.W == W; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((UInt4)value); + } + + /// + /// Performs an implicit conversion from array to . + /// + /// The input. + /// The result of the conversion. + public static implicit operator UInt4(uint[] input) + { + return new UInt4(input); + } + + /// + /// Performs an implicit conversion from to array. + /// + /// The input. + /// The result of the conversion. + public static implicit operator uint[](UInt4 input) + { + return input.ToArray(); + } + } +} diff --git a/math/Vector2.cs b/math/Vector2.cs new file mode 100644 index 0000000..213dd84 --- /dev/null +++ b/math/Vector2.cs @@ -0,0 +1,1462 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a two dimensional mathematical vector. + /// + [DataContract( Name = "float2")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Vec2 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Vec2 Zero = new Vec2(); + + /// + /// The X unit (1, 0). + /// + public static readonly Vec2 UnitX = new Vec2(1.0f, 0.0f); + + /// + /// The Y unit (0, 1). + /// + public static readonly Vec2 UnitY = new Vec2(0.0f, 1.0f); + + /// + /// A with all of its components set to one. + /// + public static readonly Vec2 One = new Vec2(1.0f, 1.0f); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public float X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public float Y; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Vec2(float value) + { + X = value; + Y = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + public Vec2(float x, float y) + { + X = x; + Y = y; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X and Y components of the vector. This must be an array with two elements. + /// Thrown when is null. + /// Thrown when contains more or less than two elements. + public Vec2(float[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 2) + throw new ArgumentOutOfRangeException("values", "There must be two and only two input values for Vector2."); + + X = values[0]; + Y = values[1]; + } + + /// + /// Gets a value indicting whether this instance is normalized. + /// + public bool IsNormalized + { + get { return Math.Abs((X * X) + (Y * Y) - 1f) < MathUtil.ZeroTolerance; } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X or Y component, depending on the index. + /// The index of the component to access. Use 0 for the X component and 1 for the Y component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 1]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Vector2 run from 0 to 1, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Vector2 run from 0 to 1, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Length() + { + return (float)Math.Sqrt((X * X) + (Y * Y)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float LengthSquared() + { + return (X * X) + (Y * Y); + } + + /// + /// Converts the vector into a unit vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Normalize() + { + float length = Length(); + if (length > MathUtil.ZeroTolerance) + { + float inv = 1.0f / length; + X *= inv; + Y *= inv; + } + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A two-element array containing the components of the vector. + public float[] ToArray() + { + return new float[] { X, Y }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Vec2 left, ref Vec2 right, out Vec2 result) + { + result = new Vec2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Add(Vec2 left, Vec2 right) + { + return new Vec2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Subtract(ref Vec2 left, ref Vec2 right, out Vec2 result) + { + result = new Vec2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Subtract(Vec2 left, Vec2 right) + { + return new Vec2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(ref Vec2 value, float scale, out Vec2 result) + { + result = new Vec2(value.X * scale, value.Y * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Multiply(Vec2 value, float scale) + { + return new Vec2(value.X * scale, value.Y * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Modulate(ref Vec2 left, ref Vec2 right, out Vec2 result) + { + result = new Vec2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Modulate(Vec2 left, Vec2 right) + { + return new Vec2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(ref Vec2 value, float scale, out Vec2 result) + { + result = new Vec2(value.X / scale, value.Y / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Divide(Vec2 value, float scale) + { + return new Vec2(value.X / scale, value.Y / scale); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// When the method completes, contains the demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Demodulate(ref Vec2 left, ref Vec2 right, out Vec2 result) + { + result = new Vec2(left.X / right.X, left.Y / right.Y); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// The demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Demodulate(Vec2 left, Vec2 right) + { + return new Vec2(left.X / right.X, left.Y / right.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Negate(ref Vec2 value, out Vec2 result) + { + result = new Vec2(-value.X, -value.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Negate(Vec2 value) + { + return new Vec2(-value.X, -value.Y); + } + + /// + /// Returns a containing the 2D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. + /// + /// A containing the 2D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// When the method completes, contains the 2D Cartesian coordinates of the specified point. + public static void Barycentric(ref Vec2 value1, ref Vec2 value2, ref Vec2 value3, float amount1, float amount2, out Vec2 result) + { + result = new Vec2( + (value1.X + (amount1 * (value2.X - value1.X))) + (amount2 * (value3.X - value1.X)), + (value1.Y + (amount1 * (value2.Y - value1.Y))) + (amount2 * (value3.Y - value1.Y))); + } + + /// + /// Returns a containing the 2D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. + /// + /// A containing the 2D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 2D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// A new containing the 2D Cartesian coordinates of the specified point. + public static Vec2 Barycentric(Vec2 value1, Vec2 value2, Vec2 value3, float amount1, float amount2) + { + Vec2 result; + Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); + return result; + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Vec2 value, ref Vec2 min, ref Vec2 max, out Vec2 result) + { + float x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + float y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + result = new Vec2(x, y); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Vec2 Clamp(Vec2 value, Vec2 min, Vec2 max) + { + Vec2 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static void Distance(ref Vec2 value1, ref Vec2 value2, out float result) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + + result = (float)Math.Sqrt((x * x) + (y * y)); + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static float Distance(Vec2 value1, Vec2 value2) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + + return (float)Math.Sqrt((x * x) + (y * y)); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector + /// When the method completes, contains the squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static void DistanceSquared(ref Vec2 value1, ref Vec2 value2, out float result) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + + result = (x * x) + (y * y); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static float DistanceSquared(Vec2 value1, Vec2 value2) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + + return (x * x) + (y * y); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dot(ref Vec2 left, ref Vec2 right, out float result) + { + result = (left.X * right.X) + (left.Y * right.Y); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(Vec2 left, Vec2 right) + { + return (left.X * right.X) + (left.Y * right.Y); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// When the method completes, contains the normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ref Vec2 value, out Vec2 result) + { + result = value; + result.Normalize(); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// The normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Normalize(Vec2 value) + { + value.Normalize(); + return value; + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Vec2 start, ref Vec2 end, float amount, out Vec2 result) + { + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Vec2 Lerp(Vec2 start, Vec2 end, float amount) + { + Vec2 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Vec2 start, ref Vec2 end, float amount, out Vec2 result) + { + amount = (amount > 1.0f) ? 1.0f : ((amount < 0.0f) ? 0.0f : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Vec2 SmoothStep(Vec2 start, Vec2 end, float amount) + { + Vec2 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// When the method completes, contains the result of the Hermite spline interpolation. + public static void Hermite(ref Vec2 value1, ref Vec2 tangent1, ref Vec2 value2, ref Vec2 tangent2, float amount, out Vec2 result) + { + float squared = amount * amount; + float cubed = amount * squared; + float part1 = ((2.0f * cubed) - (3.0f * squared)) + 1.0f; + float part2 = (-2.0f * cubed) + (3.0f * squared); + float part3 = (cubed - (2.0f * squared)) + amount; + float part4 = cubed - squared; + + result.X = (((value1.X * part1) + (value2.X * part2)) + (tangent1.X * part3)) + (tangent2.X * part4); + result.Y = (((value1.Y * part1) + (value2.Y * part2)) + (tangent1.Y * part3)) + (tangent2.Y * part4); + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// The result of the Hermite spline interpolation. + public static Vec2 Hermite(Vec2 value1, Vec2 tangent1, Vec2 value2, Vec2 tangent2, float amount) + { + Vec2 result; + Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result); + return result; + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// When the method completes, contains the result of the Catmull-Rom interpolation. + public static void CatmullRom(ref Vec2 value1, ref Vec2 value2, ref Vec2 value3, ref Vec2 value4, float amount, out Vec2 result) + { + float squared = amount * amount; + float cubed = amount * squared; + + result.X = 0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * squared)) + + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * cubed)); + + result.Y = 0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * squared)) + + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * cubed)); + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// A vector that is the result of the Catmull-Rom interpolation. + public static Vec2 CatmullRom(Vec2 value1, Vec2 value2, Vec2 value3, Vec2 value4, float amount) + { + Vec2 result; + CatmullRom(ref value1, ref value2, ref value3, ref value4, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Max(ref Vec2 left, ref Vec2 right, out Vec2 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Max(Vec2 left, Vec2 right) + { + Vec2 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Min(ref Vec2 left, ref Vec2 right, out Vec2 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 Min(Vec2 left, Vec2 right) + { + Vec2 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// When the method completes, contains the reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static void Reflect(ref Vec2 vector, ref Vec2 normal, out Vec2 result) + { + float dot = (vector.X * normal.X) + (vector.Y * normal.Y); + + result.X = vector.X - ((2.0f * dot) * normal.X); + result.Y = vector.Y - ((2.0f * dot) * normal.Y); + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// The reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static Vec2 Reflect(Vec2 vector, Vec2 normal) + { + Vec2 result; + Reflect(ref vector, ref normal, out result); + return result; + } + + /// + /// Orthogonalizes a list of vectors. + /// + /// The list of orthogonalized vectors. + /// The list of vectors to orthogonalize. + /// + /// Orthogonalization is the process of making all vectors orthogonal to each other. This + /// means that any given vector in the list will be orthogonal to any other given vector in the + /// list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthogonalize(Vec2[] destination, params Vec2[] source) + { + //Uses the modified Gram-Schmidt process. + //q1 = m1 + //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 + //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 + //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Vec2 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= (Vec2.Dot(destination[r], newvector) / Vec2.Dot(destination[r], destination[r])) * destination[r]; + } + + destination[i] = newvector; + } + } + + /// + /// Orthonormalizes a list of vectors. + /// + /// The list of orthonormalized vectors. + /// The list of vectors to orthonormalize. + /// + /// Orthonormalization is the process of making all vectors orthogonal to each + /// other and making all vectors of unit length. This means that any given vector will + /// be orthogonal to any other given vector in the list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthonormalize(Vec2[] destination, params Vec2[] source) + { + //Uses the modified Gram-Schmidt process. + //Because we are making unit vectors, we can optimize the math for orthogonalization + //and simplify the projection operation to remove the division. + //q1 = m1 / |m1| + //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| + //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| + //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Vec2 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= Vec2.Dot(destination[r], newvector) * destination[r]; + } + + newvector.Normalize(); + destination[i] = newvector; + } + } + + /// + /// Transforms a 2D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// When the method completes, contains the transformed . + public static void Transform(ref Vec2 vector, ref Quaternion rotation, out Vec2 result) + { + float x = rotation.X + rotation.X; + float y = rotation.Y + rotation.Y; + float z = rotation.Z + rotation.Z; + float wz = rotation.W * z; + float xx = rotation.X * x; + float xy = rotation.X * y; + float yy = rotation.Y * y; + float zz = rotation.Z * z; + + result = new Vec2((vector.X * (1.0f - yy - zz)) + (vector.Y * (xy - wz)), (vector.X * (xy + wz)) + (vector.Y * (1.0f - xx - zz))); + } + + /// + /// Transforms a 2D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// The transformed . + public static Vec2 Transform(Vec2 vector, Quaternion rotation) + { + Vec2 result; + Transform(ref vector, ref rotation, out result); + return result; + } + + /// + /// Transforms an array of vectors by the given rotation. + /// + /// The array of vectors to transform. + /// The rotation to apply. + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Vec2[] source, ref Quaternion rotation, Vec2[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + float x = rotation.X + rotation.X; + float y = rotation.Y + rotation.Y; + float z = rotation.Z + rotation.Z; + float wz = rotation.W * z; + float xx = rotation.X * x; + float xy = rotation.X * y; + float yy = rotation.Y * y; + float zz = rotation.Z * z; + + float num1 = (1.0f - yy - zz); + float num2 = (xy - wz); + float num3 = (xy + wz); + float num4 = (1.0f - xx - zz); + + for (int i = 0; i < source.Length; ++i) + { + destination[i] = new Vec2( + (source[i].X * num1) + (source[i].Y * num2), + (source[i].X * num3) + (source[i].Y * num4)); + } + } + + /// + /// Transforms a 2D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Vec2 vector, ref Matrix transform, out Vector4 result) + { + result = new Vector4( + (vector.X * transform.M11) + (vector.Y * transform.M21) + transform.M41, + (vector.X * transform.M12) + (vector.Y * transform.M22) + transform.M42, + (vector.X * transform.M13) + (vector.Y * transform.M23) + transform.M43, + (vector.X * transform.M14) + (vector.Y * transform.M24) + transform.M44); + } + + /// + /// Transforms a 2D vector by the given . + /// + /// The source vector. + /// The transformation . + /// The transformed . + public static Vector4 Transform(Vec2 vector, Matrix transform) + { + Vector4 result; + Transform(ref vector, ref transform, out result); + return result; + } + + /// + /// Transforms an array of 2D vectors by the given . + /// + /// The array of vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Vec2[] source, ref Matrix transform, Vector4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Transform(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// When the method completes, contains the transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(ref Vec2 coordinate, ref Matrix transform, out Vec2 result) + { + Vector4 vector = new Vector4(); + vector.X = (coordinate.X * transform.M11) + (coordinate.Y * transform.M21) + transform.M41; + vector.Y = (coordinate.X * transform.M12) + (coordinate.Y * transform.M22) + transform.M42; + vector.Z = (coordinate.X * transform.M13) + (coordinate.Y * transform.M23) + transform.M43; + vector.W = 1f / ((coordinate.X * transform.M14) + (coordinate.Y * transform.M24) + transform.M44); + + result = new Vec2(vector.X * vector.W, vector.Y * vector.W); + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// The transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static Vec2 TransformCoordinate(Vec2 coordinate, Matrix transform) + { + Vec2 result; + TransformCoordinate(ref coordinate, ref transform, out result); + return result; + } + + /// + /// Performs a coordinate transformation on an array of vectors using the given . + /// + /// The array of coordinate vectors to trasnform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(Vec2[] source, ref Matrix transform, Vec2[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformCoordinate(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// When the method completes, contains the transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(ref Vec2 normal, ref Matrix transform, out Vec2 result) + { + result = new Vec2( + (normal.X * transform.M11) + (normal.Y * transform.M21), + (normal.X * transform.M12) + (normal.Y * transform.M22)); + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// The transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static Vec2 TransformNormal(Vec2 normal, Matrix transform) + { + Vec2 result; + TransformNormal(ref normal, ref transform, out result); + return result; + } + + /// + /// Performs a normal transformation on an array of vectors using the given . + /// + /// The array of normal vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(Vec2[] source, ref Matrix transform, Vec2[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformNormal(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator +(Vec2 left, Vec2 right) + { + return new Vec2(left.X + right.X, left.Y + right.Y); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator +(Vec2 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator -(Vec2 left, Vec2 right) + { + return new Vec2(left.X - right.X, left.Y - right.Y); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator -(Vec2 value) + { + return new Vec2(-value.X, -value.Y); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to multiply. + /// The second vector to multiply. + /// The multiplication of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator *(Vec2 left, Vec2 right) + { + return new Vec2(left.X * right.X, left.Y * right.Y); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator *(float scale, Vec2 value) + { + return new Vec2(value.X * scale, value.Y * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator *(Vec2 value, float scale) + { + return new Vec2(value.X * scale, value.Y * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator /(Vec2 value, float scale) + { + return new Vec2(value.X / scale, value.Y / scale); + } + + /// + /// Divides a numerator by a vector. + /// + /// The numerator. + /// The value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator /(float numerator, Vec2 value) + { + return new Vec2(numerator / value.X, numerator / value.Y); + } + + /// + /// Divides a vector by the given vector, component-wise. + /// + /// The vector to scale. + /// The by. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vec2 operator /(Vec2 value, Vec2 by) + { + return new Vec2(value.X / by.X, value.Y / by.Y); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Vec2 left, Vec2 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Vec2 left, Vec2 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Vec2 value) + { + return new Vector3(value, 0.0f); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Vec2 value) + { + return new Vector4(value, 0.0f, 0.0f); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X, Y); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X.ToString(format, CultureInfo.CurrentCulture), Y.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1}", X, Y); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1}", X.ToString(format, formatProvider), Y.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Vec2 other) + { + return ((float)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Vec2)value); + } + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Point(Vector2 value) + { + return new System.Windows.Point(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector2(System.Windows.Point value) + { + return new Vector2((float)value.X, (float)value.Y); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Vector2(Vector2 value) + { + return new Microsoft.Xna.Framework.Vector2(value.X, value.Y); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vector2(Microsoft.Xna.Framework.Vector2 value) + { + return new Vector2(value.X, value.Y); + } +#endif + } +} diff --git a/math/Vector3.cs b/math/Vector3.cs new file mode 100644 index 0000000..c3f68c2 --- /dev/null +++ b/math/Vector3.cs @@ -0,0 +1,1737 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a three dimensional mathematical vector. + /// + [DataContract( Name = "float3")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Vector3 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Vector3 Zero = new Vector3(); + + /// + /// The X unit (1, 0, 0). + /// + public static readonly Vector3 UnitX = new Vector3(1.0f, 0.0f, 0.0f); + + /// + /// The Y unit (0, 1, 0). + /// + public static readonly Vector3 UnitY = new Vector3(0.0f, 1.0f, 0.0f); + + /// + /// The Z unit (0, 0, 1). + /// + public static readonly Vector3 UnitZ = new Vector3(0.0f, 0.0f, 1.0f); + + /// + /// A with all of its components set to one. + /// + public static readonly Vector3 One = new Vector3(1.0f, 1.0f, 1.0f); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public float X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public float Y; + + /// + /// The Z component of the vector. + /// + [DataMember( Order = 2 )] + public float Z; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Vector3(float value) + { + X = value; + Y = value; + Z = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + /// Initial value for the Z component of the vector. + public Vector3(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + /// Initial value for the Z component of the vector. + public Vector3(Vec2 value, float z) + { + X = value.X; + Y = value.Y; + Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, and Z components of the vector. This must be an array with three elements. + /// Thrown when is null. + /// Thrown when contains more or less than three elements. + public Vector3(float[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 3) + throw new ArgumentOutOfRangeException("values", "There must be three and only three input values for Vector3."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + } + + /// + /// Gets a value indicting whether this instance is normalized. + /// + public bool IsNormalized + { + get { return Math.Abs((X * X) + (Y * Y) + (Z * Z) - 1f) < MathUtil.ZeroTolerance; } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, or Z component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, and 2 for the Z component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 2]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Vector3 run from 0 to 2, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Vector3 run from 0 to 2, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Length() + { + return (float)Math.Sqrt((X * X) + (Y * Y) + (Z * Z)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float LengthSquared() + { + return (X * X) + (Y * Y) + (Z * Z); + } + + /// + /// Converts the vector into a unit vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Normalize() + { + float length = Length(); + if (length > MathUtil.ZeroTolerance) + { + float inv = 1.0f / length; + X *= inv; + Y *= inv; + Z *= inv; + } + } + + /// + /// Raises the exponent for each components. + /// + /// The exponent. + public void Pow(float exponent) + { + X = (float)Math.Pow(X, exponent); + Y = (float)Math.Pow(Y, exponent); + Z = (float)Math.Pow(Z, exponent); + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A three-element array containing the components of the vector. + public float[] ToArray() + { + return new float[] { X, Y, Z }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Vector3 left, ref Vector3 right, out Vector3 result) + { + result = new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Add(Vector3 left, Vector3 right) + { + return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Subtract(ref Vector3 left, ref Vector3 right, out Vector3 result) + { + result = new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Subtract(Vector3 left, Vector3 right) + { + return new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(ref Vector3 value, float scale, out Vector3 result) + { + result = new Vector3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Multiply(Vector3 value, float scale) + { + return new Vector3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Modulate(ref Vector3 left, ref Vector3 right, out Vector3 result) + { + result = new Vector3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Modulate(Vector3 left, Vector3 right) + { + return new Vector3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(ref Vector3 value, float scale, out Vector3 result) + { + result = new Vector3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Divide(Vector3 value, float scale) + { + return new Vector3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// When the method completes, contains the demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Demodulate(ref Vector3 left, ref Vector3 right, out Vector3 result) + { + result = new Vector3(left.X / right.X, left.Y / right.Y, left.Z / right.Z); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// The demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Demodulate(Vector3 left, Vector3 right) + { + return new Vector3(left.X / right.X, left.Y / right.Y, left.Z / right.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Negate(ref Vector3 value, out Vector3 result) + { + result = new Vector3(-value.X, -value.Y, -value.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Negate(Vector3 value) + { + return new Vector3(-value.X, -value.Y, -value.Z); + } + + /// + /// Returns a containing the 3D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 3D triangle. + /// + /// A containing the 3D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// When the method completes, contains the 3D Cartesian coordinates of the specified point. + public static void Barycentric(ref Vector3 value1, ref Vector3 value2, ref Vector3 value3, float amount1, float amount2, out Vector3 result) + { + result = new Vector3( + (value1.X + (amount1 * (value2.X - value1.X))) + (amount2 * (value3.X - value1.X)), + (value1.Y + (amount1 * (value2.Y - value1.Y))) + (amount2 * (value3.Y - value1.Y)), + (value1.Z + (amount1 * (value2.Z - value1.Z))) + (amount2 * (value3.Z - value1.Z))); + } + + /// + /// Returns a containing the 3D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 3D triangle. + /// + /// A containing the 3D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 3D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// A new containing the 3D Cartesian coordinates of the specified point. + public static Vector3 Barycentric(Vector3 value1, Vector3 value2, Vector3 value3, float amount1, float amount2) + { + Vector3 result; + Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); + return result; + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Vector3 value, ref Vector3 min, ref Vector3 max, out Vector3 result) + { + float x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + float y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + float z = value.Z; + z = (z > max.Z) ? max.Z : z; + z = (z < min.Z) ? min.Z : z; + + result = new Vector3(x, y, z); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Vector3 Clamp(Vector3 value, Vector3 min, Vector3 max) + { + Vector3 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the cross product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains he cross product of the two vectors. + public static void Cross(ref Vector3 left, ref Vector3 right, out Vector3 result) + { + result = new Vector3( + (left.Y * right.Z) - (left.Z * right.Y), + (left.Z * right.X) - (left.X * right.Z), + (left.X * right.Y) - (left.Y * right.X)); + } + + /// + /// Calculates the cross product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The cross product of the two vectors. + public static Vector3 Cross(Vector3 left, Vector3 right) + { + Vector3 result; + Cross(ref left, ref right, out result); + return result; + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static void Distance(ref Vector3 value1, ref Vector3 value2, out float result) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + + result = (float)Math.Sqrt((x * x) + (y * y) + (z * z)); + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static float Distance(Vector3 value1, Vector3 value2) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + + return (float)Math.Sqrt((x * x) + (y * y) + (z * z)); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static void DistanceSquared(ref Vector3 value1, ref Vector3 value2, out float result) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + + result = (x * x) + (y * y) + (z * z); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static float DistanceSquared(Vector3 value1, Vector3 value2) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + + return (x * x) + (y * y) + (z * z); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dot(ref Vector3 left, ref Vector3 right, out float result) + { + result = (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(Vector3 left, Vector3 right) + { + return (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// When the method completes, contains the normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ref Vector3 value, out Vector3 result) + { + result = value; + result.Normalize(); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// The normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Normalize(Vector3 value) + { + value.Normalize(); + return value; + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Vector3 start, ref Vector3 end, float amount, out Vector3 result) + { + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Vector3 Lerp(Vector3 start, Vector3 end, float amount) + { + Vector3 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Vector3 start, ref Vector3 end, float amount, out Vector3 result) + { + amount = (amount > 1.0f) ? 1.0f : ((amount < 0.0f) ? 0.0f : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Vector3 SmoothStep(Vector3 start, Vector3 end, float amount) + { + Vector3 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// When the method completes, contains the result of the Hermite spline interpolation. + public static void Hermite(ref Vector3 value1, ref Vector3 tangent1, ref Vector3 value2, ref Vector3 tangent2, float amount, out Vector3 result) + { + float squared = amount * amount; + float cubed = amount * squared; + float part1 = ((2.0f * cubed) - (3.0f * squared)) + 1.0f; + float part2 = (-2.0f * cubed) + (3.0f * squared); + float part3 = (cubed - (2.0f * squared)) + amount; + float part4 = cubed - squared; + + result.X = (((value1.X * part1) + (value2.X * part2)) + (tangent1.X * part3)) + (tangent2.X * part4); + result.Y = (((value1.Y * part1) + (value2.Y * part2)) + (tangent1.Y * part3)) + (tangent2.Y * part4); + result.Z = (((value1.Z * part1) + (value2.Z * part2)) + (tangent1.Z * part3)) + (tangent2.Z * part4); + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// The result of the Hermite spline interpolation. + public static Vector3 Hermite(Vector3 value1, Vector3 tangent1, Vector3 value2, Vector3 tangent2, float amount) + { + Vector3 result; + Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result); + return result; + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// When the method completes, contains the result of the Catmull-Rom interpolation. + public static void CatmullRom(ref Vector3 value1, ref Vector3 value2, ref Vector3 value3, ref Vector3 value4, float amount, out Vector3 result) + { + float squared = amount * amount; + float cubed = amount * squared; + + result.X = 0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * squared)) + + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * cubed)); + + result.Y = 0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * squared)) + + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * cubed)); + + result.Z = 0.5f * ((((2.0f * value2.Z) + ((-value1.Z + value3.Z) * amount)) + + (((((2.0f * value1.Z) - (5.0f * value2.Z)) + (4.0f * value3.Z)) - value4.Z) * squared)) + + ((((-value1.Z + (3.0f * value2.Z)) - (3.0f * value3.Z)) + value4.Z) * cubed)); + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// A vector that is the result of the Catmull-Rom interpolation. + public static Vector3 CatmullRom(Vector3 value1, Vector3 value2, Vector3 value3, Vector3 value4, float amount) + { + Vector3 result; + CatmullRom(ref value1, ref value2, ref value3, ref value4, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Max(ref Vector3 left, ref Vector3 right, out Vector3 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + result.Z = (left.Z > right.Z) ? left.Z : right.Z; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Max(Vector3 left, Vector3 right) + { + Vector3 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Min(ref Vector3 left, ref Vector3 right, out Vector3 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + result.Z = (left.Z < right.Z) ? left.Z : right.Z; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Min(Vector3 left, Vector3 right) + { + Vector3 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Projects a 3D vector from object space into screen space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// When the method completes, contains the vector in screen space. + public static void Project(ref Vector3 vector, float x, float y, float width, float height, float minZ, float maxZ, ref Matrix worldViewProjection, out Vector3 result) + { + Vector3 v; + TransformCoordinate(ref vector, ref worldViewProjection, out v); + + result = new Vector3(((1.0f + v.X) * 0.5f * width) + x, ((1.0f - v.Y) * 0.5f * height) + y, (v.Z * (maxZ - minZ)) + minZ); + } + + /// + /// Projects a 3D vector from object space into screen space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// The vector in screen space. + public static Vector3 Project(Vector3 vector, float x, float y, float width, float height, float minZ, float maxZ, Matrix worldViewProjection) + { + Vector3 result; + Project(ref vector, x, y, width, height, minZ, maxZ, ref worldViewProjection, out result); + return result; + } + + /// + /// Projects a 3D vector from screen space into object space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// When the method completes, contains the vector in object space. + public static void Unproject(ref Vector3 vector, float x, float y, float width, float height, float minZ, float maxZ, ref Matrix worldViewProjection, out Vector3 result) + { + Vector3 v = new Vector3(); + Matrix matrix; + Matrix.Invert(ref worldViewProjection, out matrix); + + v.X = (((vector.X - x) / width) * 2.0f) - 1.0f; + v.Y = -((((vector.Y - y) / height) * 2.0f) - 1.0f); + v.Z = (vector.Z - minZ) / (maxZ - minZ); + + TransformCoordinate(ref v, ref matrix, out result); + } + + /// + /// Projects a 3D vector from screen space into object space. + /// + /// The vector to project. + /// The X position of the viewport. + /// The Y position of the viewport. + /// The width of the viewport. + /// The height of the viewport. + /// The minimum depth of the viewport. + /// The maximum depth of the viewport. + /// The combined world-view-projection matrix. + /// The vector in object space. + public static Vector3 Unproject(Vector3 vector, float x, float y, float width, float height, float minZ, float maxZ, Matrix worldViewProjection) + { + Vector3 result; + Unproject(ref vector, x, y, width, height, minZ, maxZ, ref worldViewProjection, out result); + return result; + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// When the method completes, contains the reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static void Reflect(ref Vector3 vector, ref Vector3 normal, out Vector3 result) + { + float dot = (vector.X * normal.X) + (vector.Y * normal.Y) + (vector.Z * normal.Z); + + result.X = vector.X - ((2.0f * dot) * normal.X); + result.Y = vector.Y - ((2.0f * dot) * normal.Y); + result.Z = vector.Z - ((2.0f * dot) * normal.Z); + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// Normal of the surface. + /// The reflected vector. + /// Reflect only gives the direction of a reflection off a surface, it does not determine + /// whether the original vector was close enough to the surface to hit it. + public static Vector3 Reflect(Vector3 vector, Vector3 normal) + { + Vector3 result; + Reflect(ref vector, ref normal, out result); + return result; + } + + /// + /// Orthogonalizes a list of vectors. + /// + /// The list of orthogonalized vectors. + /// The list of vectors to orthogonalize. + /// + /// Orthogonalization is the process of making all vectors orthogonal to each other. This + /// means that any given vector in the list will be orthogonal to any other given vector in the + /// list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthogonalize(Vector3[] destination, params Vector3[] source) + { + //Uses the modified Gram-Schmidt process. + //q1 = m1 + //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 + //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 + //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Vector3 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= (Vector3.Dot(destination[r], newvector) / Vector3.Dot(destination[r], destination[r])) * destination[r]; + } + + destination[i] = newvector; + } + } + + /// + /// Orthonormalizes a list of vectors. + /// + /// The list of orthonormalized vectors. + /// The list of vectors to orthonormalize. + /// + /// Orthonormalization is the process of making all vectors orthogonal to each + /// other and making all vectors of unit length. This means that any given vector will + /// be orthogonal to any other given vector in the list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthonormalize(Vector3[] destination, params Vector3[] source) + { + //Uses the modified Gram-Schmidt process. + //Because we are making unit vectors, we can optimize the math for orthogonalization + //and simplify the projection operation to remove the division. + //q1 = m1 / |m1| + //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| + //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| + //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Vector3 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= Vector3.Dot(destination[r], newvector) * destination[r]; + } + + newvector.Normalize(); + destination[i] = newvector; + } + } + + /// + /// Transforms a 3D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// When the method completes, contains the transformed . + public static void Transform(ref Vector3 vector, ref Quaternion rotation, out Vector3 result) + { + float x = rotation.X + rotation.X; + float y = rotation.Y + rotation.Y; + float z = rotation.Z + rotation.Z; + float wx = rotation.W * x; + float wy = rotation.W * y; + float wz = rotation.W * z; + float xx = rotation.X * x; + float xy = rotation.X * y; + float xz = rotation.X * z; + float yy = rotation.Y * y; + float yz = rotation.Y * z; + float zz = rotation.Z * z; + + result = new Vector3( + ((vector.X * ((1.0f - yy) - zz)) + (vector.Y * (xy - wz))) + (vector.Z * (xz + wy)), + ((vector.X * (xy + wz)) + (vector.Y * ((1.0f - xx) - zz))) + (vector.Z * (yz - wx)), + ((vector.X * (xz - wy)) + (vector.Y * (yz + wx))) + (vector.Z * ((1.0f - xx) - yy))); + } + + /// + /// Transforms a 3D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// The transformed . + public static Vector3 Transform(Vector3 vector, Quaternion rotation) + { + Vector3 result; + Transform(ref vector, ref rotation, out result); + return result; + } + + /// + /// Transforms an array of vectors by the given rotation. + /// + /// The array of vectors to transform. + /// The rotation to apply. + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Vector3[] source, ref Quaternion rotation, Vector3[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + float x = rotation.X + rotation.X; + float y = rotation.Y + rotation.Y; + float z = rotation.Z + rotation.Z; + float wx = rotation.W * x; + float wy = rotation.W * y; + float wz = rotation.W * z; + float xx = rotation.X * x; + float xy = rotation.X * y; + float xz = rotation.X * z; + float yy = rotation.Y * y; + float yz = rotation.Y * z; + float zz = rotation.Z * z; + + float num1 = ((1.0f - yy) - zz); + float num2 = (xy - wz); + float num3 = (xz + wy); + float num4 = (xy + wz); + float num5 = ((1.0f - xx) - zz); + float num6 = (yz - wx); + float num7 = (xz - wy); + float num8 = (yz + wx); + float num9 = ((1.0f - xx) - yy); + + for (int i = 0; i < source.Length; ++i) + { + destination[i] = new Vector3( + ((source[i].X * num1) + (source[i].Y * num2)) + (source[i].Z * num3), + ((source[i].X * num4) + (source[i].Y * num5)) + (source[i].Z * num6), + ((source[i].X * num7) + (source[i].Y * num8)) + (source[i].Z * num9)); + } + } + + /// + /// Transforms a 3D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Vector3 vector, ref Matrix transform, out Vector4 result) + { + result = new Vector4( + (vector.X * transform.M11) + (vector.Y * transform.M21) + (vector.Z * transform.M31) + transform.M41, + (vector.X * transform.M12) + (vector.Y * transform.M22) + (vector.Z * transform.M32) + transform.M42, + (vector.X * transform.M13) + (vector.Y * transform.M23) + (vector.Z * transform.M33) + transform.M43, + (vector.X * transform.M14) + (vector.Y * transform.M24) + (vector.Z * transform.M34) + transform.M44); + } + + /// + /// Transforms a 3D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Vector3 vector, ref Matrix transform, out Vector3 result) + { + result = new Vector3( + (vector.X * transform.M11) + (vector.Y * transform.M21) + (vector.Z * transform.M31) + transform.M41, + (vector.X * transform.M12) + (vector.Y * transform.M22) + (vector.Z * transform.M32) + transform.M42, + (vector.X * transform.M13) + (vector.Y * transform.M23) + (vector.Z * transform.M33) + transform.M43); + } + + /// + /// Transforms a 3D vector by the given . + /// + /// The source vector. + /// The transformation . + /// The transformed . + public static Vector4 Transform(Vector3 vector, Matrix transform) + { + Vector4 result; + Transform(ref vector, ref transform, out result); + return result; + } + + /// + /// Transforms an array of 3D vectors by the given . + /// + /// The array of vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Vector3[] source, ref Matrix transform, Vector4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Transform(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// When the method completes, contains the transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(ref Vector3 coordinate, ref Matrix transform, out Vector3 result) + { + var invW = 1f / ((coordinate.X * transform.M14) + (coordinate.Y * transform.M24) + (coordinate.Z * transform.M34) + transform.M44); + result = new Vector3( + ((coordinate.X * transform.M11) + (coordinate.Y * transform.M21) + (coordinate.Z * transform.M31) + transform.M41) * invW, + ((coordinate.X * transform.M12) + (coordinate.Y * transform.M22) + (coordinate.Z * transform.M32) + transform.M42) * invW, + ((coordinate.X * transform.M13) + (coordinate.Y * transform.M23) + (coordinate.Z * transform.M33) + transform.M43) * invW); + } + + /// + /// Performs a coordinate transformation using the given . + /// + /// The coordinate vector to transform. + /// The transformation . + /// The transformed coordinates. + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static Vector3 TransformCoordinate(Vector3 coordinate, Matrix transform) + { + Vector3 result; + TransformCoordinate(ref coordinate, ref transform, out result); + return result; + } + + /// + /// Performs a coordinate transformation on an array of vectors using the given . + /// + /// The array of coordinate vectors to trasnform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A coordinate transform performs the transformation with the assumption that the w component + /// is one. The four dimensional vector obtained from the transformation operation has each + /// component in the vector divided by the w component. This forces the wcomponent to be one and + /// therefore makes the vector homogeneous. The homogeneous vector is often prefered when working + /// with coordinates as the w component can safely be ignored. + /// + public static void TransformCoordinate(Vector3[] source, ref Matrix transform, Vector3[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformCoordinate(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// When the method completes, contains the transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(ref Vector3 normal, ref Matrix transform, out Vector3 result) + { + result = new Vector3( + (normal.X * transform.M11) + (normal.Y * transform.M21) + (normal.Z * transform.M31), + (normal.X * transform.M12) + (normal.Y * transform.M22) + (normal.Z * transform.M32), + (normal.X * transform.M13) + (normal.Y * transform.M23) + (normal.Z * transform.M33)); + } + + /// + /// Performs a normal transformation using the given . + /// + /// The normal vector to transform. + /// The transformation . + /// The transformed normal. + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static Vector3 TransformNormal(Vector3 normal, Matrix transform) + { + Vector3 result; + TransformNormal(ref normal, ref transform, out result); + return result; + } + + /// + /// Performs a normal transformation on an array of vectors using the given . + /// + /// The array of normal vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + /// + /// A normal transform performs the transformation with the assumption that the w component + /// is zero. This causes the fourth row and fourth collumn of the matrix to be unused. The + /// end result is a vector that is not translated, but all other transformation properties + /// apply. This is often prefered for normal vectors as normals purely represent direction + /// rather than location because normal vectors should not be translated. + /// + public static void TransformNormal(Vector3[] source, ref Matrix transform, Vector3[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + TransformNormal(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Calculate the yaw/pitch/roll rotation equivalent to the provided quaterion. + /// + /// The input rotation as quaternion + /// The equivation yaw/pitch/roll rotation + public static Vector3 RotationYawPitchRoll(Quaternion quaternion) + { + Vector3 yawPitchRoll; + Quaternion.RotationYawPitchRoll(ref quaternion, out yawPitchRoll.X, out yawPitchRoll.Y, out yawPitchRoll.Z); + return yawPitchRoll; + } + + /// + /// Calculate the yaw/pitch/roll rotation equivalent to the provided quaterion. + /// + /// The input rotation as quaternion + /// The equivation yaw/pitch/roll rotation + public static void RotationYawPitchRoll(ref Quaternion quaternion, out Vector3 yawPitchRoll) + { + Quaternion.RotationYawPitchRoll(ref quaternion, out yawPitchRoll.X, out yawPitchRoll.Y, out yawPitchRoll.Z); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator +(Vector3 left, Vector3 right) + { + return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator +(Vector3 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(Vector3 left, Vector3 right) + { + return new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(Vector3 value) + { + return new Vector3(-value.X, -value.Y, -value.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(float scale, Vector3 value) + { + return new Vector3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(Vector3 value, float scale) + { + return new Vector3(value.X * scale, value.Y * scale, value.Z * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to multiply. + /// The second vector to multiply. + /// The multiplication of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(Vector3 left, Vector3 right) + { + return new Vector3(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + } + + /// + /// Adds a vector with the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The vector offset. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator +(Vector3 value, float scale) + { + return new Vector3(value.X + scale, value.Y + scale, value.Z + scale); + } + + /// + /// Substracts a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The vector offset. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(Vector3 value, float scale) + { + return new Vector3(value.X - scale, value.Y - scale, value.Z - scale); + } + + /// + /// Divides a numerator by a vector. + /// + /// The numerator. + /// The value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(float numerator, Vector3 value) + { + return new Vector3(numerator / value.X, numerator / value.Y, numerator / value.Z); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(Vector3 value, float scale) + { + return new Vector3(value.X / scale, value.Y / scale, value.Z / scale); + } + + /// + /// Divides a vector by the given vector, component-wise. + /// + /// The vector to scale. + /// The by. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(Vector3 value, Vector3 by) + { + return new Vector3(value.X / by.X, value.Y / by.Y, value.Z / by.Z); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Vector3 left, Vector3 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Vector3 left, Vector3 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(Vector3 value) + { + return new Vec2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(Vector3 value) + { + return new Vector4(value, 0.0f); + } + + /// + /// Tests whether one 3D vector is near another 3D vector. + /// + /// The left vector. + /// The right vector. + /// The epsilon. + /// true if left and right are near another 3D, false otherwise + public static bool NearEqual(Vector3 left, Vector3 right, Vector3 epsilon) + { + return NearEqual(ref left, ref right, ref epsilon); + } + + /// + /// Tests whether one 3D vector is near another 3D vector. + /// + /// The left vector. + /// The right vector. + /// The epsilon. + /// true if left and right are near another 3D, false otherwise + public static bool NearEqual(ref Vector3 left, ref Vector3 right, ref Vector3 epsilon) + { + return MathUtil.WithinEpsilon(left.X, right.X, epsilon.X) && + MathUtil.WithinEpsilon(left.Y, right.Y, epsilon.Y) && + MathUtil.WithinEpsilon(left.Z, right.Z, epsilon.Z); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}", X, Y, Z); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}", X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2}", X, Y, Z); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Vector3 other) + { + return ((float)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Z - Z) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Vector3)value); + } + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Vector3D(Vector3 value) + { + return new System.Windows.Media.Media3D.Vector3D(value.X, value.Y, value.Z); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(System.Windows.Media.Media3D.Vector3D value) + { + return new Vector3((float)value.X, (float)value.Y, (float)value.Z); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Vector3(Vector3 value) + { + return new Microsoft.Xna.Framework.Vector3(value.X, value.Y, value.Z); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vector3(Microsoft.Xna.Framework.Vector3 value) + { + return new Vector3(value.X, value.Y, value.Z); + } +#endif + } +} diff --git a/math/Vector4.cs b/math/Vector4.cs new file mode 100644 index 0000000..b15c73d --- /dev/null +++ b/math/Vector4.cs @@ -0,0 +1,1410 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +// +// ----------------------------------------------------------------------------- +// Original code from SlimMath project. http://code.google.com/p/slimmath/ +// Greetings to SlimDX Group. Original code published with the following license: +// ----------------------------------------------------------------------------- +/* +* Copyright (c) 2007-2011 SlimDX Group +* +* 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. +*/ +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace math +{ + /// + /// Represents a four dimensional mathematical vector. + /// + [DataContract( Name = "float4")] + [DataStyle(DataStyle.Compact)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct Vector4 : IEquatable, IFormattable + { + /// + /// The size of the type, in bytes. + /// + public static readonly int SizeInBytes = Utilities.SizeOf(); + + /// + /// A with all of its components set to zero. + /// + public static readonly Vector4 Zero = new Vector4(); + + /// + /// The X unit (1, 0, 0, 0). + /// + public static readonly Vector4 UnitX = new Vector4(1.0f, 0.0f, 0.0f, 0.0f); + + /// + /// The Y unit (0, 1, 0, 0). + /// + public static readonly Vector4 UnitY = new Vector4(0.0f, 1.0f, 0.0f, 0.0f); + + /// + /// The Z unit (0, 0, 1, 0). + /// + public static readonly Vector4 UnitZ = new Vector4(0.0f, 0.0f, 1.0f, 0.0f); + + /// + /// The W unit (0, 0, 0, 1). + /// + public static readonly Vector4 UnitW = new Vector4(0.0f, 0.0f, 0.0f, 1.0f); + + /// + /// A with all of its components set to one. + /// + public static readonly Vector4 One = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); + + /// + /// The X component of the vector. + /// + [DataMember( Order = 0 )] + public float X; + + /// + /// The Y component of the vector. + /// + [DataMember( Order = 1 )] + public float Y; + + /// + /// The Z component of the vector. + /// + [DataMember( Order = 2 )] + public float Z; + + /// + /// The W component of the vector. + /// + [DataMember( Order = 3 )] + public float W; + + /// + /// Initializes a new instance of the struct. + /// + /// The value that will be assigned to all components. + public Vector4(float value) + { + X = value; + Y = value; + Z = value; + W = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Initial value for the X component of the vector. + /// Initial value for the Y component of the vector. + /// Initial value for the Z component of the vector. + /// Initial value for the W component of the vector. + public Vector4(float x, float y, float z, float w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X, Y, and Z components. + /// Initial value for the W component of the vector. + public Vector4(Vector3 value, float w) + { + X = value.X; + Y = value.Y; + Z = value.Z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + /// Initial value for the Z component of the vector. + /// Initial value for the W component of the vector. + public Vector4(Vec2 value, float z, float w) + { + X = value.X; + Y = value.Y; + Z = z; + W = w; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The values to assign to the X, Y, Z, and W components of the vector. This must be an array with four elements. + /// Thrown when is null. + /// Thrown when contains more or less than four elements. + public Vector4(float[] values) + { + if (values == null) + throw new ArgumentNullException("values"); + if (values.Length != 4) + throw new ArgumentOutOfRangeException("values", "There must be four and only four input values for Vector4."); + + X = values[0]; + Y = values[1]; + Z = values[2]; + W = values[3]; + } + + /// + /// Gets a value indicting whether this instance is normalized. + /// + public bool IsNormalized + { + get { return Math.Abs((X * X) + (Y * Y) + (Z * Z) + (W * W) - 1f) < MathUtil.ZeroTolerance; } + } + + /// + /// Gets or sets the component at the specified index. + /// + /// The value of the X, Y, Z, or W component, depending on the index. + /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component. + /// The value of the component at the specified index. + /// Thrown when the is out of the range [0, 3]. + public float this[int index] + { + get + { + switch (index) + { + case 0: return X; + case 1: return Y; + case 2: return Z; + case 3: return W; + } + + throw new ArgumentOutOfRangeException("index", "Indices for Vector4 run from 0 to 3, inclusive."); + } + + set + { + switch (index) + { + case 0: X = value; break; + case 1: Y = value; break; + case 2: Z = value; break; + case 3: W = value; break; + default: throw new ArgumentOutOfRangeException("index", "Indices for Vector4 run from 0 to 3, inclusive."); + } + } + } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + /// + /// may be preferred when only the relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Length() + { + return (float)Math.Sqrt((X * X) + (Y * Y) + (Z * Z) + (W * W)); + } + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + /// + /// This method may be preferred to when only a relative length is needed + /// and speed is of the essence. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float LengthSquared() + { + return (X * X) + (Y * Y) + (Z * Z) + (W * W); + } + + /// + /// Converts the vector into a unit vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Normalize() + { + float length = Length(); + if (length > MathUtil.ZeroTolerance) + { + float inverse = 1.0f / length; + X *= inverse; + Y *= inverse; + Z *= inverse; + W *= inverse; + } + } + + /// + /// Raises the exponent for each components. + /// + /// The exponent. + public void Pow(float exponent) + { + X = (float)Math.Pow(X, exponent); + Y = (float)Math.Pow(Y, exponent); + Z = (float)Math.Pow(Z, exponent); + W = (float)Math.Pow(W, exponent); + } + + /// + /// Creates an array containing the elements of the vector. + /// + /// A four-element array containing the components of the vector. + public float[] ToArray() + { + return new float[] { X, Y, Z, W }; + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// When the method completes, contains the sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Vector4 left, ref Vector4 right, out Vector4 result) + { + result = new Vector4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Add(Vector4 left, Vector4 right) + { + return new Vector4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// When the method completes, contains the difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Subtract(ref Vector4 left, ref Vector4 right, out Vector4 result) + { + result = new Vector4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Subtract(Vector4 left, Vector4 right) + { + return new Vector4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(ref Vector4 value, float scale, out Vector4 result) + { + result = new Vector4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Multiply(Vector4 value, float scale) + { + return new Vector4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// When the method completes, contains the modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Modulate(ref Vector4 left, ref Vector4 right, out Vector4 result) + { + result = new Vector4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to modulate. + /// The second vector to modulate. + /// The modulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Modulate(Vector4 left, Vector4 right) + { + return new Vector4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// When the method completes, contains the scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(ref Vector4 value, float scale, out Vector4 result) + { + result = new Vector4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Divide(Vector4 value, float scale) + { + return new Vector4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// When the method completes, contains the demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Demodulate(ref Vector4 left, ref Vector4 right, out Vector4 result) + { + result = new Vector4(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W); + } + + /// + /// Demodulates a vector with another by performing component-wise division. + /// + /// The first vector to demodulate. + /// The second vector to demodulate. + /// The demodulated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Demodulate(Vector4 left, Vector4 right) + { + return new Vector4(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// When the method completes, contains a vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Negate(ref Vector4 value, out Vector4 result) + { + result = new Vector4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Negate(Vector4 value) + { + return new Vector4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 4D triangle. + /// + /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// When the method completes, contains the 4D Cartesian coordinates of the specified point. + public static void Barycentric(ref Vector4 value1, ref Vector4 value2, ref Vector4 value3, float amount1, float amount2, out Vector4 result) + { + result = new Vector4( + (value1.X + (amount1 * (value2.X - value1.X))) + (amount2 * (value3.X - value1.X)), + (value1.Y + (amount1 * (value2.Y - value1.Y))) + (amount2 * (value3.Y - value1.Y)), + (value1.Z + (amount1 * (value2.Z - value1.Z))) + (amount2 * (value3.Z - value1.Z)), + (value1.W + (amount1 * (value2.W - value1.W))) + (amount2 * (value3.W - value1.W))); + } + + /// + /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 4D triangle. + /// + /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. + /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. + /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). + /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). + /// A new containing the 4D Cartesian coordinates of the specified point. + public static Vector4 Barycentric(Vector4 value1, Vector4 value2, Vector4 value3, float amount1, float amount2) + { + Vector4 result; + Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); + return result; + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// When the method completes, contains the clamped value. + public static void Clamp(ref Vector4 value, ref Vector4 min, ref Vector4 max, out Vector4 result) + { + float x = value.X; + x = (x > max.X) ? max.X : x; + x = (x < min.X) ? min.X : x; + + float y = value.Y; + y = (y > max.Y) ? max.Y : y; + y = (y < min.Y) ? min.Y : y; + + float z = value.Z; + z = (z > max.Z) ? max.Z : z; + z = (z < min.Z) ? min.Z : z; + + float w = value.W; + w = (w > max.W) ? max.W : w; + w = (w < min.W) ? min.W : w; + + result = new Vector4(x, y, z, w); + } + + /// + /// Restricts a value to be within a specified range. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + public static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max) + { + Vector4 result; + Clamp(ref value, ref min, ref max, out result); + return result; + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static void Distance(ref Vector4 value1, ref Vector4 value2, out float result) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + float w = value1.W - value2.W; + + result = (float)Math.Sqrt((x * x) + (y * y) + (z * z) + (w * w)); + } + + /// + /// Calculates the distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The distance between the two vectors. + /// + /// may be preferred when only the relative distance is needed + /// and speed is of the essence. + /// + public static float Distance(Vector4 value1, Vector4 value2) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + float w = value1.W - value2.W; + + return (float)Math.Sqrt((x * x) + (y * y) + (z * z) + (w * w)); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// When the method completes, contains the squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static void DistanceSquared(ref Vector4 value1, ref Vector4 value2, out float result) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + float w = value1.W - value2.W; + + result = (x * x) + (y * y) + (z * z) + (w * w); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The squared distance between the two vectors. + /// Distance squared is the value before taking the square root. + /// Distance squared can often be used in place of distance if relative comparisons are being made. + /// For example, consider three points A, B, and C. To determine whether B or C is further from A, + /// compare the distance between A and B to the distance between A and C. Calculating the two distances + /// involves two square roots, which are computationally expensive. However, using distance squared + /// provides the same information and avoids calculating two square roots. + /// + public static float DistanceSquared(Vector4 value1, Vector4 value2) + { + float x = value1.X - value2.X; + float y = value1.Y - value2.Y; + float z = value1.Z - value2.Z; + float w = value1.W - value2.W; + + return (x * x) + (y * y) + (z * z) + (w * w); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector + /// Second source vector. + /// When the method completes, contains the dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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); + } + + /// + /// Calculates the dot product of two vectors. + /// + /// First source vector. + /// Second source vector. + /// The dot product of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// When the method completes, contains the normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ref Vector4 value, out Vector4 result) + { + Vector4 temp = value; + result = temp; + result.Normalize(); + } + + /// + /// Converts the vector into a unit vector. + /// + /// The vector to normalize. + /// The normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Normalize(Vector4 value) + { + value.Normalize(); + return value; + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static void Lerp(ref Vector4 start, ref Vector4 end, float amount, out Vector4 result) + { + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + result.W = start.W + ((end.W - start.W) * amount); + } + + /// + /// Performs a linear interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The linear interpolation of the two vectors. + /// + /// This method performs the linear interpolation based on the following formula. + /// start + (end - start) * amount + /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. + /// + public static Vector4 Lerp(Vector4 start, Vector4 end, float amount) + { + Vector4 result; + Lerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the cubic interpolation of the two vectors. + public static void SmoothStep(ref Vector4 start, ref Vector4 end, float amount, out Vector4 result) + { + amount = (amount > 1.0f) ? 1.0f : ((amount < 0.0f) ? 0.0f : amount); + amount = (amount * amount) * (3.0f - (2.0f * amount)); + + result.X = start.X + ((end.X - start.X) * amount); + result.Y = start.Y + ((end.Y - start.Y) * amount); + result.Z = start.Z + ((end.Z - start.Z) * amount); + result.W = start.W + ((end.W - start.W) * amount); + } + + /// + /// Performs a cubic interpolation between two vectors. + /// + /// Start vector. + /// End vector. + /// Value between 0 and 1 indicating the weight of . + /// The cubic interpolation of the two vectors. + public static Vector4 SmoothStep(Vector4 start, Vector4 end, float amount) + { + Vector4 result; + SmoothStep(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// When the method completes, contains the result of the Hermite spline interpolation. + public static void Hermite(ref Vector4 value1, ref Vector4 tangent1, ref Vector4 value2, ref Vector4 tangent2, float amount, out Vector4 result) + { + float squared = amount * amount; + float cubed = amount * squared; + float part1 = ((2.0f * cubed) - (3.0f * squared)) + 1.0f; + float part2 = (-2.0f * cubed) + (3.0f * squared); + float part3 = (cubed - (2.0f * squared)) + amount; + float part4 = cubed - squared; + + result = new Vector4( + (((value1.X * part1) + (value2.X * part2)) + (tangent1.X * part3)) + (tangent2.X * part4), + (((value1.Y * part1) + (value2.Y * part2)) + (tangent1.Y * part3)) + (tangent2.Y * part4), + (((value1.Z * part1) + (value2.Z * part2)) + (tangent1.Z * part3)) + (tangent2.Z * part4), + (((value1.W * part1) + (value2.W * part2)) + (tangent1.W * part3)) + (tangent2.W * part4)); + } + + /// + /// Performs a Hermite spline interpolation. + /// + /// First source position vector. + /// First source tangent vector. + /// Second source position vector. + /// Second source tangent vector. + /// Weighting factor. + /// The result of the Hermite spline interpolation. + public static Vector4 Hermite(Vector4 value1, Vector4 tangent1, Vector4 value2, Vector4 tangent2, float amount) + { + Vector4 result; + Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result); + return result; + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// When the method completes, contains the result of the Catmull-Rom interpolation. + public static void CatmullRom(ref Vector4 value1, ref Vector4 value2, ref Vector4 value3, ref Vector4 value4, float amount, out Vector4 result) + { + float squared = amount * amount; + float cubed = amount * squared; + + result.X = 0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * squared)) + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * cubed)); + result.Y = 0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * squared)) + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * cubed)); + result.Z = 0.5f * ((((2.0f * value2.Z) + ((-value1.Z + value3.Z) * amount)) + (((((2.0f * value1.Z) - (5.0f * value2.Z)) + (4.0f * value3.Z)) - value4.Z) * squared)) + ((((-value1.Z + (3.0f * value2.Z)) - (3.0f * value3.Z)) + value4.Z) * cubed)); + result.W = 0.5f * ((((2.0f * value2.W) + ((-value1.W + value3.W) * amount)) + (((((2.0f * value1.W) - (5.0f * value2.W)) + (4.0f * value3.W)) - value4.W) * squared)) + ((((-value1.W + (3.0f * value2.W)) - (3.0f * value3.W)) + value4.W) * cubed)); + } + + /// + /// Performs a Catmull-Rom interpolation using the specified positions. + /// + /// The first position in the interpolation. + /// The second position in the interpolation. + /// The third position in the interpolation. + /// The fourth position in the interpolation. + /// Weighting factor. + /// A vector that is the result of the Catmull-Rom interpolation. + public static Vector4 CatmullRom(Vector4 value1, Vector4 value2, Vector4 value3, Vector4 value4, float amount) + { + Vector4 result; + CatmullRom(ref value1, ref value2, ref value3, ref value4, amount, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Max(ref Vector4 left, ref Vector4 right, out Vector4 result) + { + result.X = (left.X > right.X) ? left.X : right.X; + result.Y = (left.Y > right.Y) ? left.Y : right.Y; + result.Z = (left.Z > right.Z) ? left.Z : right.Z; + result.W = (left.W > right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the largest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the largest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Max(Vector4 left, Vector4 right) + { + Vector4 result; + Max(ref left, ref right, out result); + return result; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// When the method completes, contains an new vector composed of the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Min(ref Vector4 left, ref Vector4 right, out Vector4 result) + { + result.X = (left.X < right.X) ? left.X : right.X; + result.Y = (left.Y < right.Y) ? left.Y : right.Y; + result.Z = (left.Z < right.Z) ? left.Z : right.Z; + result.W = (left.W < right.W) ? left.W : right.W; + } + + /// + /// Returns a vector containing the smallest components of the specified vectors. + /// + /// The first source vector. + /// The second source vector. + /// A vector containing the smallest components of the source vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Min(Vector4 left, Vector4 right) + { + Vector4 result; + Min(ref left, ref right, out result); + return result; + } + + /// + /// Orthogonalizes a list of vectors. + /// + /// The list of orthogonalized vectors. + /// The list of vectors to orthogonalize. + /// + /// Orthogonalization is the process of making all vectors orthogonal to each other. This + /// means that any given vector in the list will be orthogonal to any other given vector in the + /// list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthogonalize(Vector4[] destination, params Vector4[] source) + { + //Uses the modified Gram-Schmidt process. + //q1 = m1 + //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 + //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 + //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Vector4 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= (Vector4.Dot(destination[r], newvector) / Vector4.Dot(destination[r], destination[r])) * destination[r]; + } + + destination[i] = newvector; + } + } + + /// + /// Orthonormalizes a list of vectors. + /// + /// The list of orthonormalized vectors. + /// The list of vectors to orthonormalize. + /// + /// Orthonormalization is the process of making all vectors orthogonal to each + /// other and making all vectors of unit length. This means that any given vector will + /// be orthogonal to any other given vector in the list. + /// Because this method uses the modified Gram-Schmidt process, the resulting vectors + /// tend to be numerically unstable. The numeric stability decreases according to the vectors + /// position in the list so that the first vector is the most stable and the last vector is the + /// least stable. + /// + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Orthonormalize(Vector4[] destination, params Vector4[] source) + { + //Uses the modified Gram-Schmidt process. + //Because we are making unit vectors, we can optimize the math for orthogonalization + //and simplify the projection operation to remove the division. + //q1 = m1 / |m1| + //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| + //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| + //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| + //q5 = ... + + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Vector4 newvector = source[i]; + + for (int r = 0; r < i; ++r) + { + newvector -= Vector4.Dot(destination[r], newvector) * destination[r]; + } + + newvector.Normalize(); + destination[i] = newvector; + } + } + + /// + /// Transforms a 4D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// When the method completes, contains the transformed . + public static void Transform(ref Vector4 vector, ref Quaternion rotation, out Vector4 result) + { + float x = rotation.X + rotation.X; + float y = rotation.Y + rotation.Y; + float z = rotation.Z + rotation.Z; + float wx = rotation.W * x; + float wy = rotation.W * y; + float wz = rotation.W * z; + float xx = rotation.X * x; + float xy = rotation.X * y; + float xz = rotation.X * z; + float yy = rotation.Y * y; + float yz = rotation.Y * z; + float zz = rotation.Z * z; + + result = new Vector4( + ((vector.X * ((1.0f - yy) - zz)) + (vector.Y * (xy - wz))) + (vector.Z * (xz + wy)), + ((vector.X * (xy + wz)) + (vector.Y * ((1.0f - xx) - zz))) + (vector.Z * (yz - wx)), + ((vector.X * (xz - wy)) + (vector.Y * (yz + wx))) + (vector.Z * ((1.0f - xx) - yy)), + vector.W); + } + + /// + /// Transforms a 4D vector by the given rotation. + /// + /// The vector to rotate. + /// The rotation to apply. + /// The transformed . + public static Vector4 Transform(Vector4 vector, Quaternion rotation) + { + Vector4 result; + Transform(ref vector, ref rotation, out result); + return result; + } + + /// + /// Transforms an array of vectors by the given rotation. + /// + /// The array of vectors to transform. + /// The rotation to apply. + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Vector4[] source, ref Quaternion rotation, Vector4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + float x = rotation.X + rotation.X; + float y = rotation.Y + rotation.Y; + float z = rotation.Z + rotation.Z; + float wx = rotation.W * x; + float wy = rotation.W * y; + float wz = rotation.W * z; + float xx = rotation.X * x; + float xy = rotation.X * y; + float xz = rotation.X * z; + float yy = rotation.Y * y; + float yz = rotation.Y * z; + float zz = rotation.Z * z; + + float num1 = ((1.0f - yy) - zz); + float num2 = (xy - wz); + float num3 = (xz + wy); + float num4 = (xy + wz); + float num5 = ((1.0f - xx) - zz); + float num6 = (yz - wx); + float num7 = (xz - wy); + float num8 = (yz + wx); + float num9 = ((1.0f - xx) - yy); + + for (int i = 0; i < source.Length; ++i) + { + destination[i] = new Vector4( + ((source[i].X * num1) + (source[i].Y * num2)) + (source[i].Z * num3), + ((source[i].X * num4) + (source[i].Y * num5)) + (source[i].Z * num6), + ((source[i].X * num7) + (source[i].Y * num8)) + (source[i].Z * num9), + source[i].W); + } + } + + /// + /// Transforms a 4D vector by the given . + /// + /// The source vector. + /// The transformation . + /// When the method completes, contains the transformed . + public static void Transform(ref Vector4 vector, ref Matrix transform, out Vector4 result) + { + result = new Vector4( + (vector.X * transform.M11) + (vector.Y * transform.M21) + (vector.Z * transform.M31) + (vector.W * transform.M41), + (vector.X * transform.M12) + (vector.Y * transform.M22) + (vector.Z * transform.M32) + (vector.W * transform.M42), + (vector.X * transform.M13) + (vector.Y * transform.M23) + (vector.Z * transform.M33) + (vector.W * transform.M43), + (vector.X * transform.M14) + (vector.Y * transform.M24) + (vector.Z * transform.M34) + (vector.W * transform.M44)); + } + + /// + /// Transforms a 4D vector by the given . + /// + /// The source vector. + /// The transformation . + /// The transformed . + public static Vector4 Transform(Vector4 vector, Matrix transform) + { + Vector4 result; + Transform(ref vector, ref transform, out result); + return result; + } + + /// + /// Transforms an array of 4D vectors by the given . + /// + /// The array of vectors to transform. + /// The transformation . + /// The array for which the transformed vectors are stored. + /// This array may be the same array as . + /// Thrown when or is null. + /// Thrown when is shorter in length than . + public static void Transform(Vector4[] source, ref Matrix transform, Vector4[] destination) + { + if (source == null) + throw new ArgumentNullException("source"); + if (destination == null) + throw new ArgumentNullException("destination"); + if (destination.Length < source.Length) + throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); + + for (int i = 0; i < source.Length; ++i) + { + Transform(ref source[i], ref transform, out destination[i]); + } + } + + /// + /// Adds two vectors. + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator +(Vector4 left, Vector4 right) + { + return new Vector4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); + } + + /// + /// Assert a vector (return it unchanged). + /// + /// The vector to assert (unchange). + /// The asserted (unchanged) vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator +(Vector4 value) + { + return value; + } + + /// + /// Subtracts two vectors. + /// + /// The first vector to subtract. + /// The second vector to subtract. + /// The difference of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator -(Vector4 left, Vector4 right) + { + return new Vector4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); + } + + /// + /// Reverses the direction of a given vector. + /// + /// The vector to negate. + /// A vector facing in the opposite direction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator -(Vector4 value) + { + return new Vector4(-value.X, -value.Y, -value.Z, -value.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator *(float scale, Vector4 value) + { + return new Vector4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator *(Vector4 value, float scale) + { + return new Vector4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); + } + + /// + /// Modulates a vector with another by performing component-wise multiplication. + /// + /// The first vector to multiply. + /// The second vector to multiply. + /// The multiplication of the two vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator *(Vector4 left, Vector4 right) + { + return new Vector4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); + } + + /// + /// Scales a vector by the given value. + /// + /// The vector to scale. + /// The amount by which to scale the vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator /(Vector4 value, float scale) + { + return new Vector4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); + } + + /// + /// Divides a numerator by a vector. + /// + /// The numerator. + /// The value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator /(float numerator, Vector4 value) + { + return new Vector4(numerator / value.X, numerator / value.Y, numerator / value.Z, numerator / value.W); + } + + /// + /// Divides a vector by the given vector, component-wise. + /// + /// The vector to scale. + /// The by. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 operator /(Vector4 value, Vector4 by) + { + return new Vector4(value.X / by.X, value.Y / by.Y, value.Z / by.Z, value.W / by.W); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(Vector4 left, Vector4 right) + { + return left.Equals(right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(Vector4 left, Vector4 right) + { + return !left.Equals(right); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vec2(Vector4 value) + { + return new Vec2(value.X, value.Y); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector3(Vector4 value) + { + return new Vector3(value.X, value.Y, value.Z); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture), W.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + ToString(formatProvider); + + return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), Z.ToString(format, formatProvider), W.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(Vector4 other) + { + return ((float)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.Z - Z) < MathUtil.ZeroTolerance && + (float)Math.Abs(other.W - W) < MathUtil.ZeroTolerance); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (value == null) + return false; + + if (value.GetType() != GetType()) + return false; + + return Equals((Vector4)value); + } + +#if WPFInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator System.Windows.Media.Media3D.Point4D(Vector4 value) + { + return new System.Windows.Media.Media3D.Point4D(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Vector4(System.Windows.Media.Media3D.Point4D value) + { + return new Vector4((float)value.X, (float)value.Y, (float)value.Z, (float)value.W); + } +#endif + +#if XnaInterop + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Microsoft.Xna.Framework.Vector4(Vector4 value) + { + return new Microsoft.Xna.Framework.Vector4(value.X, value.Y, value.Z, value.W); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vector4(Microsoft.Xna.Framework.Vector4 value) + { + return new Vector4(value.X, value.Y, value.Z, value.W); + } +#endif + } +} diff --git a/math/VectorExtensions.cs b/math/VectorExtensions.cs new file mode 100644 index 0000000..9a269ab --- /dev/null +++ b/math/VectorExtensions.cs @@ -0,0 +1,64 @@ +// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +namespace math +{ + /// + /// Extensions methods of the vector classes. + /// + public static class VectorExtensions + { + /// + /// Return the Y/X components of the vector in the inverse order. + /// + /// the input vector + public static Vec2 YX(this Vec2 vector) + { + return new Vec2(vector.Y, vector.X); + } + + /// + /// Return the X/Y components of the vector. + /// + /// the input vector + public static Vec2 XY(this Vector3 vector) + { + return new Vec2(vector.X, vector.Y); + } + + /// + /// Return the X/Z components of the vector. + /// + /// the input vector + public static Vec2 XZ(this Vector3 vector) + { + return new Vec2(vector.X, vector.Z); + } + + /// + /// Return the Y/Z components of the vector. + /// + /// the input vector + public static Vec2 YZ(this Vector3 vector) + { + return new Vec2(vector.Y, vector.Z); + } + + /// + /// Return the X/Y components of the vector. + /// + /// the input vector + public static Vec2 XY(this Vector4 vector) + { + return new Vec2(vector.X, vector.Y); + } + + /// + /// Return the X/Y/Z components of the vector. + /// + /// the input vector + public static Vector3 XYZ(this Vector4 vector) + { + return new Vector3(vector.X, vector.Y, vector.Z); + } + } +}