< Summary

Information
Class: Gravitas.Colliders.LSCuboidCollider
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/3D/LSCuboidCollider.cs
Line coverage
100%
Covered lines: 114
Uncovered lines: 0
Coverable lines: 114
Total lines: 226
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
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/3D/LSCuboidCollider.cs

#LineLine coverage
 1//=======================================================================
 2// LSCuboidCollider.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;
 10using Gravitas.Queries;
 11using SwiftCollections;
 12using System;
 13
 14namespace Gravitas.Colliders;
 15
 16/// <summary>Represents a runtime 3D cuboid collider.</summary>
 17public sealed class LSCuboidCollider : LSCollider
 18{
 19    private FixedOrientedBox _orientedBox;
 20    private FixedOrientedBox _preparedOrientedBox;
 21    private Fixed64 _preparedArea;
 22
 23    /// <inheritdoc/>
 9988124    public override ColliderType Shape => Rotation == FixedQuaternion.Identity ? ColliderType.AABox : ColliderType.OBBox
 25
 26    /// <inheritdoc/>
 377927    public override int Priority => ColliderSettings.GetPriority(Shape);
 28
 29    /// <summary>
 30    /// Gets the canonical committed oriented-box geometry.
 31    /// </summary>
 3312332    public FixedOrientedBox OrientedBox => _orientedBox;
 33
 34    /// <inheritdoc/>
 35    public override Fixed64 ScaledRadius =>
 210936        HasCommittedShape
 210937            ? CanonicalCenteredProxyRadius
 210938            : ColliderCanonicalBounds
 210939                .GetCurrentCenteredProxyRadius(this);
 40
 41    internal override ExactMassWeight CalculateMassPropertyWeight()
 42    {
 43        Vector3d halfExtents;
 12544        if (HasCommittedShape)
 45        {
 12246            halfExtents = _orientedBox.HalfExtents;
 47        }
 48        else
 49        {
 350            GetCurrentShapeScales(
 351                out Vector3d ownerScale,
 352                out Vector3d partScale);
 353            halfExtents = ColliderScalePolicy.ScalePositive(
 354                Size,
 355                ownerScale,
 356                partScale,
 357                Fixed64.Two);
 58        }
 12559        return ExactMassWeight.FromProduct(
 12560            halfExtents.X,
 12561            halfExtents.Y,
 12562            halfExtents.Z,
 12563            (Fixed64)8);
 64    }
 65
 66    internal override ExactMassWeight CalculatePreparedMassPropertyWeight()
 67    {
 11868        Vector3d halfExtents = _preparedOrientedBox.HalfExtents;
 11869        return ExactMassWeight.FromProduct(
 11870            halfExtents.X,
 11871            halfExtents.Y,
 11872            halfExtents.Z,
 11873            (Fixed64)8);
 74    }
 75
 76    /// <summary>Creates a cuboid collider with default dimensions.</summary>
 190277    public LSCuboidCollider() { }
 78
 79    /// <summary>Creates a runtime cuboid from an authored shape definition.</summary>
 80    public LSCuboidCollider(ColliderShapeDefinition definition)
 3681        : this()
 82    {
 3683        definition.EnsureKind(ColliderShapeDefinitionKind.Cuboid);
 3684        Material = definition.Material;
 3685        Size = definition.Size;
 3686    }
 87
 88    private protected override void PrepareShape(in ColliderShapeSnapshot snapshot)
 89    {
 792690        Vector3d halfExtents = ColliderScalePolicy.ScalePositive(
 792691            snapshot.Size,
 792692            snapshot.OwnerScale,
 792693            snapshot.PartScale,
 792694            Fixed64.Two);
 792695        _preparedOrientedBox = new FixedOrientedBox(
 792696            snapshot.Center,
 792697            snapshot.Rotation,
 792698            halfExtents);
 792699        _preparedArea = GetSurfaceArea(halfExtents);
 7926100        SetPreparedBounds(_preparedOrientedBox.GetBoundsClippedToDomain());
 7926101    }
 102
 103    private protected override void PublishShape()
 104    {
 7919105        _orientedBox = _preparedOrientedBox;
 7919106        Area = _preparedArea;
 7919107    }
 108
 109    internal override Fixed3x3 CalculateCenterOfMassInertiaTensor(Fixed64 mass)
 110    {
 1430111        Vector3d halfExtents = _orientedBox.HalfExtents;
 1430112        Fixed64 xContribution = GetInertiaContribution(mass, halfExtents.X);
 1430113        Fixed64 yContribution = GetInertiaContribution(mass, halfExtents.Y);
 1430114        Fixed64 zContribution = GetInertiaContribution(mass, halfExtents.Z);
 1430115        Fixed64 xx = yContribution + zContribution;
 1430116        Fixed64 yy = xContribution + zContribution;
 1430117        Fixed64 zz = xContribution + yContribution;
 118
 1430119        Fixed3x3 tensor = new(
 1430120            xx, Fixed64.Zero, Fixed64.Zero,
 1430121            Fixed64.Zero, yy, Fixed64.Zero,
 1430122            Fixed64.Zero, Fixed64.Zero, zz
 1430123        );
 1430124        return tensor;
 125    }
 126
 127    /// <inheritdoc/>
 128    public override Fixed64 GetFrontalArea(Vector3d direction)
 129    {
 900130        if (direction.MagnitudeSquared <= Fixed64.Epsilon)
 1131            return Area;
 132
 899133        direction = direction.Normalized;
 899134        _orientedBox.GetAxes(out Vector3d axisX, out Vector3d axisY, out Vector3d axisZ);
 899135        Fixed64 dotX = Vector3d.Dot(direction, axisX).Abs();
 899136        Fixed64 dotY = Vector3d.Dot(direction, axisY).Abs();
 899137        Fixed64 dotZ = Vector3d.Dot(direction, axisZ).Abs();
 899138        Vector3d halfExtents = _orientedBox.HalfExtents;
 139
 140        // The orthographic projection of a box is the sum of each face area's
 141        // contribution along the view direction.
 899142        return GetProjectedFaceArea(halfExtents.Y, halfExtents.Z, dotX)
 899143            + GetProjectedFaceArea(halfExtents.X, halfExtents.Z, dotY)
 899144            + GetProjectedFaceArea(halfExtents.X, halfExtents.Y, dotZ);
 145    }
 146
 147    /// <inheritdoc/>
 148    public override Vector3d ClosestPointOnSurface(Vector3d other)
 149    {
 217150        FixedPointAnchor anchor =
 217151            GetClosestSurfaceAnchor(other, out _);
 217152        if (anchor.TryGetPoint(out Vector3d closestPoint))
 216153            return closestPoint;
 154
 1155        throw new InvalidOperationException(
 1156            "The selected cuboid surface point is outside the representable coordinate domain.");
 157    }
 158
 159    /// <inheritdoc/>
 160    public override Vector3d GetNormalAtPoint(Vector3d point) =>
 351161        _orientedBox.GetNearestFaceNormal(point);
 162
 163    internal override FixedPointAnchor GetClosestSurfaceAnchor(
 164        Vector3d point,
 165        out Vector3d normal)
 166    {
 428167        normal = _orientedBox.GetNearestFaceNormal(point);
 428168        return _orientedBox.GetClosestPointAnchor(point);
 169    }
 170
 171    internal override bool TryGetPlanarSurfaceNormal(Vector3d point, out Vector3d normal)
 172    {
 15173        normal = GetNormalAtPoint(point);
 15174        return true;
 175    }
 176
 177    private static Fixed64 GetSurfaceArea(Vector3d halfExtents) =>
 7926178        GetScaledProduct(halfExtents.X, halfExtents.Y, (Fixed64)8)
 7926179        + GetScaledProduct(halfExtents.X, halfExtents.Z, (Fixed64)8)
 7926180        + GetScaledProduct(halfExtents.Y, halfExtents.Z, (Fixed64)8);
 181
 182    private static Fixed64 GetProjectedFaceArea(
 183        Fixed64 firstHalfExtent,
 184        Fixed64 secondHalfExtent,
 185        Fixed64 alignment) =>
 2697186        Fixed64.TryMultiplyDivide(
 2697187            firstHalfExtent,
 2697188            secondHalfExtent,
 2697189            alignment,
 2697190            Fixed64.One / (Fixed64)4,
 2697191            out Fixed64 area)
 2697192            ? area
 2697193            : Fixed64.MaxValue;
 194
 195    private static Fixed64 GetInertiaContribution(Fixed64 mass, Fixed64 halfExtent) =>
 4290196        Fixed64.TryMultiplyDivide(
 4290197            mass,
 4290198            halfExtent,
 4290199            halfExtent,
 4290200            (Fixed64)3,
 4290201            out Fixed64 contribution)
 4290202            ? contribution
 4290203            : Fixed64.MaxValue;
 204
 205    private static Fixed64 GetScaledProduct(
 206        Fixed64 first,
 207        Fixed64 second,
 208        Fixed64 multiplier) =>
 23778209        Fixed64.TryMultiplyDivide(
 23778210            first,
 23778211            second,
 23778212            multiplier,
 23778213            Fixed64.One,
 23778214            out Fixed64 product)
 23778215            ? product
 23778216            : Fixed64.MaxValue;
 217
 218    /// <inheritdoc/>
 219    public override bool ColliderOverlapsRay(RaycastSegmentWorker worker, ref SwiftList<Vector3d> outputIntersectionPoin
 220    {
 112221        if (Shape == ColliderType.AABox)
 108222            return worker.CheckAABBoxOverlaps(this, ref outputIntersectionPoints);
 223
 4224        return worker.CheckOBBoxOverlaps(this, ref outputIntersectionPoints);
 225    }
 226}