< Summary

Information
Class: Gravitas.Colliders.LSAABBoxCollider2D
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/2D/LSAABBoxCollider2D.cs
Line coverage
100%
Covered lines: 130
Uncovered lines: 0
Coverable lines: 130
Total lines: 237
Line coverage: 100%
Branch coverage
100%
Covered branches: 28
Total branches: 28
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/2D/LSAABBoxCollider2D.cs

#LineLine coverage
 1//=======================================================================
 2// LSAABBoxCollider2D.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 Chronicler;
 9using FixedMathSharp;
 10using FixedMathSharp.Geometry;
 11using System;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas.Colliders;
 15
 16/// <summary>
 17/// Pure 2D axis-aligned box collider.
 18/// </summary>
 19public sealed class LSAABBoxCollider2D : LSCollider2D, IConvexVertexSource2D
 20{
 21    private Vector2d _size;
 22    private Vector2d _halfExtents;
 23    private Vector2d _scaledHalfExtents;
 24    private Vector2d _preparedHalfExtents;
 25
 26    /// <summary>Creates a pure 2D axis-aligned box with an authored size.</summary>
 79427    public LSAABBoxCollider2D(Vector2d size)
 28    {
 79429        Size = size;
 79230    }
 31
 32    /// <summary>Creates a runtime box from an authored shape definition.</summary>
 4133    public LSAABBoxCollider2D(ColliderShapeDefinition2D definition)
 34    {
 4135        definition.EnsureKind(ColliderShapeDefinition2DKind.AABBox);
 4136        Material = definition.Material;
 4137        Size = definition.Size;
 4138    }
 39
 40    /// <inheritdoc/>
 327541    public override ColliderType2D Shape => ColliderType2D.AABox;
 42
 43    /// <summary>Gets or sets the unscaled full box size.</summary>
 44    public Vector2d Size
 45    {
 46        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 18647        get => _size;
 48        set
 49        {
 83850            SwiftThrowHelper.ThrowIfArgument(
 83851                value.X <= Fixed64.Zero || value.Y <= Fixed64.Zero,
 83852                nameof(value),
 83853                "2D AABB size components must be greater than zero.");
 83654            if (_size == value)
 255                return;
 56
 83457            _size = value;
 83458            _halfExtents = value * Fixed64.Half;
 83459            MarkShapeDirty();
 83460        }
 61    }
 62
 63    /// <summary>Gets half of the authored unscaled size.</summary>
 464    public Vector2d HalfExtents => _halfExtents;
 65
 66    /// <summary>Gets the half extents after owner and compound-part scaling.</summary>
 67    public Vector2d ScaledHalfExtents
 68    {
 69        get
 70        {
 2394871            if (HasCommittedShape)
 1663772                return _scaledHalfExtents;
 73
 731174            GetCurrentScaleFactors(
 731175                out Vector2d ownerScale,
 731176                out Vector2d partScale);
 731177            return ColliderScalePolicy.Scale(
 731178                _size,
 731179                ownerScale,
 731180                partScale,
 731181                Fixed64.Two);
 82        }
 83    }
 84
 1297685    int IConvexVertexSource2D.VertexCount => 4;
 86
 595087    Fixed64 IConvexVertexSource2D.Rotation => Fixed64.Zero;
 88
 89    /// <inheritdoc/>
 90    public override bool ContainsPoint(Vector2d point)
 91    {
 102792        Span<Vector2d> offsets = stackalloc Vector2d[4];
 102793        GetCenterRelativeVertices(offsets);
 102794        return FixedConvex2dRelations.ContainsPoint(point, Center, offsets);
 95    }
 96
 97    /// <inheritdoc/>
 98    public override Vector2d GetClosestPoint(Vector2d point)
 99    {
 2100        Span<Vector2d> offsets = stackalloc Vector2d[4];
 2101        GetCenterRelativeVertices(offsets);
 2102        if (FixedConvex2dRelations.ContainsPoint(point, Center, offsets))
 1103            return point;
 104
 1105        Vector2d offset =
 1106            FixedConvex2dRelations.GetClosestPointOffset(
 1107                point,
 1108                Center,
 1109                offsets);
 110        // An exterior closest point lies on the segment from the representable
 111        // query to the representable box center, so its final sum is in range.
 1112        _ = Vector2d.TryAdd(Center, offset, out Vector2d closest);
 1113        return closest;
 114    }
 115
 116    /// <inheritdoc/>
 117    public override Vector2d GetSupportPoint(Vector2d direction)
 118    {
 8119        Vector2d halfExtents = ScaledHalfExtents;
 8120        Vector2d offset = new(
 8121            direction.X >= Fixed64.Zero
 8122                ? halfExtents.X
 8123                : -halfExtents.X,
 8124            direction.Y >= Fixed64.Zero
 8125                ? halfExtents.Y
 8126                : -halfExtents.Y);
 8127        if (Vector2d.TryAdd(Center, offset, out Vector2d support))
 6128            return support;
 129
 2130        throw new System.InvalidOperationException(
 2131            "The box support point is outside the Fixed64 coordinate domain.");
 132    }
 133
 134    Vector2d IConvexVertexSource2D.GetScaledLocalVertexUnchecked(int index)
 135    {
 20342136        Vector2d halfExtents = ScaledHalfExtents;
 20342137        return index switch
 20342138        {
 5087139            0 => new Vector2d(-halfExtents.X, -halfExtents.Y),
 5085140            1 => new Vector2d(halfExtents.X, -halfExtents.Y),
 5085141            2 => new Vector2d(halfExtents.X, halfExtents.Y),
 5085142            _ => new Vector2d(-halfExtents.X, halfExtents.Y)
 20342143        };
 144    }
 145
 146    FixedPointAnchor2d IConvexVertexSource2D.GetSupportAnchor(Vector2d direction)
 147    {
 54148        Vector2d halfExtents = ScaledHalfExtents;
 54149        return new FixedPointAnchor2d(
 54150            Center,
 54151            Fixed64.Zero,
 54152            new Vector2d(
 54153                direction.X >= Fixed64.Zero
 54154                    ? halfExtents.X
 54155                    : -halfExtents.X,
 54156                direction.Y >= Fixed64.Zero
 54157                    ? halfExtents.Y
 54158                    : -halfExtents.Y));
 159    }
 160
 161    private void GetCenterRelativeVertices(Span<Vector2d> offsets)
 162    {
 1029163        Vector2d halfExtents = ScaledHalfExtents;
 1029164        offsets[0] = new Vector2d(-halfExtents.X, -halfExtents.Y);
 1029165        offsets[1] = new Vector2d(halfExtents.X, -halfExtents.Y);
 1029166        offsets[2] = new Vector2d(halfExtents.X, halfExtents.Y);
 1029167        offsets[3] = new Vector2d(-halfExtents.X, halfExtents.Y);
 1029168    }
 169
 170    internal override ExactMassWeight CalculateAreaForMassProperties()
 171    {
 160172        Vector2d halfExtents = ScaledHalfExtents;
 160173        return ExactMassWeight.FromProduct(
 160174            (Fixed64)4,
 160175            halfExtents.X,
 160176            halfExtents.Y);
 177    }
 178
 179    internal override ExactMassWeight CalculatePreparedAreaForMassProperties() =>
 82180        ExactMassWeight.FromProduct(
 82181            (Fixed64)4,
 82182            _preparedHalfExtents.X,
 82183            _preparedHalfExtents.Y);
 184
 185    internal override Fixed64 CalculateCenterOfMassMoment(Fixed64 mass)
 186    {
 1460187        Vector2d halfExtents = ScaledHalfExtents;
 1460188        bool representable = Fixed64.TryMultiplyDivide(
 1460189                mass,
 1460190                halfExtents.X,
 1460191                halfExtents.X,
 1460192                (Fixed64)3,
 1460193                out Fixed64 xMoment)
 1460194            & Fixed64.TryMultiplyDivide(
 1460195                mass,
 1460196                halfExtents.Y,
 1460197                halfExtents.Y,
 1460198                (Fixed64)3,
 1460199                out Fixed64 yMoment)
 1460200            & Fixed64.TryAdd(xMoment, yMoment, out Fixed64 momentAboutCenterOfMass);
 1460201        if (!representable)
 1202            momentAboutCenterOfMass = Fixed64.MaxValue;
 203
 1460204        return momentAboutCenterOfMass;
 205    }
 206
 207    private protected override void PrepareShape(in ColliderShapeSnapshot2D snapshot)
 208    {
 882209        _preparedHalfExtents = ColliderScalePolicy.ScalePositive(
 882210            _size,
 882211            snapshot.OwnerScale,
 882212            snapshot.PartScale,
 882213            Fixed64.Two);
 882214        SetPreparedBounds(FixedBoundArea.FromCenterAndScopeClippedToDomain(
 882215            snapshot.Center,
 882216            _preparedHalfExtents));
 882217    }
 218
 219    private protected override void PublishShape() =>
 877220        _scaledHalfExtents = _preparedHalfExtents;
 221
 222    /// <inheritdoc/>
 223    protected override void RecordShapeData(IChronicler chronicler)
 224    {
 7225        Vector2d size = _size;
 7226        RecordValues.Look(chronicler, ref size, "Size", Vector2d.One);
 7227        if (chronicler.Mode == SerializationMode.Loading)
 228        {
 5229            SwiftThrowHelper.ThrowIfArgument(
 5230                size.X <= Fixed64.Zero || size.Y <= Fixed64.Zero,
 5231                nameof(size),
 5232                "2D AABB size components must be greater than zero.");
 3233            _size = size;
 3234            _halfExtents = size * Fixed64.Half;
 235        }
 5236    }
 237}