< Summary

Line coverage
100%
Covered lines: 1375
Uncovered lines: 0
Coverable lines: 1375
Total lines: 2572
Line coverage: 100%
Branch coverage
100%
Covered branches: 288
Total branches: 288
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%11100%
File 1: SweepSphereAgainst2DBatch(...)100%22100%
File 1: SweepSphereAgainst2DAllBatch(...)100%22100%
File 1: SweepCircleAgainst3DBatch(...)100%22100%
File 1: SweepCircleAgainst3DAllBatch(...)100%22100%
File 1: WriteClosestHit(...)100%22100%
File 1: AppendRange(...)100%22100%
File 1: ResetBatchCounters(...)100%11100%
File 1: AccumulateBatchCounters(...)100%11100%
File 1: ValidateClosestBatchOutput(...)100%11100%
File 1: ValidateRangeBatchOutput(...)100%11100%
File 1: ValidateSweepSphereAgainst2DRequests(...)100%22100%
File 1: ValidateSweepCircleAgainst3DRequests(...)100%22100%
File 2: TrySweepCircleAgainst3DCollider(...)100%1414100%
File 2: TrySweepCircleAgainstCuboid(...)100%22100%
File 2: TrySweepCircleAgainstCapsule(...)100%22100%
File 2: TrySweepCircleAgainstCylinder(...)100%22100%
File 2: BuildCircleAgainstProjectedFiniteSlabHit(...)100%11100%
File 2: TrySweepCircleAgainstCone(...)100%22100%
File 2: TrySweepCircleAgainstMesh(...)100%1212100%
File 2: TrySweepCircleAgainstCompound3D(...)100%88100%
File 3: TrySweepCircleAgainstSphere(...)100%44100%
File 3: TrySweepPointInPlane(...)100%11100%
File 4: .ctor(...)100%11100%
File 4: get_Context()100%11100%
File 4: Reset()100%11100%
File 5: EmitMixedSweepDiagnostics(...)100%22100%
File 5: TrySweepSphereAgainst2DCandidate(...)100%1010100%
File 5: TrySweepCircleAgainst3DCandidate(...)100%1010100%
File 5: RecordAttempt(...)100%22100%
File 5: RecordAccepted()100%11100%
File 5: RecordRejected(...)100%22100%
File 6: TrySweepSphereAgainst2D(...)100%1010100%
File 6: TrySweepSphereAgainstCompound2D(...)100%88100%
File 6: TrySweepSphereAgainstCapsuleSlab(...)100%88100%
File 6: IsSphereOverlappingCapsuleSlabCapCore(...)100%1010100%
File 6: TrySweepSphereAgainstConvexSlab(...)100%1010100%
File 6: TrySweepConvexSlabCapFace(...)100%1010100%
File 6: TrySweepConvexSlabSideFaces(...)100%1818100%
File 6: TrySweepConvexSlabEdges(...)100%22100%
File 6: TrySweepCapsuleSlabCapFace(...)100%1010100%
File 6: TrySweepCapsuleSlabSide(...)100%66100%
File 6: TrySweepCapsuleSlabBoundaryEdges(...)100%44100%
File 6: TryKeepCapsuleSlabStraightRimSweep(...)100%11100%
File 6: DoesSphereOverlapConvexSlab(...)100%66100%
File 6: TryGetConvexSlabLocalSweep(...)100%22100%
File 6: TrySweepPointAgainstSegmentCapsule3D(...)100%11100%
File 6: GetCapDistance(...)100%11100%
File 6: TryKeepEarlierSweep(...)100%66100%
File 7: TrySweepSphereAgainstCircleSlab(...)100%22100%
File 8: BuildSphereAgainst2DHit(...)100%1010100%
File 8: BuildCircleAgainst3DHit(...)100%11100%
File 8: BuildCircleAgainst3DHit(...)100%44100%
File 8: GetSweepSurfaceAnchor(...)100%44100%
File 8: Resolve3DTo2DFallback(...)100%22100%
File 8: ResetLastQueryCounters()100%11100%
File 8: IsEligible2DTarget(...)100%88100%
File 8: IsEligible3DTarget(...)100%88100%
File 8: CreateSweepBounds(...)100%11100%
File 8: CreateCircleSlabSweepBounds(...)100%11100%
File 9: SweepCircleAgainst3D(...)100%11100%
File 9: SweepCircleAgainst3DAll(...)100%11100%
File 9: SweepCircleAgainstStatic3DAll(...)100%11100%
File 9: SweepCircleAgainst3DClosestCore(...)100%1414100%
File 9: SweepCircleAgainst3DAllCore(...)100%1212100%
File 10: SweepSphereAgainst2D(...)100%11100%
File 10: SweepSphereAgainst2DAll(...)100%11100%
File 10: SweepSphereAgainstStatic2DAll(...)100%11100%
File 10: SweepSphereAgainst2DClosestCore(...)100%1414100%
File 10: SweepSphereAgainst2DAllCore(...)100%1212100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.Batch.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.Batch.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 SwiftCollections;
 10using System;
 11
 12namespace Gravitas.Queries;
 13
 14/// <summary>
 15/// Owns batched mixed 3D/2D query behavior for one <see cref="GravitasWorldContext"/>.
 16/// </summary>
 17public sealed partial class GravitasQueryMixedService
 18{
 508319    private readonly SwiftList<PhysicsMixedHit> _batchMixedHits = new();
 20
 21    /// <summary>
 22    /// Gets the number of requests processed by the last mixed batch query call.
 23    /// </summary>
 24    public int LastBatchRequestCount { get; private set; }
 25
 26    /// <summary>
 27    /// Gets the number of hits accepted by the last mixed batch query call.
 28    /// </summary>
 29    public int LastBatchHitCount { get; private set; }
 30
 31    /// <summary>
 32    /// Gets the summed top-level candidate count reported by the last mixed batch query call.
 33    /// </summary>
 34    public int LastBatchCandidateCount { get; private set; }
 35
 36    /// <summary>
 37    /// Gets the summed mesh-triangle candidate count reported by the last mixed batch query call.
 38    /// </summary>
 39    public int LastBatchMeshTriangleCandidateCount { get; private set; }
 40
 41    /// <summary>
 42    /// Executes multiple mixed swept-sphere-against-2D queries and writes one closest-hit slot per request.
 43    /// </summary>
 44    public int SweepSphereAgainst2DBatch(
 45        ReadOnlySpan<PhysicsSweepSphereAgainst2DRequest> requests,
 46        Span<PhysicsMixedHit> closestHits)
 47    {
 20948        ValidateClosestBatchOutput(requests.Length, closestHits);
 20949        ValidateSweepSphereAgainst2DRequests(requests);
 20950        ResetBatchCounters(requests.Length);
 51
 20952        int hitCount = 0;
 83853        for (int i = 0; i < requests.Length; i++)
 54        {
 21055            PhysicsSweepSphereAgainst2DRequest request = requests[i];
 21056            bool found = SweepSphereAgainst2D(
 21057                request.Start,
 21058                request.End,
 21059                request.Radius,
 21060                request.LayerMask,
 21061                out PhysicsMixedHit hit,
 21062                request.ExcludedCollider,
 21063                request.IncludeTriggers);
 21064            WriteClosestHit(closestHits, i, hit, found, ref hitCount);
 65        }
 66
 20967        LastBatchHitCount = hitCount;
 20968        return hitCount;
 69    }
 70
 71    /// <summary>
 72    /// Executes multiple mixed swept-sphere-against-2D queries and appends all hits into one caller-owned hit buffer.
 73    /// </summary>
 74    public int SweepSphereAgainst2DAllBatch(
 75        ReadOnlySpan<PhysicsSweepSphereAgainst2DRequest> requests,
 76        SwiftList<PhysicsMixedHit> hits,
 77        Span<PhysicsQueryHitRange> ranges)
 78    {
 20979        SwiftThrowHelper.ThrowIfNull(hits, nameof(hits));
 20980        ValidateRangeBatchOutput(requests.Length, ranges);
 20981        ValidateSweepSphereAgainst2DRequests(requests);
 20982        ResetBatchCounters(requests.Length);
 20983        hits.FastClear();
 84
 83885        for (int i = 0; i < requests.Length; i++)
 86        {
 21087            PhysicsSweepSphereAgainst2DRequest request = requests[i];
 21088            int count = SweepSphereAgainst2DAll(
 21089                request.Start,
 21090                request.End,
 21091                request.Radius,
 21092                request.LayerMask,
 21093                _batchMixedHits,
 21094                request.ExcludedCollider,
 21095                request.IncludeTriggers);
 21096            AppendRange(hits, ranges, i, count);
 97        }
 98
 20999        LastBatchHitCount = hits.Count;
 209100        return hits.Count;
 101    }
 102
 103    /// <summary>
 104    /// Executes multiple mixed swept-circle-against-3D queries and writes one closest-hit slot per request.
 105    /// </summary>
 106    public int SweepCircleAgainst3DBatch(
 107        ReadOnlySpan<PhysicsSweepCircleAgainst3DRequest> requests,
 108        Span<PhysicsMixedHit> closestHits)
 109    {
 209110        ValidateClosestBatchOutput(requests.Length, closestHits);
 209111        ValidateSweepCircleAgainst3DRequests(requests);
 209112        ResetBatchCounters(requests.Length);
 113
 209114        int hitCount = 0;
 838115        for (int i = 0; i < requests.Length; i++)
 116        {
 210117            PhysicsSweepCircleAgainst3DRequest request = requests[i];
 210118            bool found = SweepCircleAgainst3D(
 210119                request.Start,
 210120                request.End,
 210121                request.Radius,
 210122                request.SlabCenterY,
 210123                request.HalfThickness,
 210124                request.LayerMask,
 210125                out PhysicsMixedHit hit,
 210126                request.ExcludedCollider,
 210127                request.IncludeTriggers);
 210128            WriteClosestHit(closestHits, i, hit, found, ref hitCount);
 129        }
 130
 209131        LastBatchHitCount = hitCount;
 209132        return hitCount;
 133    }
 134
 135    /// <summary>
 136    /// Executes multiple mixed swept-circle-against-3D queries and appends all hits into one caller-owned hit buffer.
 137    /// </summary>
 138    public int SweepCircleAgainst3DAllBatch(
 139        ReadOnlySpan<PhysicsSweepCircleAgainst3DRequest> requests,
 140        SwiftList<PhysicsMixedHit> hits,
 141        Span<PhysicsQueryHitRange> ranges)
 142    {
 209143        SwiftThrowHelper.ThrowIfNull(hits, nameof(hits));
 209144        ValidateRangeBatchOutput(requests.Length, ranges);
 209145        ValidateSweepCircleAgainst3DRequests(requests);
 209146        ResetBatchCounters(requests.Length);
 209147        hits.FastClear();
 148
 838149        for (int i = 0; i < requests.Length; i++)
 150        {
 210151            PhysicsSweepCircleAgainst3DRequest request = requests[i];
 210152            int count = SweepCircleAgainst3DAll(
 210153                request.Start,
 210154                request.End,
 210155                request.Radius,
 210156                request.SlabCenterY,
 210157                request.HalfThickness,
 210158                request.LayerMask,
 210159                _batchMixedHits,
 210160                request.ExcludedCollider,
 210161                request.IncludeTriggers);
 210162            AppendRange(hits, ranges, i, count);
 163        }
 164
 209165        LastBatchHitCount = hits.Count;
 209166        return hits.Count;
 167    }
 168
 169    private void WriteClosestHit(Span<PhysicsMixedHit> closestHits, int index, PhysicsMixedHit hit, bool found, ref int 
 170    {
 420171        if (found)
 172        {
 418173            closestHits[index] = hit;
 418174            hitCount++;
 418175            AccumulateBatchCounters(1);
 176        }
 177        else
 178        {
 2179            closestHits[index] = default;
 2180            AccumulateBatchCounters(0);
 181        }
 2182    }
 183
 184    private void AppendRange(
 185        SwiftList<PhysicsMixedHit> hits,
 186        Span<PhysicsQueryHitRange> ranges,
 187        int index,
 188        int count)
 189    {
 420190        int start = hits.Count;
 420191        if (count > 0)
 418192            hits.AddRange(_batchMixedHits.AsReadOnlySpan());
 193
 420194        ranges[index] = new PhysicsQueryHitRange(start, count);
 420195        AccumulateBatchCounters(count);
 420196    }
 197
 198    private void ResetBatchCounters(int requestCount)
 199    {
 867200        LastBatchRequestCount = requestCount;
 867201        LastBatchHitCount = 0;
 867202        LastBatchCandidateCount = 0;
 867203        LastBatchMeshTriangleCandidateCount = 0;
 867204    }
 205
 206    private void AccumulateBatchCounters(int hitCount)
 207    {
 840208        LastBatchHitCount += hitCount;
 840209        LastBatchCandidateCount += LastQueryCandidateCount;
 840210        LastBatchMeshTriangleCandidateCount += LastMeshTriangleCandidateCount;
 840211    }
 212
 213    private static void ValidateClosestBatchOutput(int requestCount, Span<PhysicsMixedHit> closestHits)
 214    {
 418215        SwiftThrowHelper.ThrowIfArgument(
 418216            closestHits.Length < requestCount,
 418217            nameof(closestHits),
 418218            "Closest-hit output span must contain at least one slot per request.");
 418219    }
 220
 221    private static void ValidateRangeBatchOutput(int requestCount, Span<PhysicsQueryHitRange> ranges)
 222    {
 418223        SwiftThrowHelper.ThrowIfArgument(
 418224            ranges.Length < requestCount,
 418225            nameof(ranges),
 418226            "Range output span must contain at least one slot per request.");
 418227    }
 228
 229    private static void ValidateSweepSphereAgainst2DRequests(ReadOnlySpan<PhysicsSweepSphereAgainst2DRequest> requests)
 230    {
 1676231        for (int i = 0; i < requests.Length; i++)
 232        {
 420233            SwiftThrowHelper.ThrowIfArgument(
 420234                requests[i].Radius <= Fixed64.Zero,
 420235                nameof(requests),
 420236                "Mixed swept sphere radius must be greater than zero.");
 237        }
 418238    }
 239
 240    private static void ValidateSweepCircleAgainst3DRequests(ReadOnlySpan<PhysicsSweepCircleAgainst3DRequest> requests)
 241    {
 1676242        for (int i = 0; i < requests.Length; i++)
 243        {
 420244            SwiftThrowHelper.ThrowIfArgument(
 420245                requests[i].Radius <= Fixed64.Zero,
 420246                nameof(requests),
 420247                "Mixed swept circle radius must be greater than zero.");
 420248            SwiftThrowHelper.ThrowIfArgument(
 420249                requests[i].HalfThickness <= Fixed64.Zero,
 420250                nameof(requests),
 420251                "Mixed swept circle half-thickness must be greater than zero.");
 252        }
 418253    }
 254}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.CircleAgainst3DReducers.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.CircleAgainst3DReducers.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.Colliders;
 11using SwiftCollections.Query;
 12using System;
 13
 14namespace Gravitas.Queries;
 15
 16/// <summary>
 17/// Owns mixed swept-circle reducers against 3D collider targets.
 18/// </summary>
 19public sealed partial class GravitasQueryMixedService
 20{
 21    private bool TrySweepCircleAgainst3DCollider(
 22        LSCollider collider,
 23        Vector2d start,
 24        Vector2d end,
 25        Vector2d direction2D,
 26        Fixed64 length,
 27        Fixed64 radius,
 28        Fixed64 slabCenterY,
 29        Fixed64 halfThickness,
 30        Vector3d direction3D,
 31        LSCollider2D? sourceCollider,
 32        out PhysicsMixedHit hit)
 33    {
 80934        if (collider is LSSphereCollider sphere)
 35        {
 49636            return TrySweepCircleAgainstSphere(
 49637                start,
 49638                end,
 49639                length,
 49640                radius,
 49641                slabCenterY,
 49642                halfThickness,
 49643                direction3D,
 49644                sphere,
 49645                sourceCollider,
 49646                out hit);
 47        }
 48
 31349        if (collider is LSCuboidCollider cuboid)
 50        {
 4951            return TrySweepCircleAgainstCuboid(
 4952                start,
 4953                end,
 4954                direction2D,
 4955                length,
 4956                radius,
 4957                slabCenterY,
 4958                halfThickness,
 4959                direction3D,
 4960                cuboid,
 4961                sourceCollider,
 4962                out hit);
 63        }
 64
 26465        if (collider is LSCapsuleCollider capsule)
 8066            return TrySweepCircleAgainstCapsule(
 8067                start,
 8068                end,
 8069                direction2D,
 8070                length,
 8071                radius,
 8072                slabCenterY,
 8073                halfThickness,
 8074                direction3D,
 8075                capsule,
 8076                sourceCollider,
 8077                out hit);
 78
 18479        if (collider is LSCylinderCollider cylinder)
 8080            return TrySweepCircleAgainstCylinder(
 8081                start,
 8082                end,
 8083                direction2D,
 8084                length,
 8085                radius,
 8086                slabCenterY,
 8087                halfThickness,
 8088                direction3D,
 8089                cylinder,
 8090                sourceCollider,
 8091                out hit);
 92
 10493        if (collider is LSConeCollider cone)
 7794            return TrySweepCircleAgainstCone(start, end, direction2D, length, radius, slabCenterY, halfThickness, direct
 95
 2796        if (collider is LSMeshCollider mesh)
 1897            return TrySweepCircleAgainstMesh(start, end, direction2D, length, radius, slabCenterY, halfThickness, direct
 98
 999        if (collider is LSCompoundCollider compound)
 8100            return TrySweepCircleAgainstCompound3D(start, end, direction2D, length, radius, slabCenterY, halfThickness, 
 101
 1102        hit = default;
 1103        return false;
 104    }
 105
 106    private static bool TrySweepCircleAgainstCuboid(
 107        Vector2d start,
 108        Vector2d end,
 109        Vector2d direction,
 110        Fixed64 length,
 111        Fixed64 radius,
 112        Fixed64 slabCenterY,
 113        Fixed64 halfThickness,
 114        Vector3d direction3D,
 115        LSCuboidCollider cuboid,
 116        LSCollider2D? sourceCollider,
 117        out PhysicsMixedHit hit)
 118    {
 49119        if (!cuboid.OrientedBox.TryGetCircleSlabSweepDistance(
 49120                new Vector3d(start.X, slabCenterY, start.Y),
 49121                direction,
 49122                length,
 49123                halfThickness,
 49124                radius,
 49125                out Fixed64 distance))
 126        {
 4127            hit = default;
 4128            return false;
 129        }
 130
 45131        hit = BuildCircleAgainstProjectedFiniteSlabHit(
 45132            start,
 45133            end,
 45134            length,
 45135            distance,
 45136            radius,
 45137            slabCenterY,
 45138            halfThickness,
 45139            direction3D,
 45140            cuboid,
 45141            sourceCollider);
 45142        return true;
 143    }
 144
 145    private static bool TrySweepCircleAgainstCapsule(
 146        Vector2d start,
 147        Vector2d end,
 148        Vector2d direction,
 149        Fixed64 length,
 150        Fixed64 radius,
 151        Fixed64 slabCenterY,
 152        Fixed64 halfThickness,
 153        Vector3d direction3D,
 154        LSCapsuleCollider capsule,
 155        LSCollider2D? sourceCollider,
 156        out PhysicsMixedHit hit)
 157    {
 80158        Fixed64 slabMinY = slabCenterY - halfThickness;
 80159        Fixed64 slabMaxY = slabCenterY + halfThickness;
 80160        if (!FiniteSlabProjectionSweep.TrySweepCircleAgainstCapsule(
 80161            start,
 80162            end,
 80163            direction,
 80164            length,
 80165            radius,
 80166            slabMinY,
 80167            slabMaxY,
 80168            capsule,
 80169            out Fixed64 distance))
 170        {
 3171            hit = default;
 3172            return false;
 173        }
 174
 77175        hit = BuildCircleAgainstProjectedFiniteSlabHit(
 77176            start,
 77177            end,
 77178            length,
 77179            distance,
 77180            radius,
 77181            slabCenterY,
 77182            halfThickness,
 77183            direction3D,
 77184            capsule,
 77185            sourceCollider);
 77186        return true;
 187    }
 188
 189    private static bool TrySweepCircleAgainstCylinder(
 190        Vector2d start,
 191        Vector2d end,
 192        Vector2d direction,
 193        Fixed64 length,
 194        Fixed64 radius,
 195        Fixed64 slabCenterY,
 196        Fixed64 halfThickness,
 197        Vector3d direction3D,
 198        LSCylinderCollider cylinder,
 199        LSCollider2D? sourceCollider,
 200        out PhysicsMixedHit hit)
 201    {
 80202        Fixed64 slabMinY = slabCenterY - halfThickness;
 80203        Fixed64 slabMaxY = slabCenterY + halfThickness;
 80204        if (!FiniteSlabProjectionSweep.TrySweepCircleAgainstCylinder(
 80205            start,
 80206            end,
 80207            direction,
 80208            length,
 80209            radius,
 80210            slabMinY,
 80211            slabMaxY,
 80212            cylinder,
 80213            out Fixed64 distance))
 214        {
 3215            hit = default;
 3216            return false;
 217        }
 218
 77219        hit = BuildCircleAgainstProjectedFiniteSlabHit(
 77220            start,
 77221            end,
 77222            length,
 77223            distance,
 77224            radius,
 77225            slabCenterY,
 77226            halfThickness,
 77227            direction3D,
 77228            cylinder,
 77229            sourceCollider);
 77230        return true;
 231    }
 232
 233    private static PhysicsMixedHit BuildCircleAgainstProjectedFiniteSlabHit(
 234        Vector2d start,
 235        Vector2d end,
 236        Fixed64 length,
 237        Fixed64 distance,
 238        Fixed64 radius,
 239        Fixed64 slabCenterY,
 240        Fixed64 halfThickness,
 241        Vector3d direction3D,
 242        LSCollider collider,
 243        LSCollider2D? sourceCollider)
 244    {
 274245        Vector2d center2D = new FixedSegment2d(start, end)
 274246            .GetPointAtDistance(distance, length);
 274247        Vector3d sweepCenter = new(center2D.X, slabCenterY, center2D.Y);
 274248        return BuildCircleAgainst3DHit(
 274249            collider,
 274250            sweepCenter,
 274251            direction3D,
 274252            radius,
 274253            slabCenterY,
 274254            halfThickness,
 274255            PhysicsQueryReducerKind.Exact,
 274256            distance,
 274257            sourceCollider);
 258    }
 259
 260    private static bool TrySweepCircleAgainstCone(
 261        Vector2d start,
 262        Vector2d end,
 263        Vector2d direction,
 264        Fixed64 length,
 265        Fixed64 radius,
 266        Fixed64 slabCenterY,
 267        Fixed64 halfThickness,
 268        Vector3d direction3D,
 269        LSConeCollider cone,
 270        LSCollider2D? sourceCollider,
 271        out PhysicsMixedHit hit)
 272    {
 77273        Fixed64 slabMinY = slabCenterY - halfThickness;
 77274        Fixed64 slabMaxY = slabCenterY + halfThickness;
 77275        if (!FiniteSlabProjectionSweep.TrySweepCircleAgainstCone(
 77276            start,
 77277            end,
 77278            direction,
 77279            length,
 77280            radius,
 77281            slabMinY,
 77282            slabMaxY,
 77283            cone,
 77284            out Fixed64 distance))
 285        {
 2286            hit = default;
 2287            return false;
 288        }
 289
 75290        hit = BuildCircleAgainstProjectedFiniteSlabHit(
 75291            start,
 75292            end,
 75293            length,
 75294            distance,
 75295            radius,
 75296            slabCenterY,
 75297            halfThickness,
 75298            direction3D,
 75299            cone,
 75300            sourceCollider);
 75301        return true;
 302    }
 303
 304    private bool TrySweepCircleAgainstMesh(
 305        Vector2d start,
 306        Vector2d end,
 307        Vector2d direction,
 308        Fixed64 length,
 309        Fixed64 radius,
 310        Fixed64 slabCenterY,
 311        Fixed64 halfThickness,
 312        Vector3d direction3D,
 313        LSMeshCollider mesh,
 314        LSCollider2D? sourceCollider,
 315        out PhysicsMixedHit hit)
 316    {
 18317        CreateCircleSlabSweepBounds(
 18318            start,
 18319            end,
 18320            radius,
 18321            slabCenterY,
 18322            halfThickness,
 18323            out Vector3d min,
 18324            out Vector3d max);
 18325        mesh.GetTrianglesInBounds(new FixedBoundVolume(min, max), _meshTriangleCandidates);
 18326        LastMeshTriangleCandidateCount += _meshTriangleCandidates.Count;
 327
 18328        bool found = false;
 18329        Fixed64 bestDistance = Fixed64.MaxValue;
 18330        int bestTriangleIndex = int.MaxValue;
 18331        PhysicsMixedHit best = default;
 332
 88333        for (int i = 0; i < _meshTriangleCandidates.Count; i++)
 334        {
 26335            int triangleIndex = _meshTriangleCandidates[i];
 26336            mesh.Mesh.GetLocalTriangleVertices(
 26337                triangleIndex,
 26338                out Vector3d first,
 26339                out Vector3d second,
 26340                out Vector3d third);
 26341            var triangle = new FixedTriangle(
 26342                first,
 26343                second,
 26344                third);
 26345            if (!triangle.TryGetFiniteSlabProjectedCircleSweep(
 26346                mesh.Mesh.Origin,
 26347                mesh.Mesh.Rotation,
 26348                start,
 26349                direction,
 26350                maximumDistance: length,
 26351                radius,
 26352                slabCenterY,
 26353                halfThickness,
 26354                out Fixed64 distance,
 26355                out FixedPointAnchor triangleContact))
 356            {
 357                continue;
 358            }
 359
 23360            if (found
 23361                && (distance > bestDistance
 23362                    || (distance == bestDistance && triangleIndex >= bestTriangleIndex)))
 363            {
 364                continue;
 365            }
 366
 21367            Vector2d center2D = new FixedSegment2d(start, end)
 21368                .GetPointAtDistance(distance, length);
 21369            Vector3d sweepCenter = new(center2D.X, slabCenterY, center2D.Y);
 21370            best = BuildCircleAgainst3DHit(
 21371                mesh,
 21372                triangleContact,
 21373                sweepCenter,
 21374                direction3D,
 21375                radius,
 21376                slabCenterY,
 21377                halfThickness,
 21378                PhysicsQueryReducerKind.Exact,
 21379                distance,
 21380                sourceCollider);
 21381            bestDistance = distance;
 21382            bestTriangleIndex = triangleIndex;
 21383            found = true;
 384        }
 385
 18386        hit = best;
 18387        return found;
 388    }
 389
 390    private bool TrySweepCircleAgainstCompound3D(
 391        Vector2d start,
 392        Vector2d end,
 393        Vector2d direction,
 394        Fixed64 length,
 395        Fixed64 radius,
 396        Fixed64 slabCenterY,
 397        Fixed64 halfThickness,
 398        Vector3d direction3D,
 399        LSCompoundCollider compound,
 400        LSCollider2D? sourceCollider,
 401        out PhysicsMixedHit hit)
 402    {
 8403        bool found = false;
 8404        Fixed64 bestDistance = Fixed64.MaxValue;
 8405        PhysicsMixedHit best = default;
 406
 52407        for (int i = 0; i < compound.PartCount; i++)
 408        {
 18409            LSCollider part = compound.GetPartCollider(i);
 18410            if (!TrySweepCircleAgainst3DCollider(
 18411                part,
 18412                start,
 18413                end,
 18414                direction,
 18415                length,
 18416                radius,
 18417                slabCenterY,
 18418                halfThickness,
 18419                direction3D,
 18420                sourceCollider,
 18421                out PhysicsMixedHit candidate))
 422            {
 423                continue;
 424            }
 425
 8426            if (!PhysicsHitSelectionPolicy.ShouldReplaceDistance(candidate.Distance, found, bestDistance))
 427                continue;
 428
 4429            best = candidate;
 4430            bestDistance = candidate.Distance;
 4431            found = true;
 432        }
 433
 8434        if (!found)
 435        {
 4436            hit = default;
 4437            return false;
 438        }
 439
 4440        hit = new PhysicsMixedHit(
 4441            compound,
 4442            sourceCollider,
 4443            best.Anchor3D,
 4444            best.Anchor2D,
 4445            best.Normal3DTo2D,
 4446            best.ReducerKind,
 4447            best.Distance,
 4448            best.Direction3D);
 4449        return true;
 450    }
 451
 452}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.CircleGeometry.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.CircleGeometry.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.Colliders;
 11
 12namespace Gravitas.Queries;
 13
 14/// <summary>
 15/// Owns shared projected-circle sweep geometry helpers for mixed query reducers.
 16/// </summary>
 17public sealed partial class GravitasQueryMixedService
 18{
 19    internal static bool TrySweepCircleAgainstSphere(
 20        Vector2d start,
 21        Vector2d end,
 22        Fixed64 length,
 23        Fixed64 radius,
 24        Fixed64 slabCenterY,
 25        Fixed64 halfThickness,
 26        Vector3d direction3D,
 27        LSSphereCollider sphere,
 28        LSCollider2D? sourceCollider,
 29        out PhysicsMixedHit hit)
 30    {
 49731        Fixed64 sphereRadius = sphere.ScaledRadius;
 49732        if (!FixedMath.TryGetSphereSlabCrossSectionRadius(
 49733                sphere.Center.Y,
 49734                sphereRadius,
 49735                slabCenterY,
 49736                halfThickness,
 49737                out Fixed64 planarSphereRadius))
 38        {
 239            hit = default;
 240            return false;
 41        }
 42
 49543        Vector2d sphereCenter = new(sphere.Center.X, sphere.Center.Z);
 49544        if (!TrySweepPointInPlane(
 49545            start,
 49546            end,
 49547            length,
 49548            sphereCenter,
 49549            planarSphereRadius,
 49550            radius,
 49551            out Fixed64 distance))
 52        {
 453            hit = default;
 454            return false;
 55        }
 56
 49157        Vector2d center2D = new FixedSegment2d(start, end)
 49158            .GetPointAtDistance(distance, length);
 49159        Vector3d sweepCenter = new(
 49160            center2D.X,
 49161            slabCenterY,
 49162            center2D.Y);
 49163        hit = BuildCircleAgainst3DHit(
 49164            sphere,
 49165            sweepCenter,
 49166            direction3D,
 49167            radius,
 49168            slabCenterY,
 49169            halfThickness,
 49170            PhysicsQueryReducerKind.Exact,
 49171            distance,
 49172            sourceCollider);
 49173        return true;
 74    }
 75
 76    private static bool TrySweepPointInPlane(
 77        Vector2d start,
 78        Vector2d end,
 79        Fixed64 length,
 80        Vector2d point,
 81        Fixed64 radius,
 82        Fixed64 radiusExpansion,
 83        out Fixed64 distance) =>
 49584        new FixedSegment2d(start, end)
 49585            .TryGetCircleIntersectionDistanceInterval(
 49586                new FixedBoundCircle(point, radius),
 49587                radiusExpansion,
 49588                length,
 49589                out distance,
 49590                out _,
 49591                out _,
 49592                out _);
 93}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.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 Gravitas.Colliders;
 9using SwiftCollections;
 10
 11namespace Gravitas.Queries;
 12
 13/// <summary>
 14/// Owns explicit mixed 3D/2D query buffers for one <see cref="GravitasWorldContext"/>.
 15/// </summary>
 16public sealed partial class GravitasQueryMixedService
 17{
 18    private readonly GravitasWorldContext _context;
 508319    private readonly SwiftList<LSCollider2D> _candidates2D = new();
 508320    private readonly SwiftList<LSCollider> _candidates3D = new();
 508321    private readonly SwiftList<int> _meshTriangleCandidates = new();
 22
 23    /// <summary>
 24    /// Creates a mixed query service owned by the specified world context.
 25    /// </summary>
 508326    public GravitasQueryMixedService(GravitasWorldContext context)
 27    {
 508328        SwiftThrowHelper.ThrowIfNull(context, nameof(context));
 508329        _context = context;
 508330    }
 31
 32    /// <summary>
 33    /// Gets the world context that owns this query service.
 34    /// </summary>
 135    public GravitasWorldContext Context => _context;
 36
 37    internal int LastQueryCandidateCount { get; private set; }
 38
 39    internal int LastMeshTriangleCandidateCount { get; private set; }
 40
 41    /// <summary>
 42    /// Clears reusable mixed-query buffers and diagnostic counters.
 43    /// </summary>
 44    public void Reset()
 45    {
 3146        _candidates2D.FastClear();
 3147        _candidates3D.FastClear();
 3148        _meshTriangleCandidates.FastClear();
 3149        _batchMixedHits.FastClear();
 3150        ResetLastQueryCounters();
 3151        ResetBatchCounters(0);
 3152    }
 53
 54}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.Diagnostics.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.Diagnostics.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 Gravitas.Colliders;
 10using Gravitas.Diagnostics;
 11
 12namespace Gravitas.Queries;
 13
 14/// <summary>
 15/// Owns mixed query diagnostics, candidate accounting, and reducer counters.
 16/// </summary>
 17public sealed partial class GravitasQueryMixedService
 18{
 19    private void EmitMixedSweepDiagnostics(
 20        GravitasColliderDimension sourceDimension,
 21        GravitasColliderDimension targetDimension,
 22        Vector3d start,
 23        Vector3d end,
 24        Fixed64 radius,
 25        int layerMaskBits,
 26        bool found,
 27        int hitCount,
 28        PhysicsMixedHit hit,
 29        bool captureReducerDiagnostics,
 30        QueryReducerCounters reducerCounters)
 31    {
 133532        _context.Diagnostics.EmitMixedQuery(
 133533            start,
 133534            end,
 133535            radius,
 133536            layerMaskBits,
 133537            found,
 133538            hitCount,
 133539            hit);
 40
 133541        if (!captureReducerDiagnostics)
 133142            return;
 43
 444        _context.Diagnostics.EmitQuerySummary(
 445            sourceDimension,
 446            targetDimension,
 447            start,
 448            end,
 449            reducerCounters.ExactReducerAttempts,
 450            reducerCounters.AcceptedHits,
 451            fallbackHits: 0,
 452            rejectedConservativeCandidates:
 453                reducerCounters.RejectedConservativeCandidates);
 454    }
 55
 56    private static bool TrySweepSphereAgainst2DCandidate(
 57        Vector3d start,
 58        Vector3d end,
 59        Vector3d direction,
 60        Fixed64 length,
 61        Fixed64 radius,
 62        LSCollider2D collider,
 63        LSCollider? excludedCollider,
 64        bool includeTriggers,
 65        bool staticTargetsOnly,
 66        bool captureReducerDiagnostics,
 67        ref QueryReducerCounters reducerCounters,
 68        out PhysicsMixedHit candidate)
 69    {
 64270        if (!IsEligible2DTarget(collider, excludedCollider, includeTriggers, staticTargetsOnly))
 71        {
 272            candidate = default;
 273            return false;
 74        }
 75
 64076        PhysicsQueryReducerKind reducerKind = default;
 64077        if (captureReducerDiagnostics)
 78        {
 279            reducerKind = MixedQueryReducerClassifier.ClassifySweepSphereAgainst2D(collider);
 280            reducerCounters.RecordAttempt(reducerKind);
 81        }
 82
 64083        if (!TrySweepSphereAgainst2D(start, end, direction, length, radius, collider, out candidate))
 84        {
 1985            if (captureReducerDiagnostics)
 186                reducerCounters.RecordRejected(reducerKind);
 1987            return false;
 88        }
 89
 62090        if (captureReducerDiagnostics)
 191            reducerCounters.RecordAccepted();
 62092        return true;
 93    }
 94
 95    private bool TrySweepCircleAgainst3DCandidate(
 96        LSCollider collider,
 97        Vector2d start,
 98        Vector2d end,
 99        Vector2d direction2D,
 100        Fixed64 length,
 101        Fixed64 radius,
 102        Fixed64 slabCenterY,
 103        Fixed64 halfThickness,
 104        Vector3d direction3D,
 105        LSCollider2D? excludedCollider,
 106        bool includeTriggers,
 107        bool staticTargetsOnly,
 108        bool captureReducerDiagnostics,
 109        ref QueryReducerCounters reducerCounters,
 110        out PhysicsMixedHit candidate)
 111    {
 793112        if (!IsEligible3DTarget(collider, excludedCollider, includeTriggers, staticTargetsOnly))
 113        {
 2114            candidate = default;
 2115            return false;
 116        }
 117
 791118        PhysicsQueryReducerKind reducerKind = default;
 791119        if (captureReducerDiagnostics)
 120        {
 3121            reducerKind = MixedQueryReducerClassifier.ClassifySweepCircleAgainst3D(collider);
 3122            reducerCounters.RecordAttempt(reducerKind);
 123        }
 124
 791125        if (!TrySweepCircleAgainst3DCollider(
 791126            collider,
 791127            start,
 791128            end,
 791129            direction2D,
 791130            length,
 791131            radius,
 791132            slabCenterY,
 791133            halfThickness,
 791134            direction3D,
 791135            excludedCollider,
 791136            out candidate))
 137        {
 14138            if (captureReducerDiagnostics)
 1139                reducerCounters.RecordRejected(reducerKind);
 14140            return false;
 141        }
 142
 777143        if (captureReducerDiagnostics)
 2144            reducerCounters.RecordAccepted();
 777145        return true;
 146    }
 147
 148
 149    private struct QueryReducerCounters
 150    {
 151        public int ExactReducerAttempts;
 152        public int AcceptedHits;
 153        public int RejectedConservativeCandidates;
 154
 155        public void RecordAttempt(PhysicsQueryReducerKind reducerKind)
 156        {
 5157            if (reducerKind == PhysicsQueryReducerKind.Exact)
 4158                ExactReducerAttempts++;
 5159        }
 160
 3161        public void RecordAccepted() => AcceptedHits++;
 162
 163        public void RecordRejected(PhysicsQueryReducerKind reducerKind)
 164        {
 2165            if (reducerKind == PhysicsQueryReducerKind.ConservativeFallback)
 1166                RejectedConservativeCandidates++;
 2167        }
 168    }
 169}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.SphereAgainst2DReducers.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.SphereAgainst2DReducers.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.Colliders;
 11using Gravitas.CollisionHandling;
 12using System;
 13
 14namespace Gravitas.Queries;
 15
 16/// <summary>
 17/// Owns mixed swept-sphere reducers against embedded 2D collider targets.
 18/// </summary>
 19public sealed partial class GravitasQueryMixedService
 20{
 21    private static bool TrySweepSphereAgainst2D(
 22        Vector3d start,
 23        Vector3d end,
 24        Vector3d direction,
 25        Fixed64 length,
 26        Fixed64 radius,
 27        LSCollider2D collider,
 28        out PhysicsMixedHit hit)
 29    {
 72130        if (collider is LSCompoundCollider2D compound)
 4131            return TrySweepSphereAgainstCompound2D(start, end, direction, length, radius, compound, out hit);
 32
 68033        if (collider is LSCircleCollider2D circle)
 7534            return TrySweepSphereAgainstCircleSlab(start, end, direction, length, radius, circle, out hit);
 35
 60536        if (collider is LSCapsuleCollider2D capsule)
 1837            return TrySweepSphereAgainstCapsuleSlab(start, end, direction, length, radius, capsule, out hit);
 38
 58739        if (collider is LSAABBoxCollider2D || collider is LSPolygonCollider2D)
 58640            return TrySweepSphereAgainstConvexSlab(start, end, direction, length, radius, collider, out hit);
 41
 142        throw new InvalidOperationException(
 143            "The embedded 2D collider type is not supported by mixed queries.");
 44    }
 45
 46    private static bool TrySweepSphereAgainstCompound2D(
 47        Vector3d start,
 48        Vector3d end,
 49        Vector3d direction,
 50        Fixed64 length,
 51        Fixed64 radius,
 52        LSCompoundCollider2D compound,
 53        out PhysicsMixedHit hit)
 54    {
 4155        bool found = false;
 4156        Fixed64 bestDistance = Fixed64.MaxValue;
 4157        PhysicsMixedHit best = default;
 58
 24459        for (int i = 0; i < compound.PartCount; i++)
 60        {
 8161            LSCollider2D part = compound.GetPartCollider(i);
 8162            if (!TrySweepSphereAgainst2D(start, end, direction, length, radius, part, out PhysicsMixedHit candidate))
 63                continue;
 64
 7965            if (!PhysicsHitSelectionPolicy.ShouldReplaceDistance(candidate.Distance, found, bestDistance))
 66                continue;
 67
 4168            best = candidate;
 4169            bestDistance = candidate.Distance;
 4170            found = true;
 71        }
 72
 4173        if (!found)
 74        {
 175            hit = default;
 176            return false;
 77        }
 78
 4079        hit = new PhysicsMixedHit(
 4080            null,
 4081            compound,
 4082            best.Anchor3D,
 4083            best.Anchor2D,
 4084            best.Normal3DTo2D,
 4085            best.ReducerKind,
 4086            best.Distance,
 4087            best.Direction3D);
 4088        return true;
 89    }
 90
 91    internal static bool TrySweepSphereAgainstCapsuleSlab(
 92        Vector3d start,
 93        Vector3d end,
 94        Vector3d direction,
 95        Fixed64 length,
 96        Fixed64 radius,
 97        LSCapsuleCollider2D capsule,
 98        out PhysicsMixedHit hit)
 99    {
 25100        if (IsSphereOverlappingCapsuleSlabCapCore(start, radius, capsule))
 101        {
 1102            hit = BuildSphereAgainst2DHit(
 1103                capsule,
 1104                start,
 1105                radius,
 1106                PhysicsQueryReducerKind.Exact,
 1107                Fixed64.Zero,
 1108                direction);
 1109            return true;
 110        }
 111
 24112        Fixed64 slabMinY = capsule.MixedBounds3D.Min.Y;
 24113        Fixed64 slabMaxY = capsule.MixedBounds3D.Max.Y;
 24114        SweepCandidate best = default;
 115
 24116        if (Fixed64.TryAdd(slabMaxY, radius, out Fixed64 expandedMaxY))
 117        {
 23118            TrySweepCapsuleSlabCapFace(
 23119                start,
 23120                end,
 23121                length,
 23122                capsule,
 23123                expandedMaxY,
 23124                requireStartBeyondPlane: true,
 23125                -Vector3d.Up,
 23126                ref best);
 127        }
 128
 24129        if (Fixed64.TrySubtract(slabMinY, radius, out Fixed64 expandedMinY))
 130        {
 23131            TrySweepCapsuleSlabCapFace(
 23132                start,
 23133                end,
 23134                length,
 23135                capsule,
 23136                expandedMinY,
 23137                requireStartBeyondPlane: false,
 23138                Vector3d.Up,
 23139                ref best);
 140        }
 141
 24142        TrySweepCapsuleSlabSide(
 24143            start,
 24144            end,
 24145            length,
 24146            capsule,
 24147            capsule.ScaledRadius,
 24148            radius,
 24149            slabMinY,
 24150            slabMaxY,
 24151            ref best);
 24152        TrySweepCapsuleSlabBoundaryEdges(
 24153            start,
 24154            end,
 24155            length,
 24156            capsule,
 24157            capsule.ScaledRadius,
 24158            radius,
 24159            slabMinY,
 24160            slabMaxY,
 24161            ref best);
 162
 24163        if (!best.Found)
 164        {
 10165            hit = default;
 10166            return false;
 167        }
 168
 14169        Vector3d sweepCenter = new FixedSegment(start, end).GetPointAtDistance(best.Distance, length);
 14170        hit = BuildSphereAgainst2DHit(
 14171            capsule,
 14172            sweepCenter,
 14173            radius,
 14174            PhysicsQueryReducerKind.Exact,
 14175            best.Distance,
 14176            direction,
 14177            best.FeatureNormal);
 14178        return true;
 179    }
 180
 181    private static bool IsSphereOverlappingCapsuleSlabCapCore(
 182        Vector3d center,
 183        Fixed64 radius,
 184        LSCapsuleCollider2D capsule)
 185    {
 25186        Fixed64 slabMinY = capsule.MixedBounds3D.Min.Y;
 25187        Fixed64 slabMaxY = capsule.MixedBounds3D.Max.Y;
 188        Fixed64 verticalExcess;
 25189        if (center.Y > slabMaxY)
 190        {
 10191            if (!Fixed64.TrySubtract(center.Y, slabMaxY, out verticalExcess))
 1192                return false;
 193        }
 15194        else if (center.Y < slabMinY)
 195        {
 3196            if (!Fixed64.TrySubtract(slabMinY, center.Y, out verticalExcess))
 1197                return false;
 198        }
 199        else
 12200            return false;
 201
 11202        return verticalExcess <= radius
 11203            && capsule.ContainsPoint(new Vector2d(center.X, center.Z));
 204    }
 205
 206    internal static bool TrySweepSphereAgainstConvexSlab(
 207        Vector3d start,
 208        Vector3d end,
 209        Vector3d direction,
 210        Fixed64 length,
 211        Fixed64 radius,
 212        LSCollider2D collider,
 213        out PhysicsMixedHit hit)
 214    {
 591215        if (DoesSphereOverlapConvexSlab(start, radius, collider))
 216        {
 1217            hit = BuildSphereAgainst2DHit(
 1218                collider,
 1219                start,
 1220                radius,
 1221                PhysicsQueryReducerKind.Exact,
 1222                Fixed64.Zero,
 1223                direction);
 1224            return true;
 225        }
 226
 590227        Fixed64 slabMinY = collider.MixedBounds3D.Min.Y;
 590228        Fixed64 slabMaxY = collider.MixedBounds3D.Max.Y;
 590229        SweepCandidate best = default;
 230
 590231        if (Fixed64.TryAdd(slabMaxY, radius, out Fixed64 expandedMaxY))
 232        {
 589233            TrySweepConvexSlabCapFace(
 589234                start,
 589235                end,
 589236                length,
 589237                collider,
 589238                expandedMaxY,
 589239                requireStartBeyondPlane: true,
 589240                -Vector3d.Up,
 589241                ref best);
 242        }
 243
 590244        if (Fixed64.TrySubtract(slabMinY, radius, out Fixed64 expandedMinY))
 245        {
 589246            TrySweepConvexSlabCapFace(
 589247                start,
 589248                end,
 589249                length,
 589250                collider,
 589251                expandedMinY,
 589252                requireStartBeyondPlane: false,
 589253                Vector3d.Up,
 589254                ref best);
 255        }
 256
 590257        if (TryGetConvexSlabLocalSweep(
 590258                start,
 590259                end,
 590260                direction,
 590261                collider,
 590262                out Vector3d localStart,
 590263                out Vector3d localEnd,
 590264                out Vector3d localDirection))
 265        {
 589266            Fixed64 localSlabMinY = -collider.MixedHalfThickness;
 589267            Fixed64 localSlabMaxY = collider.MixedHalfThickness;
 589268            TrySweepConvexSlabSideFaces(
 589269                localStart,
 589270                localEnd,
 589271                localDirection,
 589272                length,
 589273                radius,
 589274                collider,
 589275                localSlabMinY,
 589276                localSlabMaxY,
 589277                ref best);
 589278            TrySweepConvexSlabEdges(
 589279                localStart,
 589280                localEnd,
 589281                length,
 589282                radius,
 589283                collider,
 589284                localSlabMinY,
 589285                localSlabMaxY,
 589286                ref best);
 287        }
 288
 590289        if (!best.Found)
 290        {
 10291            hit = default;
 10292            return false;
 293        }
 294
 580295        Vector3d sweepCenter = new FixedSegment(start, end).GetPointAtDistance(best.Distance, length);
 580296        hit = BuildSphereAgainst2DHit(
 580297            collider,
 580298            sweepCenter,
 580299            radius,
 580300            PhysicsQueryReducerKind.Exact,
 580301            best.Distance,
 580302            direction,
 580303            best.FeatureNormal);
 580304        return true;
 305    }
 306
 307    private static void TrySweepConvexSlabCapFace(
 308        Vector3d start,
 309        Vector3d end,
 310        Fixed64 length,
 311        LSCollider2D collider,
 312        Fixed64 planeY,
 313        bool requireStartBeyondPlane,
 314        Vector3d featureNormal,
 315        ref SweepCandidate best)
 316    {
 1178317        bool canReach = requireStartBeyondPlane
 1178318            ? start.Y > planeY && end.Y <= planeY
 1178319            : start.Y < planeY && end.Y >= planeY;
 1178320        if (!canReach)
 753321            return;
 322
 425323        Fixed64 distance = GetCapDistance(start.Y, end.Y, planeY, length);
 425324        Vector3d point = new FixedSegment(start, end).GetPointAtDistance(distance, length);
 425325        if (!collider.ContainsPoint(new Vector2d(point.X, point.Z)))
 2326            return;
 327
 423328        TryKeepEarlierSweep(true, distance, featureNormal, ref best);
 423329    }
 330
 331    private static void TrySweepConvexSlabSideFaces(
 332        Vector3d start,
 333        Vector3d end,
 334        Vector3d direction,
 335        Fixed64 length,
 336        Fixed64 radius,
 337        LSCollider2D collider,
 338        Fixed64 slabMinY,
 339        Fixed64 slabMaxY,
 340        ref SweepCandidate best)
 341    {
 589342        Vector2d planarStart = new(start.X, start.Z);
 589343        Vector2d planarDirection = new(direction.X, direction.Z);
 589344        Vector2d polygonCenter = Vector2d.Zero;
 589345        int vertexCount = collider.VertexCount;
 346
 5890347        for (int i = 0; i < vertexCount; i++)
 348        {
 2356349            Vector2d first = collider.GetScaledLocalVertexUnchecked(i);
 2356350            Vector2d second = collider.GetScaledLocalVertexUnchecked((i + 1) % vertexCount);
 2356351            Vector2d edge = second - first;
 2356352            Fixed64 edgeLength = edge.Magnitude;
 2356353            Vector2d edgeDirection = edge / edgeLength;
 2356354            Vector2d outward = new(edge.Y, -edge.X);
 2356355            if (Vector2d.Dot(polygonCenter - first, outward) > Fixed64.Zero)
 304356                outward = -outward;
 2356357            outward /= outward.Magnitude;
 2356358            Fixed64 signedStart = Vector2d.Dot(planarStart - first, outward);
 2356359            Fixed64 signedDirection = Vector2d.Dot(planarDirection, outward);
 2356360            if (signedStart <= radius || signedDirection >= -Fixed64.Epsilon)
 361                continue;
 362
 237363            Fixed64 distance = (radius - signedStart) / signedDirection;
 237364            if (distance > length)
 365                continue;
 366
 236367            Vector3d sweepCenter = new FixedSegment(start, end).GetPointAtDistance(distance, length);
 236368            if (sweepCenter.Y < slabMinY || sweepCenter.Y > slabMaxY)
 369                continue;
 370
 232371            Vector2d planarPoint = new(sweepCenter.X, sweepCenter.Z);
 232372            Fixed64 projection = Vector2d.Dot(planarPoint - first, edgeDirection);
 232373            if (projection < Fixed64.Zero || projection > edgeLength)
 374                continue;
 375
 79376            TryKeepEarlierSweep(true, distance, default, ref best);
 377        }
 589378    }
 379
 380    private static void TrySweepConvexSlabEdges(
 381        Vector3d start,
 382        Vector3d end,
 383        Fixed64 length,
 384        Fixed64 radius,
 385        LSCollider2D collider,
 386        Fixed64 slabMinY,
 387        Fixed64 slabMaxY,
 388        ref SweepCandidate best)
 389    {
 589390        int vertexCount = collider.VertexCount;
 5890391        for (int i = 0; i < vertexCount; i++)
 392        {
 2356393            Vector2d first = collider.GetScaledLocalVertexUnchecked(i);
 2356394            Vector2d second = collider.GetScaledLocalVertexUnchecked((i + 1) % vertexCount);
 2356395            Vector3d bottomFirst = new(first.X, slabMinY, first.Y);
 2356396            Vector3d topFirst = new(first.X, slabMaxY, first.Y);
 2356397            Vector3d bottomSecond = new(second.X, slabMinY, second.Y);
 2356398            Vector3d topSecond = new(second.X, slabMaxY, second.Y);
 399
 2356400            TryKeepEarlierSweep(
 2356401                TrySweepPointAgainstSegmentCapsule3D(
 2356402                    start,
 2356403                    end,
 2356404                    length,
 2356405                    bottomFirst,
 2356406                    topFirst,
 2356407                    Fixed64.Zero,
 2356408                    radius,
 2356409                    out Fixed64 verticalDistance),
 2356410                verticalDistance,
 2356411                default,
 2356412                ref best);
 2356413            TryKeepEarlierSweep(
 2356414                TrySweepPointAgainstSegmentCapsule3D(
 2356415                    start,
 2356416                    end,
 2356417                    length,
 2356418                    bottomFirst,
 2356419                    bottomSecond,
 2356420                    Fixed64.Zero,
 2356421                    radius,
 2356422                    out Fixed64 bottomDistance),
 2356423                bottomDistance,
 2356424                default,
 2356425                ref best);
 2356426            TryKeepEarlierSweep(
 2356427                TrySweepPointAgainstSegmentCapsule3D(
 2356428                    start,
 2356429                    end,
 2356430                    length,
 2356431                    topFirst,
 2356432                    topSecond,
 2356433                    Fixed64.Zero,
 2356434                    radius,
 2356435                    out Fixed64 topDistance),
 2356436                topDistance,
 2356437                default,
 2356438                ref best);
 439        }
 589440    }
 441
 442    private static void TrySweepCapsuleSlabCapFace(
 443        Vector3d start,
 444        Vector3d end,
 445        Fixed64 length,
 446        LSCapsuleCollider2D capsule,
 447        Fixed64 planeY,
 448        bool requireStartBeyondPlane,
 449        Vector3d featureNormal,
 450        ref SweepCandidate best)
 451    {
 46452        bool canReach = requireStartBeyondPlane
 46453            ? start.Y > planeY && end.Y <= planeY
 46454            : start.Y < planeY && end.Y >= planeY;
 46455        if (!canReach)
 40456            return;
 457
 6458        Fixed64 distance = GetCapDistance(start.Y, end.Y, planeY, length);
 6459        Vector3d point = new FixedSegment(start, end).GetPointAtDistance(distance, length);
 6460        if (!capsule.ContainsPoint(new Vector2d(point.X, point.Z)))
 2461            return;
 462
 4463        TryKeepEarlierSweep(true, distance, featureNormal, ref best);
 4464    }
 465
 466    private static void TrySweepCapsuleSlabSide(
 467        Vector3d start,
 468        Vector3d end,
 469        Fixed64 length,
 470        LSCapsuleCollider2D capsule,
 471        Fixed64 targetRadius,
 472        Fixed64 radiusExpansion,
 473        Fixed64 slabMinY,
 474        Fixed64 slabMaxY,
 475        ref SweepCandidate best)
 476    {
 24477        var planarSegment = new FixedSegment2d(
 24478            new Vector2d(start.X, start.Z),
 24479            new Vector2d(end.X, end.Z));
 24480        if (!planarSegment.TryGetCapsuleIntersectionDistanceInterval(
 24481                capsule.Center,
 24482                capsule.Rotation,
 24483                capsule.AxisLength,
 24484                targetRadius,
 24485                radiusExpansion,
 24486                length,
 24487                out Fixed64 distance,
 24488                out _,
 24489                out _,
 24490                out _))
 491        {
 5492            return;
 493        }
 494
 19495        Fixed64 y = new FixedSegment(start, end).GetPointAtDistance(distance, length).Y;
 19496        if (y < slabMinY || y > slabMaxY)
 10497            return;
 498
 9499        TryKeepEarlierSweep(true, distance, default, ref best);
 9500    }
 501
 502    private static void TrySweepCapsuleSlabBoundaryEdges(
 503        Vector3d start,
 504        Vector3d end,
 505        Fixed64 length,
 506        LSCapsuleCollider2D capsule,
 507        Fixed64 targetRadius,
 508        Fixed64 radiusExpansion,
 509        Fixed64 slabMinY,
 510        Fixed64 slabMaxY,
 511        ref SweepCandidate best)
 512    {
 24513        Vector2d capsuleAxis = new(
 24514            -FixedMath.Sin(capsule.Rotation),
 24515            FixedMath.Cos(capsule.Rotation));
 24516        if (!FixedSegment2d.TryGetCenteredAxisEndpoint(
 24517                capsule.Center,
 24518                capsuleAxis,
 24519                capsule.AxisLength,
 24520                positive: false,
 24521                out Vector2d firstEndpoint)
 24522            || !FixedSegment2d.TryGetCenteredAxisEndpoint(
 24523                capsule.Center,
 24524                capsuleAxis,
 24525                capsule.AxisLength,
 24526                positive: true,
 24527                out Vector2d secondEndpoint))
 528        {
 2529            return;
 530        }
 531
 22532        Vector2d outward =
 22533            new Vector2d(capsuleAxis.Y, -capsuleAxis.X)
 22534            * targetRadius;
 22535        Vector2d firstSideStart = firstEndpoint + outward;
 22536        Vector2d firstSideEnd = secondEndpoint + outward;
 22537        Vector2d secondSideStart = firstEndpoint - outward;
 22538        Vector2d secondSideEnd = secondEndpoint - outward;
 539
 22540        TryKeepCapsuleSlabStraightRimSweep(
 22541            start, end, length, firstSideStart, firstSideEnd, slabMinY, radiusExpansion, ref best);
 22542        TryKeepCapsuleSlabStraightRimSweep(
 22543            start, end, length, firstSideStart, firstSideEnd, slabMaxY, radiusExpansion, ref best);
 22544        TryKeepCapsuleSlabStraightRimSweep(
 22545            start, end, length, secondSideStart, secondSideEnd, slabMinY, radiusExpansion, ref best);
 22546        TryKeepCapsuleSlabStraightRimSweep(
 22547            start, end, length, secondSideStart, secondSideEnd, slabMaxY, radiusExpansion, ref best);
 548
 22549        Fixed64 slabCenterY = capsule.MixedSlabCenterY;
 22550        Fixed64 slabHalfThickness = capsule.MixedHalfThickness;
 22551        TryKeepEarlierSweep(
 22552            new FixedSegment(start, end).TryGetSweptSphereFiniteCylinderIntersectionDistance(
 22553                new Vector3d(firstEndpoint.X, slabCenterY, firstEndpoint.Y),
 22554                Vector3d.Up,
 22555                slabHalfThickness,
 22556                targetRadius,
 22557                radiusExpansion,
 22558                length,
 22559                out Fixed64 firstVerticalDistance),
 22560            firstVerticalDistance,
 22561            default,
 22562            ref best);
 22563        TryKeepEarlierSweep(
 22564            new FixedSegment(start, end).TryGetSweptSphereFiniteCylinderIntersectionDistance(
 22565                new Vector3d(secondEndpoint.X, slabCenterY, secondEndpoint.Y),
 22566                Vector3d.Up,
 22567                slabHalfThickness,
 22568                targetRadius,
 22569                radiusExpansion,
 22570                length,
 22571                out Fixed64 secondVerticalDistance),
 22572            secondVerticalDistance,
 22573            default,
 22574            ref best);
 22575    }
 576
 577    private static void TryKeepCapsuleSlabStraightRimSweep(
 578        Vector3d start,
 579        Vector3d end,
 580        Fixed64 length,
 581        Vector2d planarStart,
 582        Vector2d planarEnd,
 583        Fixed64 y,
 584        Fixed64 radiusExpansion,
 585        ref SweepCandidate best)
 586    {
 88587        TryKeepEarlierSweep(
 88588            TrySweepPointAgainstSegmentCapsule3D(
 88589                start,
 88590                end,
 88591                length,
 88592                new Vector3d(planarStart.X, y, planarStart.Y),
 88593                new Vector3d(planarEnd.X, y, planarEnd.Y),
 88594                Fixed64.Zero,
 88595                radiusExpansion,
 88596                out Fixed64 distance),
 88597            distance,
 88598            default,
 88599            ref best);
 88600    }
 601
 602    private static bool DoesSphereOverlapConvexSlab(
 603        Vector3d center,
 604        Fixed64 radius,
 605        LSCollider2D collider)
 606    {
 591607        if (!FixedMath.TryGetSphereSlabCrossSectionRadius(
 591608                center.Y,
 591609                radius,
 591610                collider.MixedSlabCenterY,
 591611                collider.MixedHalfThickness,
 591612                out Fixed64 planarRadius))
 613        {
 426614            return false;
 615        }
 616
 165617        Vector2d planarPoint = new(center.X, center.Z);
 165618        if (collider.ContainsPoint(planarPoint))
 1619            return true;
 620
 164621        return collider.TryGetClosestBoundaryAnchor(
 164622                planarPoint,
 164623                out _,
 164624                out Fixed64 planarDistance)
 164625            && planarDistance <= planarRadius;
 626    }
 627
 628    private static bool TryGetConvexSlabLocalSweep(
 629        Vector3d start,
 630        Vector3d end,
 631        Vector3d direction,
 632        LSCollider2D collider,
 633        out Vector3d localStart,
 634        out Vector3d localEnd,
 635        out Vector3d localDirection)
 636    {
 590637        var frameOrigin = new Vector3d(
 590638            collider.Center.X,
 590639            collider.MixedSlabCenterY,
 590640            collider.Center.Y);
 590641        FixedQuaternion frameRotation =
 590642            FixedQuaternion.FromAxisAngle(
 590643                Vector3d.Up,
 590644                -collider.ConvexRotation);
 590645        var startAnchor = new FixedPointAnchor(
 590646            start,
 590647            FixedQuaternion.Identity,
 590648            Vector3d.Zero);
 590649        var endAnchor = new FixedPointAnchor(
 590650            end,
 590651            FixedQuaternion.Identity,
 590652            Vector3d.Zero);
 590653        bool representable = startAnchor.TryGetLocalPointIn(
 590654                frameOrigin,
 590655                frameRotation,
 590656                out localStart)
 590657            & endAnchor.TryGetLocalPointIn(
 590658                frameOrigin,
 590659                frameRotation,
 590660                out localEnd);
 590661        localDirection = representable
 590662            ? frameRotation.Inverse().Rotate(direction)
 590663            : default;
 590664        return representable;
 665    }
 666
 667    private static bool TrySweepPointAgainstSegmentCapsule3D(
 668        Vector3d start,
 669        Vector3d end,
 670        Fixed64 length,
 671        Vector3d segmentStart,
 672        Vector3d segmentEnd,
 673        Fixed64 radius,
 674        Fixed64 radiusExpansion,
 675        out Fixed64 distance)
 676    {
 7156677        return new FixedSegment(start, end).TryGetCapsuleIntersectionDistanceInterval(
 7156678                new FixedSegment(segmentStart, segmentEnd),
 7156679                radius,
 7156680                radiusExpansion,
 7156681                length,
 7156682                out distance,
 7156683                out _,
 7156684                out _,
 7156685                out _);
 686    }
 687
 688    private static Fixed64 GetCapDistance(
 689        Fixed64 start,
 690        Fixed64 end,
 691        Fixed64 plane,
 692        Fixed64 length)
 693    {
 694        // Cap admission proves that plane lies between start and end. The public
 695        // sweep contract has already proved the segment displacement and length
 696        // representable, so the fused result is bounded by length and cannot fail.
 431697        Fixed64 verticalSpan = end - start;
 431698        Fixed64 capOffset = plane - start;
 431699        _ = Fixed64.TryMultiplyDivide(length, capOffset, verticalSpan, out Fixed64 distance);
 431700        return distance;
 701    }
 702
 703    private static void TryKeepEarlierSweep(
 704        bool candidateFound,
 705        Fixed64 candidateDistance,
 706        Vector3d candidateFeatureNormal,
 707        ref SweepCandidate best)
 708    {
 7715709        if (!candidateFound)
 5947710            return;
 711
 1768712        if (best.Found && candidateDistance >= best.Distance)
 949713            return;
 714
 819715        best.Found = true;
 819716        best.Distance = candidateDistance;
 819717        best.FeatureNormal = candidateFeatureNormal;
 819718    }
 719
 720    private struct SweepCandidate
 721    {
 722        public bool Found;
 723        public Fixed64 Distance;
 724        public Vector3d FeatureNormal;
 725    }
 726
 727}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.SphereSlab.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.SphereSlab.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.Colliders;
 11
 12namespace Gravitas.Queries;
 13
 14/// <summary>
 15/// Owns mixed swept-sphere slab and prism helper reducers.
 16/// </summary>
 17public sealed partial class GravitasQueryMixedService
 18{
 19    /// <summary>
 20    /// Reduces a prepared spatial-distance sphere sweep against one embedded circle slab.
 21    /// The direction must match the authored start/end segment and the length must use the same parameterization.
 22    /// </summary>
 23    internal static bool TrySweepSphereAgainstCircleSlab(
 24        Vector3d start,
 25        Vector3d end,
 26        Vector3d direction,
 27        Fixed64 length,
 28        Fixed64 radius,
 29        LSCircleCollider2D circle,
 30        out PhysicsMixedHit hit)
 31    {
 8632        Vector2d planarCenter = circle.Center;
 8633        var center = new Vector3d(planarCenter.X, circle.MixedSlabCenterY, planarCenter.Y);
 8634        var segment = new FixedSegment(start, end);
 8635        if (!segment
 8636            .TryGetSweptSphereCenteredFiniteCylinderIntersectionDistanceFromHalfAxisLength(
 8637                center,
 8638                Vector3d.Up,
 8639                circle.MixedHalfThickness,
 8640                circle.ScaledRadius,
 8641                radius,
 8642                length,
 8643                out Fixed64 distance))
 44        {
 645            hit = default;
 646            return false;
 47        }
 48
 8049        Vector3d sweepCenter = segment.GetPointAtDistance(distance, length);
 8050        hit = BuildSphereAgainst2DHit(
 8051            circle,
 8052            sweepCenter,
 8053            radius,
 8054            PhysicsQueryReducerKind.Exact,
 8055            distance,
 8056            direction);
 8057        return true;
 58    }
 59
 60}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.Support.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.Support.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.Colliders;
 11using Gravitas.CollisionHandling;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas.Queries;
 15
 16/// <summary>
 17/// Owns mixed query hit construction, eligibility, bounds, and shared support helpers.
 18/// </summary>
 19public sealed partial class GravitasQueryMixedService
 20{
 21    private static PhysicsMixedHit BuildSphereAgainst2DHit(
 22        LSCollider2D collider,
 23        Vector3d sweepCenter,
 24        Fixed64 radius,
 25        PhysicsQueryReducerKind reducerKind,
 26        Fixed64 distance,
 27        Vector3d direction,
 28        Vector3d featureNormal = default)
 29    {
 67630        ContactAnchor anchor2D =
 67631            MixedEmbedded2DGeometry.GetClosestAnchorOnEmbeddedVolume(
 67632                collider,
 67633                sweepCenter);
 34        // Exact reducers bound each anchor component by the admitted sphere
 35        // expansion.
 67636        _ = anchor2D.TryGetOffsetFrom(
 67637            sweepCenter,
 67638            out Vector3d to2D);
 39        Vector3d normal3DTo2D;
 67640        if (featureNormal != Vector3d.Zero)
 41        {
 42742            normal3DTo2D = featureNormal;
 43        }
 24944        else if (to2D.Y != Fixed64.Zero
 24945                 && new Vector2d(to2D.X, to2D.Z).MagnitudeSquared <= Fixed64.Epsilon)
 46        {
 1447            normal3DTo2D = to2D.Y > Fixed64.Zero
 1448                ? Vector3d.Up
 1449                : -Vector3d.Up;
 50        }
 51        else
 52        {
 23553            normal3DTo2D = to2D.MagnitudeSquared > Fixed64.Epsilon
 23554                ? to2D.Normalized
 23555                : Resolve3DTo2DFallback(collider, sweepCenter, direction);
 56        }
 67657        var anchor3D = new ContactAnchor(
 67658            sweepCenter,
 67659            normal3DTo2D * radius);
 67660        return new PhysicsMixedHit(
 67661            null,
 67662            collider,
 67663            anchor3D,
 67664            anchor2D,
 67665            normal3DTo2D,
 67666            reducerKind,
 67667            distance,
 67668            direction);
 69    }
 70
 71    private static PhysicsMixedHit BuildCircleAgainst3DHit(
 72        LSCollider collider,
 73        Vector3d sweepCenter,
 74        Vector3d direction,
 75        Fixed64 radius,
 76        Fixed64 slabCenterY,
 77        Fixed64 halfThickness,
 78        PhysicsQueryReducerKind reducerKind,
 79        Fixed64 distance,
 80        LSCollider2D? sourceCollider)
 81    {
 76582        return BuildCircleAgainst3DHit(
 76583            collider,
 76584            GetSweepSurfaceAnchor(
 76585                collider,
 76586                sweepCenter,
 76587                direction),
 76588            sweepCenter,
 76589            direction,
 76590            radius,
 76591            slabCenterY,
 76592            halfThickness,
 76593            reducerKind,
 76594            distance,
 76595            sourceCollider);
 96    }
 97
 98    private static PhysicsMixedHit BuildCircleAgainst3DHit(
 99        LSCollider collider,
 100        FixedPointAnchor point3D,
 101        Vector3d sweepCenter,
 102        Vector3d direction,
 103        Fixed64 radius,
 104        Fixed64 slabCenterY,
 105        Fixed64 halfThickness,
 106        PhysicsQueryReducerKind reducerKind,
 107        Fixed64 distance,
 108        LSCollider2D? sourceCollider)
 109    {
 786110        var anchor3D = new ContactAnchor(point3D);
 111        // Every accepted reducer bounds the target witness by the source
 112        // circle radius and slab half-thickness.
 786113        _ = anchor3D.TryGetOffsetFrom(
 786114            sweepCenter,
 786115            out Vector3d fromSweepCenter);
 786116        Vector3d to2D = -fromSweepCenter;
 786117        Vector3d normal3DTo2D = to2D.MagnitudeSquared > Fixed64.Epsilon
 786118            ? to2D.Normalized
 786119            : -direction;
 786120        Vector2d planarNormal = new(
 786121            normal3DTo2D.X,
 786122            normal3DTo2D.Z);
 786123        Vector2d planarOffset = planarNormal.MagnitudeSquared > Fixed64.Epsilon
 786124            ? -planarNormal.Normalized * radius
 786125            : Vector2d.Zero;
 786126        Fixed64 verticalOffset = FixedMath.Clamp(
 786127            fromSweepCenter.Y,
 786128            -halfThickness,
 786129            halfThickness);
 786130        var anchor2D = new ContactAnchor(
 786131            sweepCenter,
 786132            new Vector3d(
 786133                planarOffset.X,
 786134                verticalOffset,
 786135                planarOffset.Y));
 786136        return new PhysicsMixedHit(
 786137            collider,
 786138            sourceCollider,
 786139            anchor3D,
 786140            anchor2D,
 786141            normal3DTo2D,
 786142            reducerKind,
 786143            distance,
 786144            direction);
 145    }
 146
 147    private static FixedPointAnchor GetSweepSurfaceAnchor(
 148        LSCollider collider,
 149        Vector3d sweepCenter,
 150        Vector3d direction)
 151    {
 765152        if (collider is LSCuboidCollider cuboid)
 45153            return cuboid.OrientedBox.GetClosestPointAnchor(sweepCenter);
 154
 720155        Vector3d centerDelta = sweepCenter - collider.Center;
 720156        if (centerDelta.MagnitudeSquared <= Fixed64.Epsilon)
 157        {
 3158            return new FixedPointAnchor(
 3159                collider.Center,
 3160                FixedQuaternion.Identity,
 3161                -direction * collider.ScaledRadius);
 162        }
 163
 717164        return new FixedPointAnchor(
 717165            collider.ClosestPointOnSurface(sweepCenter),
 717166            FixedQuaternion.Identity,
 717167            Vector3d.Zero);
 168    }
 169
 170    private static Vector3d Resolve3DTo2DFallback(LSCollider2D collider, Vector3d sweepCenter, Vector3d direction)
 171    {
 4172        Vector3d embeddedCenter = new(collider.Center.X, collider.MixedSlabCenterY, collider.Center.Y);
 4173        Vector3d to2D = embeddedCenter - sweepCenter;
 4174        if (to2D.MagnitudeSquared > Fixed64.Epsilon)
 3175            return to2D.Normalized;
 176
 1177        return direction;
 178    }
 179
 180    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 181    private void ResetLastQueryCounters()
 182    {
 43183        LastQueryCandidateCount = 0;
 43184        LastMeshTriangleCandidateCount = 0;
 43185    }
 186
 187    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 188    private static bool IsEligible2DTarget(
 189        LSCollider2D collider,
 190        LSCollider? excludedCollider,
 191        bool includeTriggers,
 192        bool staticTargetsOnly)
 193    {
 642194        if (!includeTriggers && collider.IsTrigger)
 1195            return false;
 196
 641197        return excludedCollider == null
 641198            || (!ReferenceEquals(collider.AgentOrNull, excludedCollider.AgentOrNull)
 641199                && !excludedCollider.ExcludesMixedCollisionWith(collider));
 200    }
 201
 202    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 203    private static bool IsEligible3DTarget(
 204        LSCollider collider,
 205        LSCollider2D? excludedCollider,
 206        bool includeTriggers,
 207        bool staticTargetsOnly)
 208    {
 793209        if (!includeTriggers && collider.IsTrigger)
 1210            return false;
 211
 792212        return excludedCollider == null
 792213            || (!ReferenceEquals(collider.AgentOrNull, excludedCollider.AgentOrNull)
 792214                && !collider.ExcludesMixedCollisionWith(excludedCollider));
 215    }
 216
 217    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 218    private static void CreateSweepBounds(Vector3d start, Vector3d end, Fixed64 radius, out Vector3d min, out Vector3d m
 219    {
 646220        Vector3d radiusExtents = Vector3d.One * radius;
 646221        min = Vector3d.Min(start, end) - radiusExtents;
 646222        max = Vector3d.Max(start, end) + radiusExtents;
 646223    }
 224
 225    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 226    private static void CreateCircleSlabSweepBounds(
 227        Vector2d start,
 228        Vector2d end,
 229        Fixed64 radius,
 230        Fixed64 slabCenterY,
 231        Fixed64 halfThickness,
 232        out Vector3d min,
 233        out Vector3d max)
 234    {
 708235        min = new Vector3d(
 708236            FixedMath.Min(start.X, end.X) - radius,
 708237            slabCenterY - halfThickness,
 708238            FixedMath.Min(start.Y, end.Y) - radius);
 708239        max = new Vector3d(
 708240            FixedMath.Max(start.X, end.X) + radius,
 708241            slabCenterY + halfThickness,
 708242            FixedMath.Max(start.Y, end.Y) + radius);
 708243    }
 244
 245}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.SweepCircle3D.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.SweepCircle3D.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 Gravitas.Colliders;
 10using Gravitas.Diagnostics;
 11using Gravitas.Support;
 12using SwiftCollections;
 13
 14namespace Gravitas.Queries;
 15
 16/// <summary>
 17/// Owns mixed 2D-circle against 3D-collider sweep query behavior.
 18/// </summary>
 19public sealed partial class GravitasQueryMixedService
 20{
 21    /// <summary>
 22    /// Sweeps a 2D circle embedded in a finite mixed slab against 3D colliders and returns the closest hit.
 23    /// </summary>
 24    public bool SweepCircleAgainst3D(
 25        Vector2d start,
 26        Vector2d end,
 27        Fixed64 radius,
 28        Fixed64 slabCenterY,
 29        Fixed64 halfThickness,
 30        PhysicsLayerMask layerMask,
 31        out PhysicsMixedHit hit,
 32        LSCollider2D? excludedCollider = null,
 33        bool includeTriggers = true)
 34    {
 25835        return SweepCircleAgainst3DClosestCore(
 25836            start,
 25837            end,
 25838            radius,
 25839            slabCenterY,
 25840            halfThickness,
 25841            layerMask,
 25842            excludedCollider,
 25843            includeTriggers,
 25844            staticTargetsOnly: false,
 25845            cacheTargetPartitions: false,
 25846            out hit);
 47    }
 48
 49    /// <summary>
 50    /// Sweeps a 2D circle embedded in a finite mixed slab against 3D colliders and writes hits from closest to farthest
 51    /// </summary>
 52    public int SweepCircleAgainst3DAll(
 53        Vector2d start,
 54        Vector2d end,
 55        Fixed64 radius,
 56        Fixed64 slabCenterY,
 57        Fixed64 halfThickness,
 58        PhysicsLayerMask layerMask,
 59        SwiftList<PhysicsMixedHit> results,
 60        LSCollider2D? excludedCollider = null,
 61        bool includeTriggers = true)
 62    {
 30663        return SweepCircleAgainst3DAllCore(
 30664            start,
 30665            end,
 30666            radius,
 30667            slabCenterY,
 30668            halfThickness,
 30669            layerMask,
 30670            results,
 30671            excludedCollider,
 30672            includeTriggers,
 30673            staticTargetsOnly: false);
 74    }
 75
 76    internal int SweepCircleAgainstStatic3DAll(
 77        Vector2d start,
 78        Vector2d end,
 79        Fixed64 radius,
 80        Fixed64 slabCenterY,
 81        Fixed64 halfThickness,
 82        PhysicsLayerMask layerMask,
 83        SwiftList<PhysicsMixedHit> results,
 84        LSCollider2D? excludedCollider = null,
 85        bool includeTriggers = true,
 86        bool cacheTargetPartitions = false)
 87    {
 13288        return SweepCircleAgainst3DAllCore(
 13289            start,
 13290            end,
 13291            radius,
 13292            slabCenterY,
 13293            halfThickness,
 13294            layerMask,
 13295            results,
 13296            excludedCollider,
 13297            includeTriggers,
 13298            staticTargetsOnly: true,
 13299            cacheTargetPartitions);
 100    }
 101
 102    private bool SweepCircleAgainst3DClosestCore(
 103        Vector2d start,
 104        Vector2d end,
 105        Fixed64 radius,
 106        Fixed64 slabCenterY,
 107        Fixed64 halfThickness,
 108        PhysicsLayerMask layerMask,
 109        LSCollider2D? excludedCollider,
 110        bool includeTriggers,
 111        bool staticTargetsOnly,
 112        bool cacheTargetPartitions,
 113        out PhysicsMixedHit hit)
 114    {
 258115        SwiftThrowHelper.ThrowIfArgument(radius <= Fixed64.Zero, nameof(radius), "Mixed swept circle radius must be grea
 258116        SwiftThrowHelper.ThrowIfArgument(halfThickness <= Fixed64.Zero, nameof(halfThickness), "Mixed swept circle half-
 117
 258118        if (!Vector2d.TrySubtract(end, start, out Vector2d segment)
 258119            || !Vector2d.TryGetMagnitude(segment, out Fixed64 length)
 258120            || length <= Fixed64.Epsilon)
 121        {
 3122            ResetLastQueryCounters();
 3123            hit = default;
 3124            return false;
 125        }
 126
 255127        Vector3d start3D = new(start.X, slabCenterY, start.Y);
 255128        Vector3d end3D = new(end.X, slabCenterY, end.Y);
 255129        Vector2d direction2D = segment.Normalized;
 255130        Vector3d direction = new(direction2D.X, Fixed64.Zero, direction2D.Y);
 255131        CreateCircleSlabSweepBounds(start, end, radius, slabCenterY, halfThickness, out Vector3d min, out Vector3d max);
 255132        _context.MixedCollisions.Collect3DCandidatesInMixedBounds(
 255133            min,
 255134            max,
 255135            layerMask,
 255136            _candidates3D,
 255137            staticStyleOnly: staticTargetsOnly,
 255138            cachePartitionRefresh: cacheTargetPartitions);
 255139        LastQueryCandidateCount = _candidates3D.Count;
 255140        LastMeshTriangleCandidateCount = 0;
 255141        bool captureReducerDiagnostics = _context.Diagnostics.Enabled;
 255142        QueryReducerCounters reducerCounters = default;
 255143        bool found = false;
 255144        PhysicsMixedHit best = default;
 145
 1024146        for (int i = 0; i < _candidates3D.Count; i++)
 147        {
 257148            if (!TrySweepCircleAgainst3DCandidate(
 257149                _candidates3D[i],
 257150                start,
 257151                end,
 257152                direction2D,
 257153                length,
 257154                radius,
 257155                slabCenterY,
 257156                halfThickness,
 257157                direction,
 257158                excludedCollider,
 257159                includeTriggers,
 257160                staticTargetsOnly,
 257161                captureReducerDiagnostics,
 257162                ref reducerCounters,
 257163                out PhysicsMixedHit candidate))
 164            {
 165                continue;
 166            }
 167
 254168            if (PhysicsHitSelectionPolicy.ShouldReplace(candidate, found, best))
 169            {
 253170                best = candidate;
 253171                found = true;
 172            }
 173        }
 174
 255175        hit = found ? best : default;
 255176        EmitMixedSweepDiagnostics(
 255177            GravitasColliderDimension.TwoD,
 255178            GravitasColliderDimension.ThreeD,
 255179            start3D,
 255180            end3D,
 255181            radius,
 255182            layerMask.Bits,
 255183            found,
 255184            reducerCounters.AcceptedHits,
 255185            hit,
 255186            captureReducerDiagnostics,
 255187            reducerCounters);
 255188        return found;
 189    }
 190
 191    private int SweepCircleAgainst3DAllCore(
 192        Vector2d start,
 193        Vector2d end,
 194        Fixed64 radius,
 195        Fixed64 slabCenterY,
 196        Fixed64 halfThickness,
 197        PhysicsLayerMask layerMask,
 198        SwiftList<PhysicsMixedHit> results,
 199        LSCollider2D? excludedCollider,
 200        bool includeTriggers,
 201        bool staticTargetsOnly,
 202        bool cacheTargetPartitions = false)
 203    {
 438204        SwiftThrowHelper.ThrowIfNull(results, nameof(results));
 438205        SwiftThrowHelper.ThrowIfArgument(radius <= Fixed64.Zero, nameof(radius), "Mixed swept circle radius must be grea
 438206        SwiftThrowHelper.ThrowIfArgument(halfThickness <= Fixed64.Zero, nameof(halfThickness), "Mixed swept circle half-
 207
 438208        results.FastClear();
 438209        if (!Vector2d.TrySubtract(end, start, out Vector2d segment)
 438210            || !Vector2d.TryGetMagnitude(segment, out Fixed64 length)
 438211            || length <= Fixed64.Epsilon)
 212        {
 3213            ResetLastQueryCounters();
 3214            return 0;
 215        }
 216
 435217        Vector3d start3D = new(start.X, slabCenterY, start.Y);
 435218        Vector3d end3D = new(end.X, slabCenterY, end.Y);
 435219        Vector2d direction2D = segment.Normalized;
 435220        Vector3d direction = new(direction2D.X, Fixed64.Zero, direction2D.Y);
 435221        CreateCircleSlabSweepBounds(start, end, radius, slabCenterY, halfThickness, out Vector3d min, out Vector3d max);
 435222        _context.MixedCollisions.Collect3DCandidatesInMixedBounds(
 435223            min,
 435224            max,
 435225            layerMask,
 435226            _candidates3D,
 435227            staticStyleOnly: staticTargetsOnly,
 435228            cachePartitionRefresh: cacheTargetPartitions);
 435229        LastQueryCandidateCount = _candidates3D.Count;
 435230        LastMeshTriangleCandidateCount = 0;
 435231        bool captureReducerDiagnostics = _context.Diagnostics.Enabled;
 435232        QueryReducerCounters reducerCounters = default;
 233
 1942234        for (int i = 0; i < _candidates3D.Count; i++)
 235        {
 536236            if (!TrySweepCircleAgainst3DCandidate(
 536237                _candidates3D[i],
 536238                start,
 536239                end,
 536240                direction2D,
 536241                length,
 536242                radius,
 536243                slabCenterY,
 536244                halfThickness,
 536245                direction,
 536246                excludedCollider,
 536247                includeTriggers,
 536248                staticTargetsOnly,
 536249                captureReducerDiagnostics,
 536250                ref reducerCounters,
 536251                out PhysicsMixedHit candidate))
 252            {
 253                continue;
 254            }
 255
 523256            results.Add(candidate);
 257        }
 258
 435259        PhysicsMixedHitSorter.SortByDistance(results);
 435260        EmitMixedSweepDiagnostics(
 435261            GravitasColliderDimension.TwoD,
 435262            GravitasColliderDimension.ThreeD,
 435263            start3D,
 435264            end3D,
 435265            radius,
 435266            layerMask.Bits,
 435267            results.Count > 0,
 435268            results.Count,
 435269            results.Count > 0 ? results[0] : default,
 435270            captureReducerDiagnostics,
 435271            reducerCounters);
 435272        return results.Count;
 273    }
 274
 275}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/Mixed/GravitasQueryMixedService.SweepSphere2D.cs

#LineLine coverage
 1//=======================================================================
 2// GravitasQueryMixedService.SweepSphere2D.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 Gravitas.Colliders;
 10using Gravitas.Diagnostics;
 11using Gravitas.Support;
 12using SwiftCollections;
 13
 14namespace Gravitas.Queries;
 15
 16/// <summary>
 17/// Owns mixed 3D-sphere against embedded 2D-slab sweep query behavior.
 18/// </summary>
 19public sealed partial class GravitasQueryMixedService
 20{
 21    /// <summary>
 22    /// Sweeps a 3D sphere against embedded 2D mixed slabs and returns the closest hit.
 23    /// </summary>
 24    public bool SweepSphereAgainst2D(
 25        Vector3d start,
 26        Vector3d end,
 27        Fixed64 radius,
 28        PhysicsLayerMask layerMask,
 29        out PhysicsMixedHit hit,
 30        LSCollider? excludedCollider = null,
 31        bool includeTriggers = true)
 32    {
 27033        return SweepSphereAgainst2DClosestCore(
 27034            start,
 27035            end,
 27036            radius,
 27037            layerMask,
 27038            excludedCollider,
 27039            includeTriggers,
 27040            staticTargetsOnly: false,
 27041            cacheTargetPartitions: false,
 27042            out hit);
 43    }
 44
 45    /// <summary>
 46    /// Sweeps a 3D sphere against embedded 2D mixed slabs and writes hits from closest to farthest.
 47    /// </summary>
 48    public int SweepSphereAgainst2DAll(
 49        Vector3d start,
 50        Vector3d end,
 51        Fixed64 radius,
 52        PhysicsLayerMask layerMask,
 53        SwiftList<PhysicsMixedHit> results,
 54        LSCollider? excludedCollider = null,
 55        bool includeTriggers = true)
 56    {
 25957        return SweepSphereAgainst2DAllCore(
 25958            start,
 25959            end,
 25960            radius,
 25961            layerMask,
 25962            results,
 25963            excludedCollider,
 25964            includeTriggers,
 25965            staticTargetsOnly: false);
 66    }
 67
 68    internal int SweepSphereAgainstStatic2DAll(
 69        Vector3d start,
 70        Vector3d end,
 71        Fixed64 radius,
 72        PhysicsLayerMask layerMask,
 73        SwiftList<PhysicsMixedHit> results,
 74        LSCollider? excludedCollider = null,
 75        bool includeTriggers = true,
 76        bool cacheTargetPartitions = false)
 77    {
 12378        return SweepSphereAgainst2DAllCore(
 12379            start,
 12380            end,
 12381            radius,
 12382            layerMask,
 12383            results,
 12384            excludedCollider,
 12385            includeTriggers,
 12386            staticTargetsOnly: true,
 12387            cacheTargetPartitions);
 88    }
 89
 90    private bool SweepSphereAgainst2DClosestCore(
 91        Vector3d start,
 92        Vector3d end,
 93        Fixed64 radius,
 94        PhysicsLayerMask layerMask,
 95        LSCollider? excludedCollider,
 96        bool includeTriggers,
 97        bool staticTargetsOnly,
 98        bool cacheTargetPartitions,
 99        out PhysicsMixedHit hit)
 100    {
 270101        SwiftThrowHelper.ThrowIfArgument(radius <= Fixed64.Zero, nameof(radius), "Mixed swept sphere radius must be grea
 102
 270103        if (!Vector3d.TrySubtract(end, start, out Vector3d segment)
 270104            || !Vector3d.TryGetMagnitude(segment, out Fixed64 length)
 270105            || length <= Fixed64.Epsilon)
 106        {
 3107            ResetLastQueryCounters();
 3108            hit = default;
 3109            return false;
 110        }
 111
 267112        Vector3d direction = segment.Normalized;
 267113        CreateSweepBounds(start, end, radius, out Vector3d min, out Vector3d max);
 267114        _context.MixedCollisions.Collect2DCandidatesInMixedBounds(
 267115            min,
 267116            max,
 267117            layerMask,
 267118            _candidates2D,
 267119            staticStyleOnly: staticTargetsOnly,
 267120            cachePartitionRefresh: cacheTargetPartitions);
 267121        LastQueryCandidateCount = _candidates2D.Count;
 267122        LastMeshTriangleCandidateCount = 0;
 267123        bool captureReducerDiagnostics = _context.Diagnostics.Enabled;
 267124        QueryReducerCounters reducerCounters = default;
 267125        bool found = false;
 267126        PhysicsMixedHit best = default;
 127
 1068128        for (int i = 0; i < _candidates2D.Count; i++)
 129        {
 268130            if (!TrySweepSphereAgainst2DCandidate(
 268131                start,
 268132                end,
 268133                direction,
 268134                length,
 268135                radius,
 268136                _candidates2D[i],
 268137                excludedCollider,
 268138                includeTriggers,
 268139                staticTargetsOnly,
 268140                captureReducerDiagnostics,
 268141                ref reducerCounters,
 268142                out PhysicsMixedHit candidate))
 143            {
 144                continue;
 145            }
 146
 254147            if (PhysicsHitSelectionPolicy.ShouldReplace(candidate, found, best))
 148            {
 253149                best = candidate;
 253150                found = true;
 151            }
 152        }
 153
 266154        hit = found ? best : default;
 266155        EmitMixedSweepDiagnostics(
 266156            GravitasColliderDimension.ThreeD,
 266157            GravitasColliderDimension.TwoD,
 266158            start,
 266159            end,
 266160            radius,
 266161            layerMask.Bits,
 266162            found,
 266163            reducerCounters.AcceptedHits,
 266164            hit,
 266165            captureReducerDiagnostics,
 266166            reducerCounters);
 266167        return found;
 168    }
 169
 170    private int SweepSphereAgainst2DAllCore(
 171        Vector3d start,
 172        Vector3d end,
 173        Fixed64 radius,
 174        PhysicsLayerMask layerMask,
 175        SwiftList<PhysicsMixedHit> results,
 176        LSCollider? excludedCollider,
 177        bool includeTriggers,
 178        bool staticTargetsOnly,
 179        bool cacheTargetPartitions = false)
 180    {
 382181        SwiftThrowHelper.ThrowIfNull(results, nameof(results));
 382182        SwiftThrowHelper.ThrowIfArgument(radius <= Fixed64.Zero, nameof(radius), "Mixed swept sphere radius must be grea
 183
 382184        results.FastClear();
 382185        if (!Vector3d.TrySubtract(end, start, out Vector3d segment)
 382186            || !Vector3d.TryGetMagnitude(segment, out Fixed64 length)
 382187            || length <= Fixed64.Epsilon)
 188        {
 3189            ResetLastQueryCounters();
 3190            return 0;
 191        }
 192
 379193        Vector3d direction = segment.Normalized;
 379194        CreateSweepBounds(start, end, radius, out Vector3d min, out Vector3d max);
 379195        _context.MixedCollisions.Collect2DCandidatesInMixedBounds(
 379196            min,
 379197            max,
 379198            layerMask,
 379199            _candidates2D,
 379200            staticStyleOnly: staticTargetsOnly,
 379201            cachePartitionRefresh: cacheTargetPartitions);
 379202        LastQueryCandidateCount = _candidates2D.Count;
 379203        LastMeshTriangleCandidateCount = 0;
 379204        bool captureReducerDiagnostics = _context.Diagnostics.Enabled;
 379205        QueryReducerCounters reducerCounters = default;
 206
 1506207        for (int i = 0; i < _candidates2D.Count; i++)
 208        {
 374209            if (TrySweepSphereAgainst2DCandidate(
 374210                start,
 374211                end,
 374212                direction,
 374213                length,
 374214                radius,
 374215                _candidates2D[i],
 374216                excludedCollider,
 374217                includeTriggers,
 374218                staticTargetsOnly,
 374219                captureReducerDiagnostics,
 374220                ref reducerCounters,
 374221                out PhysicsMixedHit candidate))
 222            {
 366223                results.Add(candidate);
 224            }
 225        }
 226
 379227        PhysicsMixedHitSorter.SortByDistance(results);
 379228        EmitMixedSweepDiagnostics(
 379229            GravitasColliderDimension.ThreeD,
 379230            GravitasColliderDimension.TwoD,
 379231            start,
 379232            end,
 379233            radius,
 379234            layerMask.Bits,
 379235            results.Count > 0,
 379236            results.Count,
 379237            results.Count > 0 ? results[0] : default,
 379238            captureReducerDiagnostics,
 379239            reducerCounters);
 379240        return results.Count;
 241    }
 242
 243}

Methods/Properties

.ctor(Gravitas.GravitasWorldContext)
SweepSphereAgainst2DBatch(System.ReadOnlySpan`1<Gravitas.Queries.PhysicsSweepSphereAgainst2DRequest>,System.Span`1<Gravitas.Queries.PhysicsMixedHit>)
SweepSphereAgainst2DAllBatch(System.ReadOnlySpan`1<Gravitas.Queries.PhysicsSweepSphereAgainst2DRequest>,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,System.Span`1<Gravitas.Queries.PhysicsQueryHitRange>)
SweepCircleAgainst3DBatch(System.ReadOnlySpan`1<Gravitas.Queries.PhysicsSweepCircleAgainst3DRequest>,System.Span`1<Gravitas.Queries.PhysicsMixedHit>)
SweepCircleAgainst3DAllBatch(System.ReadOnlySpan`1<Gravitas.Queries.PhysicsSweepCircleAgainst3DRequest>,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,System.Span`1<Gravitas.Queries.PhysicsQueryHitRange>)
WriteClosestHit(System.Span`1<Gravitas.Queries.PhysicsMixedHit>,System.Int32,Gravitas.Queries.PhysicsMixedHit,System.Boolean,System.Int32&)
AppendRange(SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,System.Span`1<Gravitas.Queries.PhysicsQueryHitRange>,System.Int32,System.Int32)
ResetBatchCounters(System.Int32)
AccumulateBatchCounters(System.Int32)
ValidateClosestBatchOutput(System.Int32,System.Span`1<Gravitas.Queries.PhysicsMixedHit>)
ValidateRangeBatchOutput(System.Int32,System.Span`1<Gravitas.Queries.PhysicsQueryHitRange>)
ValidateSweepSphereAgainst2DRequests(System.ReadOnlySpan`1<Gravitas.Queries.PhysicsSweepSphereAgainst2DRequest>)
ValidateSweepCircleAgainst3DRequests(System.ReadOnlySpan`1<Gravitas.Queries.PhysicsSweepCircleAgainst3DRequest>)
TrySweepCircleAgainst3DCollider(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepCircleAgainstCuboid(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCuboidCollider,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepCircleAgainstCapsule(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCapsuleCollider,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepCircleAgainstCylinder(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCylinderCollider,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
BuildCircleAgainstProjectedFiniteSlabHit(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider2D)
TrySweepCircleAgainstCone(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSConeCollider,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepCircleAgainstMesh(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSMeshCollider,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepCircleAgainstCompound3D(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCompoundCollider,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepCircleAgainstSphere(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSSphereCollider,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepPointInPlane(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
.ctor(Gravitas.GravitasWorldContext)
get_Context()
Reset()
EmitMixedSweepDiagnostics(Gravitas.Diagnostics.GravitasColliderDimension,Gravitas.Diagnostics.GravitasColliderDimension,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Int32,System.Boolean,System.Int32,Gravitas.Queries.PhysicsMixedHit,System.Boolean,Gravitas.Queries.GravitasQueryMixedService/QueryReducerCounters)
TrySweepSphereAgainst2DCandidate(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D,Gravitas.Colliders.LSCollider,System.Boolean,System.Boolean,System.Boolean,Gravitas.Queries.GravitasQueryMixedService/QueryReducerCounters&,Gravitas.Queries.PhysicsMixedHit&)
TrySweepCircleAgainst3DCandidate(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCollider2D,System.Boolean,System.Boolean,System.Boolean,Gravitas.Queries.GravitasQueryMixedService/QueryReducerCounters&,Gravitas.Queries.PhysicsMixedHit&)
RecordAttempt(Gravitas.Queries.PhysicsQueryReducerKind)
RecordAccepted()
RecordRejected(Gravitas.Queries.PhysicsQueryReducerKind)
TrySweepSphereAgainst2D(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepSphereAgainstCompound2D(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCompoundCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepSphereAgainstCapsuleSlab(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCapsuleCollider2D,Gravitas.Queries.PhysicsMixedHit&)
IsSphereOverlappingCapsuleSlabCapCore(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCapsuleCollider2D)
TrySweepSphereAgainstConvexSlab(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D,Gravitas.Queries.PhysicsMixedHit&)
TrySweepConvexSlabCapFace(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Vector3d,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
TrySweepConvexSlabSideFaces(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
TrySweepConvexSlabEdges(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
TrySweepCapsuleSlabCapFace(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCapsuleCollider2D,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Vector3d,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
TrySweepCapsuleSlabSide(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCapsuleCollider2D,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
TrySweepCapsuleSlabBoundaryEdges(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCapsuleCollider2D,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
TryKeepCapsuleSlabStraightRimSweep(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
DoesSphereOverlapConvexSlab(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D)
TryGetConvexSlabLocalSweep(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,Gravitas.Colliders.LSCollider2D,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
TrySweepPointAgainstSegmentCapsule3D(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
GetCapDistance(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryKeepEarlierSweep(System.Boolean,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Queries.GravitasQueryMixedService/SweepCandidate&)
TrySweepSphereAgainstCircleSlab(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCircleCollider2D,Gravitas.Queries.PhysicsMixedHit&)
BuildSphereAgainst2DHit(Gravitas.Colliders.LSCollider2D,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Queries.PhysicsQueryReducerKind,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
BuildCircleAgainst3DHit(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Queries.PhysicsQueryReducerKind,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D)
BuildCircleAgainst3DHit(Gravitas.Colliders.LSCollider,FixedMathSharp.Geometry.FixedPointAnchor,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Queries.PhysicsQueryReducerKind,FixedMathSharp.Fixed64,Gravitas.Colliders.LSCollider2D)
GetSweepSurfaceAnchor(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
Resolve3DTo2DFallback(Gravitas.Colliders.LSCollider2D,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
ResetLastQueryCounters()
IsEligible2DTarget(Gravitas.Colliders.LSCollider2D,Gravitas.Colliders.LSCollider,System.Boolean,System.Boolean)
IsEligible3DTarget(Gravitas.Colliders.LSCollider,Gravitas.Colliders.LSCollider2D,System.Boolean,System.Boolean)
CreateSweepBounds(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
CreateCircleSlabSweepBounds(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
SweepCircleAgainst3D(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,Gravitas.Queries.PhysicsMixedHit&,Gravitas.Colliders.LSCollider2D,System.Boolean)
SweepCircleAgainst3DAll(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,Gravitas.Colliders.LSCollider2D,System.Boolean)
SweepCircleAgainstStatic3DAll(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,Gravitas.Colliders.LSCollider2D,System.Boolean,System.Boolean)
SweepCircleAgainst3DClosestCore(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,Gravitas.Colliders.LSCollider2D,System.Boolean,System.Boolean,System.Boolean,Gravitas.Queries.PhysicsMixedHit&)
SweepCircleAgainst3DAllCore(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,Gravitas.Colliders.LSCollider2D,System.Boolean,System.Boolean,System.Boolean)
SweepSphereAgainst2D(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,Gravitas.Queries.PhysicsMixedHit&,Gravitas.Colliders.LSCollider,System.Boolean)
SweepSphereAgainst2DAll(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,Gravitas.Colliders.LSCollider,System.Boolean)
SweepSphereAgainstStatic2DAll(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,Gravitas.Colliders.LSCollider,System.Boolean,System.Boolean)
SweepSphereAgainst2DClosestCore(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,Gravitas.Colliders.LSCollider,System.Boolean,System.Boolean,System.Boolean,Gravitas.Queries.PhysicsMixedHit&)
SweepSphereAgainst2DAllCore(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Support.PhysicsLayerMask,SwiftCollections.SwiftList`1<Gravitas.Queries.PhysicsMixedHit>,Gravitas.Colliders.LSCollider,System.Boolean,System.Boolean,System.Boolean)