< Summary

Information
Class: Gravitas.Colliders.ColliderCanonicalBounds2D
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/ColliderCanonicalBounds2D.cs
Line coverage
100%
Covered lines: 176
Uncovered lines: 0
Coverable lines: 176
Total lines: 310
Line coverage: 100%
Branch coverage
100%
Covered branches: 56
Total branches: 56
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/ColliderCanonicalBounds2D.cs

#LineLine coverage
 1//=======================================================================
 2// ColliderCanonicalBounds2D.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2026-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using FixedMathSharp;
 9using FixedMathSharp.Geometry;
 10
 11namespace Gravitas.Colliders;
 12
 13/// <summary>
 14/// Contains methods for computing canonical bounds of 2D colliders,
 15/// including proxy radii and relative bounds, based on collider type and properties.
 16/// </summary>
 17internal static class ColliderCanonicalBounds2D
 18{
 19    internal static Fixed64 GetCurrentCenteredProxyRadius(
 20        LSCollider2D collider)
 21    {
 522        if (collider is LSCompoundCollider2D compound)
 223            return GetCurrentCompoundCenteredProxyRadius(compound);
 24
 325        return GetShapeCenteredProxyRadius(collider);
 26    }
 27
 28    internal static Fixed64 GetCenteredProxyRadius(
 29        LSCollider2D collider)
 30    {
 1336731        if (collider is LSCompoundCollider2D compound)
 11232            return GetCompoundCenteredProxyRadius(compound);
 33
 1325534        return GetShapeCenteredProxyRadius(collider);
 35    }
 36
 37    private static Fixed64 GetShapeCenteredProxyRadius(
 38        LSCollider2D collider)
 39    {
 1325840        if (collider is LSCircleCollider2D circle)
 617741            return circle.ScaledRadius;
 708142        if (collider is LSCapsuleCollider2D capsule)
 43        {
 74944            FixedBoundArea localBounds =
 74945                FixedBoundArea.FromCenteredCapsuleClippedToDomain(
 74946                    Vector2d.Zero,
 74947                    Vector2d.Forward,
 74948                    capsule.AxisLength,
 74949                    capsule.ScaledRadius);
 74950            return localBounds.Max.Y;
 51        }
 633252        if (collider.VertexCount == 0)
 1253            return Fixed64.MaxValue;
 54
 632055        Fixed64 radius = Fixed64.Zero;
 6314456        for (int index = 0; index < collider.VertexCount; index++)
 57        {
 2525458            if (!collider.GetScaledLocalVertexUnchecked(index)
 2525459                    .TryGetMagnitudeCeiling(out Fixed64 vertexRadius))
 60            {
 261                return Fixed64.MaxValue;
 62            }
 63
 2525264            radius = FixedMath.Max(radius, vertexRadius);
 65        }
 66
 631867        return radius;
 68    }
 69
 70    private static Fixed64 GetCurrentCompoundCenteredProxyRadius(
 71        LSCompoundCollider2D compound)
 72    {
 273        Fixed64 radius = Fixed64.Zero;
 674        for (int index = 0; index < compound.PartCount; index++)
 75        {
 276            LSCollider2D part = compound.GetPartCollider(index);
 277            bool distanceResolved = part.TryGetCurrentScaledOffset(
 278                    out Vector2d centerOffset)
 279                & centerOffset.TryGetMagnitudeCeiling(
 280                    out Fixed64 centerDistance);
 281            bool radiusResolved = Fixed64.TryAdd(
 282                centerDistance,
 283                GetCurrentCenteredProxyRadius(part),
 284                out Fixed64 partRadius);
 285            if (!(distanceResolved & radiusResolved))
 186                return Fixed64.MaxValue;
 87
 188            radius = FixedMath.Max(radius, partRadius);
 89        }
 90
 191        return radius;
 92    }
 93
 94    internal static Fixed64 GetGroundProbeRadius(
 95        LSCollider2D collider)
 96    {
 1336897        if (collider is LSCircleCollider2D circle)
 617598            return circle.ScaledRadius;
 719399        if (collider is LSCapsuleCollider2D capsule)
 749100            return capsule.ScaledRadius;
 6444101        if (collider is LSCompoundCollider2D compound)
 102        {
 113103            return TryGetCompoundRelativeBounds(
 113104                compound,
 113105                compound.CanonicalCenter,
 113106                out Vector2d minimum,
 113107                out Vector2d maximum)
 113108                ? GetMinimumAbsoluteExtent(minimum, maximum)
 113109                : Fixed64.MaxValue;
 110        }
 6331111        if (collider.VertexCount == 0)
 11112            return Fixed64.Zero;
 113
 6320114        return TryGetConvexBoundsFromRelativeCenter(
 6320115            collider,
 6320116            Vector2d.Zero,
 6320117            out Vector2d convexMinimum,
 6320118            out Vector2d convexMaximum)
 6320119            ? GetMinimumAbsoluteExtent(convexMinimum, convexMaximum)
 6320120            : Fixed64.MaxValue;
 121    }
 122
 123    private static Fixed64 GetCompoundCenteredProxyRadius(
 124        LSCompoundCollider2D compound)
 125    {
 112126        Fixed64 radius = Fixed64.Zero;
 658127        for (int index = 0; index < compound.PartCount; index++)
 128        {
 219129            LSCollider2D part = compound.GetPartCollider(index);
 219130            bool offsetResolved = Vector2d.TrySubtract(
 219131                part.CanonicalCenter,
 219132                compound.CanonicalCenter,
 219133                out Vector2d centerOffset);
 219134            bool distanceResolved =
 219135                centerOffset.TryGetMagnitudeCeiling(
 219136                    out Fixed64 centerDistance);
 219137            bool radiusResolved = Fixed64.TryAdd(
 219138                centerDistance,
 219139                part.CanonicalCenteredProxyRadius,
 219140                out Fixed64 partRadius);
 219141            if (!(offsetResolved & distanceResolved & radiusResolved))
 2142                return Fixed64.MaxValue;
 143
 217144            radius = FixedMath.Max(radius, partRadius);
 145        }
 146
 110147        return radius;
 148    }
 149
 150    private static bool TryGetCompoundRelativeBounds(
 151        LSCompoundCollider2D compound,
 152        Vector2d referenceCenter,
 153        out Vector2d minimum,
 154        out Vector2d maximum)
 155    {
 113156        minimum = default;
 113157        maximum = default;
 113158        bool hasBounds = false;
 660159        for (int index = 0; index < compound.PartCount; index++)
 160        {
 220161            LSCollider2D part = compound.GetPartCollider(index);
 220162            if (!TryGetRelativeBounds(
 220163                    part,
 220164                    referenceCenter,
 220165                    out Vector2d partMinimum,
 220166                    out Vector2d partMaximum))
 167            {
 3168                minimum = default;
 3169                maximum = default;
 3170                return false;
 171            }
 172
 217173            if (!hasBounds)
 174            {
 110175                minimum = partMinimum;
 110176                maximum = partMaximum;
 110177                hasBounds = true;
 110178                continue;
 179            }
 180
 107181            minimum = new Vector2d(
 107182                FixedMath.Min(minimum.X, partMinimum.X),
 107183                FixedMath.Min(minimum.Y, partMinimum.Y));
 107184            maximum = new Vector2d(
 107185                FixedMath.Max(maximum.X, partMaximum.X),
 107186                FixedMath.Max(maximum.Y, partMaximum.Y));
 187        }
 188
 110189        return hasBounds;
 190    }
 191
 192    private static bool TryGetRelativeBounds(
 193        LSCollider2D collider,
 194        Vector2d referenceCenter,
 195        out Vector2d minimum,
 196        out Vector2d maximum)
 197    {
 220198        if (!Vector2d.TrySubtract(
 220199                collider.CanonicalCenter,
 220200                referenceCenter,
 220201                out Vector2d relativeCenter))
 202        {
 1203            minimum = default;
 1204            maximum = default;
 1205            return false;
 206        }
 207
 219208        if (collider is LSCircleCollider2D circle)
 209        {
 146210            return TryGetRoundBounds(
 146211                relativeCenter,
 146212                circle.ScaledRadius,
 146213                circle.ScaledRadius,
 146214                out minimum,
 146215                out maximum);
 216        }
 217
 73218        if (collider is LSCapsuleCollider2D capsule)
 219        {
 19220            FixedBoundArea centeredBounds =
 19221                FixedBoundArea.FromCenteredRotatedCapsuleClippedToDomain(
 19222                    Vector2d.Zero,
 19223                    capsule.Rotation,
 19224                    capsule.AxisLength,
 19225                    capsule.ScaledRadius);
 19226            return TryGetRoundBounds(
 19227                relativeCenter,
 19228                centeredBounds.Max.X,
 19229                centeredBounds.Max.Y,
 19230                out minimum,
 19231                out maximum);
 232        }
 233
 54234        return TryGetConvexBoundsFromRelativeCenter(
 54235            collider,
 54236            relativeCenter,
 54237            out minimum,
 54238            out maximum);
 239    }
 240
 241    private static bool TryGetRoundBounds(
 242        Vector2d center,
 243        Fixed64 extentX,
 244        Fixed64 extentY,
 245        out Vector2d minimum,
 246        out Vector2d maximum)
 247    {
 165248        bool representable =
 165249            Fixed64.TrySubtract(center.X, extentX, out Fixed64 minimumX)
 165250            & Fixed64.TrySubtract(center.Y, extentY, out Fixed64 minimumY)
 165251            & Fixed64.TryAdd(center.X, extentX, out Fixed64 maximumX)
 165252            & Fixed64.TryAdd(center.Y, extentY, out Fixed64 maximumY);
 165253        minimum = representable
 165254            ? new Vector2d(minimumX, minimumY)
 165255            : default;
 165256        maximum = representable
 165257            ? new Vector2d(maximumX, maximumY)
 165258            : default;
 165259        return representable;
 260    }
 261
 262    private static bool TryGetConvexBoundsFromRelativeCenter(
 263        LSCollider2D collider,
 264        Vector2d relativeCenter,
 265        out Vector2d minimum,
 266        out Vector2d maximum)
 267    {
 6374268        if (!Vector2d.TryTransformPoint(
 6374269                relativeCenter,
 6374270                collider.GetScaledLocalVertexUnchecked(0),
 6374271                collider.ConvexRotation,
 6374272                out minimum))
 273        {
 1274            minimum = default;
 1275            maximum = default;
 1276            return false;
 277        }
 278
 6373279        maximum = minimum;
 50910280        for (int index = 1; index < collider.VertexCount; index++)
 281        {
 19083282            if (!Vector2d.TryTransformPoint(
 19083283                    relativeCenter,
 19083284                    collider.GetScaledLocalVertexUnchecked(index),
 19083285                    collider.ConvexRotation,
 19083286                    out Vector2d point))
 287            {
 1288                minimum = default;
 1289                maximum = default;
 1290                return false;
 291            }
 292
 19082293            minimum = new Vector2d(
 19082294                FixedMath.Min(minimum.X, point.X),
 19082295                FixedMath.Min(minimum.Y, point.Y));
 19082296            maximum = new Vector2d(
 19082297                FixedMath.Max(maximum.X, point.X),
 19082298                FixedMath.Max(maximum.Y, point.Y));
 299        }
 300
 6372301        return true;
 302    }
 303
 304    private static Fixed64 GetMinimumAbsoluteExtent(
 305        Vector2d minimum,
 306        Vector2d maximum) =>
 6428307        FixedMath.Min(
 6428308            FixedMath.Max(minimum.X.Abs(), maximum.X.Abs()),
 6428309            FixedMath.Max(minimum.Y.Abs(), maximum.Y.Abs()));
 310}