// Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using System; using System.ComponentModel; using System.Numerics; using System.Text.RegularExpressions; namespace Framework.GameMath { /// /// Represents a ray in 3D space. /// /// /// A ray is R(t) = Origin + t * Direction where t>=0. The Direction isnt necessarily of unit length. /// [Serializable] [TypeConverter(typeof(RayConverter))] public struct Ray : ICloneable { #region Private Fields private static Vector3 _inf = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); private Vector3 _origin; private Vector3 _direction; #endregion #region Constructors /// /// Initializes a new instance of the class using given origin and direction vectors. /// /// Ray's origin point. /// Ray's direction vector. public Ray(Vector3 origin, Vector3 direction) { _origin = origin; _direction = direction; } /// /// Initializes a new instance of the class using given ray. /// /// A instance to assign values from. public Ray(Ray ray) { _origin = ray.Origin; _direction = ray.Direction; } #endregion #region Public Properties /// /// Gets or sets the ray's origin. /// public Vector3 Origin { get { return _origin; } set { _origin = value; } } /// /// Gets or sets the ray's direction vector. /// public Vector3 Direction { get { return _direction; } set { _direction = value; } } #endregion #region ICloneable Members /// /// Creates an exact copy of this object. /// /// The object this method creates, cast as an object. object ICloneable.Clone() { return new Ray(this); } /// /// Creates an exact copy of this object. /// /// The object this method creates. public Ray Clone() { return new Ray(this); } #endregion #region Public Static Parse Methods /// /// Converts the specified string to its equivalent. /// /// A string representation of a /// A that represents the vector specified by the parameter. public static Ray Parse(string s) { Regex r = new(@"\((?\([^\)]*\)), (?\([^\)]*\))\)", RegexOptions.None); Match m = r.Match(s); if (m.Success) { return new Ray( m.Result("${origin}").ParseVector3(), m.Result("${direction}").ParseVector3() ); } else { throw new Exception("Unsuccessful Match."); } } #endregion #region Public Methods /// /// Gets a point on the ray. /// /// /// public Vector3 GetPointOnRay(float t) { return (Origin + Direction * t); } #endregion #region Overrides /// /// Get the hashcode for this instance. /// /// Returns the hash code for this vector instance. public override int GetHashCode() { return _origin.GetHashCode() ^ _direction.GetHashCode(); } /// /// Returns a value indicating whether this instance is equal to /// the specified object. /// /// An object to compare to this instance. /// if is a and has the same values as this instance; otherwise, . public override bool Equals(object obj) { if (obj is Ray) { Ray r = (Ray)obj; return ((_origin == r.Origin) && (_direction == r.Direction)); } return false; } /// /// Returns a string representation of this object. /// /// A string representation of this object. public override string ToString() { return $"({_origin}, {_direction})"; } #endregion #region Comparison Operators /// /// Tests whether two specified rays are equal. /// /// The first of two rays to compare. /// The second of two rays to compare. /// if the two rays are equal; otherwise, . public static bool operator ==(Ray a, Ray b) { return ValueType.Equals(a, b); } /// /// Tests whether two specified rays are not equal. /// /// The first of two rays to compare. /// The second of two rays to compare. /// if the two rays are not equal; otherwise, . public static bool operator !=(Ray a, Ray b) { return !ValueType.Equals(a, b); } #endregion public Vector3 intersection(Plane plane) { float rate = Vector3.Dot(Direction, plane.Normal); if (rate >= 0.0f) { return _inf; } else { float t = -(plane.D + Vector3.Dot(Origin, plane.Normal)) / rate; return Origin + Direction * t; } } public float intersectionTime(AxisAlignedBox box) { Vector3 dummy = Vector3.Zero; bool inside; float time = CollisionDetection.collisionTimeForMovingPointFixedAABox(_origin, _direction, box, ref dummy, out inside); if (float.IsInfinity(time) && inside) return 0.0f; else return time; } public Vector3 invDirection() { return Vector3.Divide(Vector3.One, Direction); } public Ray bumpedRay(float distance) { return new Ray(Origin + Direction * distance, Direction); } } #region RayConverter class /// /// Converts a to and from string representation. /// public class RayConverter : ExpandableObjectConverter { /// /// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context. /// /// An that provides a format context. /// A that represents the type you want to convert from. /// true if this converter can perform the conversion; otherwise, false. public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) return true; return base.CanConvertFrom(context, sourceType); } /// /// Returns whether this converter can convert the object to the specified type, using the specified context. /// /// An that provides a format context. /// A that represents the type you want to convert to. /// true if this converter can perform the conversion; otherwise, false. public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) return true; return base.CanConvertTo(context, destinationType); } /// /// Converts the given value object to the specified type, using the specified context and culture information. /// /// An that provides a format context. /// A object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed. /// The to convert. /// The Type to convert the parameter to. /// An that represents the converted value. public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { if ((destinationType == typeof(string)) && (value is Ray)) { Ray r = (Ray)value; return r.ToString(); } return base.ConvertTo(context, culture, value, destinationType); } /// /// Converts the given object to the type of this converter, using the specified context and culture information. /// /// An that provides a format context. /// The to use as the current culture. /// The to convert. /// An that represents the converted value. public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { if (value.GetType() == typeof(string)) { return Ray.Parse((string)value); } return base.ConvertFrom(context, culture, value); } } #endregion }