// 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, $"{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 );
}
}
}