| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- // Copyright (c) Craftwork Games. All rights reserved.
- // Licensed under the MIT license.
- // See LICENSE file in the project root for full license information.
- using System;
- using System.Diagnostics;
- using System.Runtime.Serialization;
- using Microsoft.Xna.Framework;
- namespace MonoGame.Extended
- {
- /// <summary>
- /// Represents a semi-infinite ray in 2D space starting at an origin point and extending in a direction.
- /// </summary>
- [DataContract]
- [DebuggerDisplay("{DebugDisplayString,nq}")]
- public struct Ray2D : IEquatable<Ray2D>
- {
- #region Public Fields
- /// <summary>
- /// The direction vector defining which way this ray extends from its origin.
- /// Should be normalized for accurate distance calculations.
- /// </summary>
- [DataMember]
- public Vector2 Direction;
- /// <summary>
- /// The starting point of this ray in 2D space.
- /// </summary>
- [DataMember]
- public Vector2 Origin;
- #endregion
- #region Internal Properties
- internal readonly string DebugDisplayString
- {
- get
- {
- return string.Concat(
- "Origin( ", Origin.ToString(), " ) \r\n",
- "Direction( ", Direction.ToString(), " )"
- );
- }
- }
- #endregion
- #region Public Constructors
- /// <summary>
- /// Creates a new <see cref="Ray2D"/> with the specified origin and direction.
- /// </summary>
- /// <param name="origin">The starting point of the ray in 2D space.</param>
- /// <param name="direction">
- /// The direction vector defining which way the ray extends. Should be normalized for accurate distance calculations.
- /// </param>
- public Ray2D(Vector2 origin, Vector2 direction)
- {
- Origin = origin;
- Direction = direction;
- }
- #endregion
- #region Public Methods
- #endregion
- /// <summary>
- /// Creates a <see cref="Ray2D"/> from a starting point toward a target point.
- /// </summary>
- /// <param name="start">The starting point of the ray in 2D space.</param>
- /// <param name="through">A target point the ray should pass through.</param>
- /// <returns>
- /// A new <see cref="Ray2D"/> starting at the specified point with a unit direction vector
- /// pointing toward the target point.
- /// </returns>
- /// <exception cref="ArgumentException">
- /// Thrown when the points are too close to define a valid direction.
- /// </exception>
- public static Ray2D CreateFromPoints(Vector2 start, Vector2 through)
- {
- Vector2 direction = through - start;
- float lengthSq = direction.LengthSquared();
- if (lengthSq < Collision2D.Epsilon * Collision2D.Epsilon)
- {
- throw new ArgumentException("Points must be distinct to define a ray direction");
- }
- return new Ray2D(start, Vector2.Normalize(direction));
- }
- /// <summary>
- /// Computes a point along this ray at the specified parametric distance.
- /// </summary>
- /// <param name="distanceAlongRay">The parametric distance along the ray from the origin.</param>
- /// <returns>
- /// The point at position <c>Origin + distanceAlongRay * Direction</c>.
- /// </returns>
- /// <remarks>
- /// Negative values of <paramref name="distanceAlongRay"/> return points behind the ray's origin.
- /// </remarks>
- public readonly Vector2 GetPoint(float distanceAlongRay)
- {
- return Origin + distanceAlongRay * Direction;
- }
- /// <summary>
- /// Computes the closest point on this ray to a specified point.
- /// </summary>
- /// <param name="point">The point in 2D space to find the closest point to.</param>
- /// <param name="distanceAlongRay">
- /// When this method returns, contains the parametric distance along the ray to the closest point.
- /// Will be clamped to zero or greater, as rays only extend forward from the origin.
- /// </param>
- /// <returns>
- /// The closest point on the ray. If the point projects behind the ray's origin, returns the origin itself.
- /// </returns>
- public readonly Vector2 ClosestPoint(Vector2 point, out float distanceAlongRay)
- {
- Vector2 ab = Direction;
- float denom = Vector2.Dot(ab, ab);
- if (denom <= Collision2D.Epsilon)
- {
- // Direction of ray is effectively zero, so it's just a point.
- distanceAlongRay = 0.0f;
- return Origin;
- }
- // Project point ab, but deferring divide by Dot(ab, ab)
- distanceAlongRay = Vector2.Dot(point - Origin, ab);
- if (distanceAlongRay <= 0.0f)
- {
- // point projects before the ray origin, clamp to origin
- distanceAlongRay = 0.0f;
- return Origin;
- }
- // Point projects after the ray origin, must do deferred divide
- distanceAlongRay /= denom;
- return Origin + distanceAlongRay * ab;
- }
- /// <summary>
- /// Computes the squared distance from a point to the closest point on this ray.
- /// </summary>
- /// <param name="point">The point in 2D space to measure from.</param>
- /// <returns>
- /// The squared distance. Returns the distance to the origin if the point
- /// projects behind the ray's starting point, or the perpendicular distance if the point
- /// projects onto the ray.
- /// </returns>
- /// <remarks>
- /// This method returns squared distance to avoid the expensive square root operation,
- /// making it efficient for distance comparisons.
- /// </remarks>
- public readonly float DistanceSquaredToPoint(Vector2 point)
- {
- Vector2 closestPoint = ClosestPoint(point, out _);
- return Vector2.DistanceSquared(point, closestPoint);
- }
- /// <summary>
- /// Computes the distance from a point to the closest point on this ray.
- /// </summary>
- /// <param name="point">The point in 2D space to measure from.</param>
- /// <returns>
- /// The distance. Returns the distance to the origin if the point
- /// projects behind the ray's starting point, or the perpendicular distance if the point
- /// projects onto the ray.
- /// </returns>
- /// <remarks>
- /// This method performs a square root operation. For distance comparisons,
- /// use <see cref="DistanceSquaredToPoint"/> instead to avoid the computational cost.
- /// </remarks>
- public readonly float DistanceToPoint(Vector2 point)
- {
- float distSq = DistanceSquaredToPoint(point);
- return MathF.Sqrt(distSq);
- }
- /// <summary>
- /// Returns a normalized representation of the specified ray with a unit direction vector.
- /// </summary>
- /// <param name="value">The ray to normalize.</param>
- /// <param name="result">
- /// When this method returns, contains a <see cref="Ray2D"/> with the same origin but a unit direction vector.
- /// </param>
- public static void Normalize(ref Ray2D value, out Ray2D result)
- {
- result = new Ray2D(value.Origin, Vector2.Normalize(value.Direction));
- }
- /// <summary>
- /// Returns a normalized representation of the specified ray with a unit direction vector.
- /// </summary>
- /// <param name="value">The ray to normalize.</param>
- /// <returns>
- /// A new <see cref="Ray2D"/> with the same origin but a unit direction vector.
- /// </returns>
- public static Ray2D Normalize(Ray2D value)
- {
- Ray2D result;
- Normalize(ref value, out result);
- return result;
- }
- /// <summary>
- /// Normalizes this ray's direction vector to unit length.
- /// </summary>
- /// <remarks>
- /// After normalization, the <see cref="Direction"/> will be a unit vector while
- /// <see cref="Origin"/> remains unchanged.
- /// </remarks>
- public void Normalize()
- {
- Direction.Normalize();
- }
- /// <summary>
- /// Tests if this ray intersects with a line.
- /// </summary>
- /// <param name="line">The line to test against.</param>
- /// <param name="distanceAlongRay">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the intersection point.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="point">
- /// When this method returns <see langword="true"/>, contains the point where the ray and line intersect.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if the ray and line intersect in the ray's forward direction;
- /// otherwise, <see langword="false"/> if they are parallel or the intersection point is behind the ray's origin.
- /// </returns>
- public readonly bool Intersects(Line2D line, out float? distanceAlongRay, out Vector2? point)
- {
- return line.Intersects(this, out distanceAlongRay, out point);
- }
- /// <summary>
- /// Tests if this ray intersects with a line.
- /// </summary>
- /// <param name="line">The line to test against.</param>
- /// <returns>
- /// <see langword="true"/> if the ray and line intersect in the ray's forward direction;
- /// otherwise, <see langword="false"/> if they are parallel or the intersection point is behind the ray's origin.
- /// </returns>
- public readonly bool Intersects(Line2D line)
- {
- return line.Intersects(this);
- }
- /// <summary>
- /// Tests if this ray intersects with another ray.
- /// </summary>
- /// <param name="other">The other ray to test against.</param>
- /// <param name="distanceAlongRay1">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the intersection point.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="distanceAlongRay2">
- /// When this method returns <see langword="true"/>, contains the parametric distance along the other ray
- /// to the intersection point.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="point">
- /// When this method returns <see langword="true"/>, contains the point where the rays intersect.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if both rays intersect in their forward directions;
- /// otherwise, <see langword="false"/> if they are parallel or if the intersection point is behind either ray's origin.
- /// </returns>
- public readonly bool Intersects(Ray2D other, out float? distanceAlongRay1, out float? distanceAlongRay2, out Vector2? point)
- {
- // C. Ericson, Real-Time Collision Detection, Morgan Kaufmann, 2005
- // Parametric intersection of two rays using 2D cross products
- // Derived from Section 5.1.9.1 "2D Segment Intersection" (line–line formulation)
- if (!Collision2D.SolveParametricIntersection2D(Origin, Direction, other.Origin, other.Direction, out float t1, out float t2))
- {
- distanceAlongRay1 = distanceAlongRay2 = null;
- point = null;
- return false;
- }
- // Only intersections in forward direction. Negative direction indicates
- // intersection would have happened behind origin.
- if (t1 < 0.0f || t2 < 0.0f)
- {
- distanceAlongRay1 = distanceAlongRay2 = null;
- point = null;
- return false;
- }
- distanceAlongRay1 = t1;
- distanceAlongRay2 = t2;
- point = Origin + t1 * Direction;
- return true;
- }
- /// <summary>
- /// Tests if this ray intersects with another ray.
- /// </summary>
- /// <param name="other">The other ray to test against.</param>
- /// <returns>
- /// <see langword="true"/> if both rays intersect in their forward directions;
- /// otherwise, <see langword="false"/> if they are parallel or if the intersection point is behind either ray's origin.
- /// </returns>
- public readonly bool Intersects(Ray2D other)
- {
- return Intersects(other, out _, out _, out _);
- }
- /// <summary>
- /// Tests if this ray intersects with a line segment.
- /// </summary>
- /// <param name="segment">The line segment to test against.</param>
- /// <param name="distanceAlongRay">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the intersection point.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="distanceAlongSegment">
- /// When this method returns <see langword="true"/>, contains the parametric distance along the segment
- /// to the intersection point, in the range [0, 1] where 0 represents the start and 1 represents the end.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="point">
- /// When this method returns <see langword="true"/>, contains the point where the ray and segment intersect.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the segment in its forward direction and within the segment's bounds;
- /// otherwise, <see langword="false"/> if they are parallel, the intersection is outside the segment, or behind the ray's origin.
- /// </returns>
- public readonly bool Intersects(LineSegment2D segment, out float? distanceAlongRay, out float? distanceAlongSegment, out Vector2? point)
- {
- // C. Ericson, Real-Time Collision Detection, Morgan Kaufmann, 2005
- // Parametric intersection of a ray with a line segment using 2D cross products
- // Derived from Section 5.1.9.1 "2D Segment Intersection" (line–line formulation)
- Vector2 segmentDir = segment.End - segment.Start;
- if (!Collision2D.SolveParametricIntersection2D(Origin, Direction, segment.Start, segmentDir, out float tRay, out float tSeg))
- {
- distanceAlongRay = distanceAlongSegment = null;
- point = null;
- return false;
- }
- // Intersection only if within segments bounds [0,1] and
- // only in forward direction of ray. Negative direction of ray indicates
- // intersection would have happened behind ray origin
- if (tSeg < 0.0f || tSeg > 1.0f || tRay < 0.0f)
- {
- distanceAlongRay = distanceAlongSegment = null;
- point = null;
- return false;
- }
- distanceAlongRay = tRay;
- distanceAlongSegment = tSeg;
- point = Origin + tRay * Direction;
- return true;
- }
- /// <summary>Tests if this <see cref="Ray2D"/> intersects a <see cref="LineSegment2D"/>.</summary>
- /// <param name="segment">The <see cref="LineSegment2D"/> to test against.</param>
- /// <returns>
- /// <see langword="true"/> if this ray and <paramref name="segment"/> intersect; otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(LineSegment2D segment)
- {
- return Intersects(segment, out _, out _, out _);
- }
- /// <summary>
- /// Tests if this ray intersects with an axis-aligned bounding box and computes the parametric distances to the intersection points.
- /// </summary>
- /// <param name="box">The bounding box to test against.</param>
- /// <param name="tRayMin">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the entry intersection point, where the intersection point equals <c>Origin + tRayMin * Direction</c>.
- /// If the ray origin is inside the bounding box, this will be <c>0</c>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="tRayMax">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the exit intersection point, where the intersection point equals <c>Origin + tRayMax * Direction</c>.
- /// This is always greater than or equal to <paramref name="tRayMin"/>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the bounding box in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingBox2D box, out float? tRayMin, out float? tRayMax)
- {
- if (!Collision2D.ClipLineToAabb(Origin, Direction, box.Min, box.Max, 0.0f, float.MaxValue, out float tEnter, out float tExit))
- {
- tRayMin = tRayMax = null;
- return false;
- }
- tRayMin = tEnter;
- tRayMax = tExit;
- return true;
- }
- /// <summary>
- /// Tests if this ray intersects with an axis-aligned bounding box.
- /// </summary>
- /// <param name="box">The bounding box to test against.</param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the bounding box in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingBox2D box)
- {
- return Intersects(box, out _, out _);
- }
- /// <summary>
- /// Tests if this ray intersects with a circle and computes the parametric distances to the intersection points.
- /// </summary>
- /// <param name="circle">The circle to test against.</param>
- /// <param name="tRayMin">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the first intersection point, where the intersection point equals <c>Origin + tRayMin * Direction</c>.
- /// If the ray origin is inside the circle, this will be <c>0</c>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="tRayMax">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the second intersection point, where the intersection point equals <c>Origin + tRayMax * Direction</c>.
- /// This is always greater than or equal to <paramref name="tRayMin"/>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the circle in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingCircle2D circle, out float? tRayMin, out float? tRayMax)
- {
- if (!Collision2D.RayCircleIntersectionInterval(Origin, Direction, circle.Center, circle.Radius, out float tMin, out float tMax))
- {
- tRayMin = tRayMax = null;
- return false;
- }
- if (!Collision2D.ClipInterval(tMin, tMax, 0.0f, float.MaxValue, out float entry, out float exit))
- {
- tRayMin = tRayMax = null;
- return false;
- }
- tRayMin = entry;
- tRayMax = exit;
- return true;
- }
- /// <summary>
- /// Tests if this ray intersects with a circle.
- /// </summary>
- /// <param name="circle">The circle to test against.</param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the circle in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingCircle2D circle)
- {
- return Intersects(circle, out _, out _);
- }
- /// <summary>
- /// Tests if this ray intersects with a capsule and computes the parametric distances to the intersection points.
- /// </summary>
- /// <param name="capsule">The capsule to test against.</param>
- /// <param name="tRayMin">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the entry intersection point, where the intersection point equals Origin + tRayMin * Direction.
- /// If the ray origin is inside the capsule, this will be 0.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="tRayMax">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the exit intersection point, where the intersection point equals Origin + tRayMax * Direction.
- /// This is always greater than or equal to <paramref name="tRayMin"/>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the capsule in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingCapsule2D capsule, out float? tRayMin, out float? tRayMax)
- {
- if (!Collision2D.RayCapsuleIntersectionInterval(Origin, Direction, capsule.PointA, capsule.PointB, capsule.Radius, out float tMin, out float tMax))
- {
- tRayMin = tRayMax = null;
- return false;
- }
- if (!Collision2D.ClipInterval(tMin, tMax, 0.0f, float.MaxValue, out float entry, out float exit))
- {
- tRayMin = tRayMax = null;
- return false;
- }
- tRayMin = entry;
- tRayMax = exit;
- return true;
- }
- /// <summary>
- /// Tests if this ray intersects with a capsule.
- /// </summary>
- /// <param name="capsule">The capsule to test against.</param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the capsule in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingCapsule2D capsule)
- {
- return Intersects(capsule, out _, out _);
- }
- /// <summary>
- /// Tests if this ray intersects with an oriented bounding box and computes the parametric distances to the intersection points.
- /// </summary>
- /// <param name="obb">The oriented bounding box to test against.</param>
- /// <param name="tRayMin">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the entry intersection point, where the intersection point equals Origin + tRayMin * Direction.
- /// If the ray origin is inside the box, this will be 0.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="tRayMax">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the exit intersection point, where the intersection point equals Origin + tRayMax * Direction.
- /// This is always greater than or equal to <paramref name="tRayMin"/>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the box in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(OrientedBoundingBox2D obb, out float? tRayMin, out float? tRayMax)
- {
- // C. Ericson, Real-Time Collision Detection, Morgan Kaufmann, 2005
- // Parametric intersection of a ray with an oriented bounding rectangle (2D reduction)
- // Derived from Section 5.3.3 "Intersecting Ray or Segment Against Box"
- // Project the ray origin and direction onto the OBBs local axes
- // to transform the ray from world space to OBB local space.
- Vector2 diff = Origin - obb.Center;
- Vector2 localOrigin = new Vector2(
- Vector2.Dot(diff, obb.AxisX),
- Vector2.Dot(diff, obb.AxisY)
- );
- Vector2 localDirection = new Vector2(
- Vector2.Dot(Direction, obb.AxisX),
- Vector2.Dot(Direction, obb.AxisY)
- );
- if (!Collision2D.ClipLineToAabb(localOrigin, localDirection, -obb.HalfExtents, obb.HalfExtents, 0.0f, float.MaxValue, out float tEnter, out float tExit))
- {
- tRayMin = tRayMax = null;
- return false;
- }
- tRayMin = tEnter;
- tRayMax = tExit;
- return true;
- }
- /// <summary>
- /// Tests if this ray intersects with an oriented bounding box.
- /// </summary>
- /// <param name="obb">The oriented bounding box to test against.</param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the box in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(OrientedBoundingBox2D obb)
- {
- return Intersects(obb, out _, out _);
- }
- /// <summary>
- /// Tests if this ray intersects with a polygon and computes the parametric distances to the intersection points.
- /// </summary>
- /// <param name="polygon">The polygon to test against.</param>
- /// <param name="tRayMin">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the entry intersection point, where the intersection point equals Origin + tRayMin * Direction.
- /// If the ray origin is inside the polygon, this will be 0.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="tRayMax">
- /// When this method returns <see langword="true"/>, contains the parametric distance along this ray
- /// to the exit intersection point, where the intersection point equals Origin + tRayMax * Direction.
- /// This is always greater than or equal to <paramref name="tRayMin"/>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <param name="point">
- /// When this method returns <see langword="true"/>, contains the position of the entry intersection point
- /// corresponding to <paramref name="tRayMin"/>.
- /// When this method returns <see langword="false"/>, contains <see langword="null"/>.
- /// </param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the polygon in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingPolygon2D polygon, out float? tRayMin, out float? tRayMax, out Vector2? point)
- {
- // Clip ray's parametric line against the polygon
- if (!Collision2D.ClipLineToConvexPolygon(Origin, Direction, polygon.Vertices, polygon.Normals, 0.0f, float.MaxValue, out float t0, out float t1))
- {
- tRayMin = tRayMax = null;
- point = null;
- return false;
- }
- if (!Collision2D.ClipInterval(t0, t1, 0.0f, float.MaxValue, out float entry, out float exit))
- {
- tRayMin = tRayMax = null;
- point = null;
- return false;
- }
- tRayMin = entry;
- tRayMax = exit;
- point = Origin + Direction * entry;
- return true;
- }
- /// <summary>
- /// Tests if this ray intersects with a polygon.
- /// </summary>
- /// <param name="polygon">The polygon to test against.</param>
- /// <returns>
- /// <see langword="true"/> if the ray intersects the polygon in its forward direction;
- /// otherwise, <see langword="false"/>.
- /// </returns>
- public readonly bool Intersects(BoundingPolygon2D polygon)
- {
- return Intersects(polygon, out _, out _, out _);
- }
- /// <summary>
- /// Deconstructs this ray into its component values.
- /// </summary>
- /// <param name="origin">
- /// When this method returns, contains the starting point of this ray in 2D space.
- /// </param>
- /// <param name="direction">
- /// When this method returns, contains the direction vector defining which way this ray extends.
- /// </param>
- public readonly void Deconstruct(out Vector2 origin, out Vector2 direction)
- {
- origin = Origin;
- direction = Direction;
- }
- /// <inheritdoc/>
- public readonly bool Equals(Ray2D other)
- {
- return Origin.Equals(other.Origin) && Direction.Equals(other.Direction);
- }
- /// <inheritdoc/>
- public override readonly bool Equals(object obj)
- {
- return (obj is Ray2D other) && Equals(other);
- }
- /// <inheritdoc/>
- public override readonly int GetHashCode()
- {
- return Origin.GetHashCode() ^ Direction.GetHashCode();
- }
- /// <inheritdoc/>
- public override readonly string ToString()
- {
- return "{{Origin:" + Origin.ToString() + " Direction:" + Direction.ToString() + "}}";
- }
- /// <summary/>
- public static bool operator ==(Ray2D left, Ray2D right)
- {
- return left.Equals(right);
- }
- /// <summary/>
- public static bool operator !=(Ray2D left, Ray2D right)
- {
- return !left.Equals(right);
- }
- }
- }
|