< Summary

Line coverage
100%
Covered lines: 898
Uncovered lines: 0
Coverable lines: 898
Total lines: 1628
Line coverage: 100%
Branch coverage
100%
Covered branches: 204
Total branches: 204
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: .cctor()100%11100%
File 1: .ctor(...)100%11100%
File 1: PrepareConvexMeshSource(...)100%11100%
File 1: PrepareCompoundSource(...)100%11100%
File 1: PreparePrimitiveSource(...)100%22100%
File 1: PrepareCircleSlabSource(...)100%11100%
File 1: PrepareSphereSource(...)100%11100%
File 1: TrySweepPreparedSource(...)100%88100%
File 1: Prepare(...)100%11100%
File 1: Prepare(...)100%66100%
File 1: TrySweepCompoundSource(...)100%66100%
File 1: TrySweepSourceShape(...)100%88100%
File 1: TrySweepTargetCompound(...)100%66100%
File 1: TrySweepConcaveMeshTarget(...)100%88100%
File 1: BuildOrderedSweepTriangleCandidates(...)100%44100%
File 1: TryComputeSweepLowerBoundNumerator(...)100%11100%
File 1: TryComputeSweepLowerBoundNumerator(...)100%44100%
File 1: IncludeAxisEntryNumerator(...)100%1616100%
File 1: RemainingSweepTrianglesCannotBeat(...)100%22100%
File 1: TrySweepConvexTarget(...)100%1616100%
File 1: TryResolveEndpointBracket(...)100%1212100%
File 1: GetChordOffset(...)100%11100%
File 1: ResolveHitAnchor(...)100%1414100%
File 1: ResolveHitNormal(...)100%22100%
File 1: CreateColliderShape(...)100%11100%
File 1: CreateTriangleShape(...)100%11100%
File 1: ThrowIfConcaveSource(...)100%22100%
File 1: CreateSweptSourceBoundsInMeshFrame(...)100%11100%
File 1: CanSweptSourceShapeReachTarget(...)100%22100%
File 1: ComesBeforeReducerCandidate(...)100%44100%
File 1: CreateConcaveSourceException(...)100%11100%
File 2: ComputeDistance(...)100%1818100%
File 2: CreateSupportPoint(...)100%11100%
File 2: SolveClosestSimplex(...)100%66100%
File 3: ReduceSegment(...)100%66100%
File 3: ReduceTriangle(...)100%11100%
File 3: ReduceTetrahedron(...)100%22100%
File 3: EvaluateFace(...)100%44100%
File 3: IsCloser(...)100%44100%
File 3: ReduceByWeights(...)100%22100%
File 3: AddWeightedPoint(...)100%22100%
File 3: ClosestPointOnTriangleToOrigin(...)100%22100%
File 3: ClosestPointOnDegenerateTriangleToOrigin(...)100%44100%
File 3: GetClosestSegmentWeights(...)100%66100%
File 3: IsOriginInsideTetrahedron(...)100%88100%
File 3: IsSameSideOfFace(...)100%22100%
File 3: SignedTetrahedronVolume6(...)100%11100%
File 3: ContainsSupportPoint(...)100%66100%
File 4: .ctor(...)100%11100%
File 4: .ctor(...)100%11100%
File 4: Compare(...)100%22100%
File 4: .ctor(...)100%11100%
File 4: CreateIntersection(...)100%11100%
File 4: .ctor(...)100%11100%
File 4: get_Intersection()100%11100%
File 4: FromWeights(...)100%11100%
File 4: FromIndexedWeights(...)100%22100%
File 4: FromSpanWeights(...)100%66100%
File 4: FromArrayWeights(...)100%11100%
File 4: .ctor(...)100%11100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/3D/Sweeps/ConvexSweepQueryWorker.cs

#LineLine coverage
 1//=======================================================================
 2// ConvexSweepQueryWorker.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 SwiftCollections;
 13using SwiftCollections.Query;
 14using System;
 15using System.Runtime.CompilerServices;
 16
 17namespace Gravitas.Queries;
 18
 19/// <summary>
 20/// Performs deterministic translational convex-source sweeps against 3D query
 21/// targets using support-mapped conservative advancement.
 22/// </summary>
 23internal sealed partial class ConvexSweepQueryWorker
 24{
 25    private const int MaxGjkIterations = 32;
 26    private const int MaxConservativeAdvancementIterations = 32;
 127    private static readonly Fixed64 DistanceTolerance =
 128        Fixed64.FromFraction(1, 1_048_576);
 129    internal static readonly Fixed64 ContactTolerance = Fixed64.FromFraction(1, 4096);
 130    private static readonly SweepTriangleCandidateComparer SweepTriangleComparer = new();
 131    private static readonly FixedPointAnchor ZeroAnchor =
 132        new(
 133            Vector3d.Zero,
 134            FixedQuaternion.Identity,
 135            Vector3d.Zero);
 36
 2039937    private readonly SupportPoint[] _simplex = new SupportPoint[4];
 2039938    private readonly SwiftList<int> _triangleCandidates = new(16);
 2039939    private readonly SwiftList<SweepTriangleCandidate> _sweepTriangleCandidates = new(16);
 40    private readonly int _maxConservativeAdvancementIterations;
 41
 42    private LSCollider? _source;
 43    private ConvexShape _sourceShape;
 44    private bool _hasSource;
 45    private Vector3d _displacement;
 46    private Vector3d _outputDirection;
 47    private Vector3d _sweptSourceBoundsMin;
 48    private Vector3d _sweptSourceBoundsMax;
 49    private FixedPointAnchor _displacementAnchor;
 50    private FixedSegment _chord;
 51    private Fixed64 _length;
 52
 53    internal int LastMeshTriangleCandidateCount { get; private set; }
 54
 2039955    internal ConvexSweepQueryWorker(
 2039956        int maxConservativeAdvancementIterations = MaxConservativeAdvancementIterations) =>
 2039957        _maxConservativeAdvancementIterations = maxConservativeAdvancementIterations;
 58
 59    public void PrepareConvexMeshSource(LSMeshCollider source, Vector3d displacement)
 60    {
 2361        SwiftThrowHelper.ThrowIfNull(source, nameof(source));
 2362        ThrowIfConcaveSource(source);
 2263        Prepare(source, displacement);
 2264    }
 65
 66    public void PrepareCompoundSource(LSCompoundCollider source, Vector3d displacement)
 67    {
 1568        SwiftThrowHelper.ThrowIfNull(source, nameof(source));
 1569        Prepare(source, displacement);
 1570    }
 71
 72    public void PreparePrimitiveSource(LSCollider source, Vector3d displacement)
 73    {
 14874        SwiftThrowHelper.ThrowIfNull(source, nameof(source));
 14875        if (!ConvexColliderSupport.IsSupported(source))
 176            throw new NotSupportedException(
 177                $"Convex swept queries do not support {source.GetType().Name} sources.");
 78
 14779        Prepare(source, displacement);
 14780    }
 81
 82    public void PrepareCircleSlabSource(Vector3d center, Fixed64 radius, Fixed64 halfHeight, Vector3d displacement)
 83    {
 1884        SwiftThrowHelper.ThrowIfArgument(radius <= Fixed64.Zero, nameof(radius), "Circle-slab sweep radius must be great
 1785        SwiftThrowHelper.ThrowIfArgument(halfHeight <= Fixed64.Zero, nameof(halfHeight), "Circle-slab sweep half-height 
 1686        _source = null;
 1687        Prepare(ConvexShape.CreateCircleSlab(center, radius, halfHeight), displacement);
 1688    }
 89
 90    internal void PrepareSphereSource(
 91        Vector3d center,
 92        Fixed64 radius,
 93        Vector3d displacement)
 94    {
 1446795        SwiftThrowHelper.ThrowIfArgument(
 1446796            radius < Fixed64.Zero,
 1446797            nameof(radius),
 1446798            "Sphere sweep radius cannot be negative.");
 1446799        _source = null;
 14467100        Prepare(ConvexShape.CreateSphere(center, radius), displacement);
 14467101    }
 102
 103    public bool TrySweepPreparedSource(LSCollider target, out Physics3DHit hit)
 104    {
 196105        LastMeshTriangleCandidateCount = 0;
 196106        hit = default;
 196107        if (!_hasSource
 196108            || _length <= Fixed64.Epsilon
 196109            || !SweepBoundsUtility.OverlapsInclusive(_sweptSourceBoundsMin, _sweptSourceBoundsMax, target.BoundsMin, tar
 110        {
 40111            return false;
 112        }
 113
 156114        if (_source is LSCompoundCollider compound)
 12115            return TrySweepCompoundSource(compound, target, out hit);
 116
 144117        return TrySweepSourceShape(
 144118            _sourceShape,
 144119            target,
 144120            out hit,
 144121            out _);
 122    }
 123
 124    private void Prepare(LSCollider source, Vector3d displacement)
 125    {
 184126        _source = source;
 184127        Prepare(CreateColliderShape(source, Vector3d.Zero), displacement);
 184128    }
 129
 130    private void Prepare(ConvexShape sourceShape, Vector3d displacement)
 131    {
 14667132        _sourceShape = sourceShape;
 14667133        _displacement = displacement;
 14667134        _displacementAnchor = new FixedPointAnchor(
 14667135            Vector3d.Zero,
 14667136            FixedQuaternion.Identity,
 14667137            displacement);
 14667138        _chord = new FixedSegment(Vector3d.Zero, displacement);
 14667139        _hasSource =
 14667140            Vector3d.TryGetMagnitude(displacement, out _length)
 14667141            && sourceShape.CanTranslateCenter(displacement);
 14667142        if (!_hasSource)
 143        {
 5144            _outputDirection = Vector3d.Zero;
 5145            return;
 146        }
 147
 14662148        _outputDirection =
 14662149            _length <= Fixed64.Epsilon
 14662150                ? Vector3d.Zero
 14662151                : displacement.Normalized;
 14662152        sourceShape.GetSourceBounds(out Vector3d sourceMin, out Vector3d sourceMax);
 153        // Bounds are broad-phase clips, not canonical pose coordinates. A
 154        // valid center may have support beyond a scalar face.
 14662155        SweepBoundsUtility.CreateSweptBounds(
 14662156            sourceMin,
 14662157            sourceMax,
 14662158            displacement,
 14662159            ContactTolerance,
 14662160            out _sweptSourceBoundsMin,
 14662161            out _sweptSourceBoundsMax);
 14662162    }
 163
 164    private bool TrySweepCompoundSource(LSCompoundCollider source, LSCollider target, out Physics3DHit hit)
 165    {
 12166        hit = default;
 12167        bool found = false;
 12168        Fixed64 closestNumerator = Fixed64.MaxValue;
 12169        int closestPartIndex = int.MaxValue;
 170
 62171        for (int i = 0; i < source.PartCount; i++)
 172        {
 19173            LSCollider part = source.GetPartCollider(i);
 19174            if (!TrySweepSourceShape(
 19175                    CreateColliderShape(part, Vector3d.Zero),
 19176                    target,
 19177                    out Physics3DHit candidate,
 19178                    out Fixed64 candidateNumerator)
 19179                || !ComesBeforeReducerCandidate(
 19180                    candidateNumerator,
 19181                    i,
 19182                    found,
 19183                    closestNumerator,
 19184                    closestPartIndex))
 185            {
 186                continue;
 187            }
 188
 13189            hit = candidate;
 13190            closestNumerator = candidateNumerator;
 13191            closestPartIndex = i;
 13192            found = true;
 193        }
 194
 12195        return found;
 196    }
 197
 198    private bool TrySweepSourceShape(
 199        ConvexShape sourceShape,
 200        LSCollider target,
 201        out Physics3DHit hit,
 202        out Fixed64 hitNumerator)
 203    {
 169204        hit = default;
 169205        hitNumerator = default;
 206
 169207        if (!CanSweptSourceShapeReachTarget(sourceShape, target))
 6208            return false;
 209
 163210        if (target is LSCompoundCollider compound)
 211        {
 3212            return TrySweepTargetCompound(
 3213                sourceShape,
 3214                compound,
 3215                out hit,
 3216                out hitNumerator);
 217        }
 218
 160219        if (target is LSMeshCollider mesh && mesh.Mode == MeshColliderMode.Concave)
 220        {
 19221            return TrySweepConcaveMeshTarget(
 19222                sourceShape,
 19223                mesh,
 19224                out hit,
 19225                out hitNumerator);
 226        }
 227
 141228        return TrySweepConvexTarget(
 141229            sourceShape,
 141230            CreateColliderShape(target, Vector3d.Zero),
 141231            target,
 141232            out hit,
 141233            out hitNumerator);
 234    }
 235
 236    private bool TrySweepTargetCompound(
 237        ConvexShape sourceShape,
 238        LSCompoundCollider compound,
 239        out Physics3DHit hit,
 240        out Fixed64 hitNumerator)
 241    {
 3242        hit = default;
 3243        hitNumerator = default;
 3244        bool found = false;
 3245        Fixed64 closestNumerator = Fixed64.MaxValue;
 3246        int closestPartIndex = int.MaxValue;
 247
 18248        for (int i = 0; i < compound.PartCount; i++)
 249        {
 6250            LSCollider part = compound.GetPartCollider(i);
 6251            if (!TrySweepSourceShape(
 6252                    sourceShape,
 6253                    part,
 6254                    out Physics3DHit partHit,
 6255                    out Fixed64 partNumerator)
 6256                || !ComesBeforeReducerCandidate(
 6257                    partNumerator,
 6258                    i,
 6259                    found,
 6260                    closestNumerator,
 6261                    closestPartIndex))
 262            {
 263                continue;
 264            }
 265
 2266            hit = new Physics3DHit(compound, partHit.Anchor, partHit.Normal, partHit.Distance, partHit.Direction);
 2267            hitNumerator = partNumerator;
 2268            closestNumerator = partNumerator;
 2269            closestPartIndex = i;
 2270            found = true;
 271        }
 272
 3273        return found;
 274    }
 275
 276    private bool TrySweepConcaveMeshTarget(
 277        ConvexShape sourceShape,
 278        LSMeshCollider mesh,
 279        out Physics3DHit hit,
 280        out Fixed64 hitNumerator)
 281    {
 19282        hit = default;
 19283        hitNumerator = default;
 19284        bool found = false;
 19285        Fixed64 closestNumerator = Fixed64.MaxValue;
 19286        int closestTriangleIndex = int.MaxValue;
 287
 19288        CreateSweptSourceBoundsInMeshFrame(
 19289            sourceShape,
 19290            mesh,
 19291            out Vector3d min,
 19292            out Vector3d max);
 293
 19294        mesh.Mesh.GetTrianglesInLocalBounds(
 19295            new FixedBoundVolume(min, max),
 19296            _triangleCandidates);
 19297        LastMeshTriangleCandidateCount += _triangleCandidates.Count;
 19298        BuildOrderedSweepTriangleCandidates(sourceShape, mesh);
 299
 92300        for (int i = 0; i < _sweepTriangleCandidates.Count; i++)
 301        {
 28302            SweepTriangleCandidate sweepCandidate = _sweepTriangleCandidates[i];
 28303            if (RemainingSweepTrianglesCannotBeat(
 28304                sweepCandidate.LowerBoundNumerator,
 28305                found,
 28306                closestNumerator))
 307            {
 308                break;
 309            }
 310
 27311            int triangleIndex = sweepCandidate.TriangleIndex;
 27312            ConvexShape triangle = CreateTriangleShape(mesh, triangleIndex);
 27313            if (!TrySweepConvexTarget(
 27314                    sourceShape,
 27315                    triangle,
 27316                    mesh,
 27317                    out Physics3DHit candidate,
 27318                    out Fixed64 candidateNumerator)
 27319                || !ComesBeforeReducerCandidate(
 27320                    candidateNumerator,
 27321                    triangleIndex,
 27322                    found,
 27323                    closestNumerator,
 27324                    closestTriangleIndex))
 325            {
 326                continue;
 327            }
 328
 16329            hit = candidate;
 16330            hitNumerator = candidateNumerator;
 16331            closestNumerator = candidateNumerator;
 16332            closestTriangleIndex = triangleIndex;
 16333            found = true;
 334        }
 335
 19336        return found;
 337    }
 338
 339    private void BuildOrderedSweepTriangleCandidates(ConvexShape sourceShape, LSMeshCollider mesh)
 340    {
 19341        _sweepTriangleCandidates.FastClear();
 96342        for (int i = 0; i < _triangleCandidates.Count; i++)
 343        {
 29344            int triangleIndex = _triangleCandidates[i];
 29345            ConvexShape triangle = CreateTriangleShape(mesh, triangleIndex);
 29346            if (TryComputeSweepLowerBoundNumerator(
 29347                    sourceShape,
 29348                    triangle,
 29349                    out Fixed64 lowerBoundNumerator))
 350            {
 28351                _sweepTriangleCandidates.Add(
 28352                    new SweepTriangleCandidate(
 28353                        triangleIndex,
 28354                        lowerBoundNumerator));
 355            }
 356        }
 357
 19358        _sweepTriangleCandidates.SortInPlace(SweepTriangleComparer);
 19359    }
 360
 361    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 362    private bool TryComputeSweepLowerBoundNumerator(
 363        ConvexShape sourceShape,
 364        ConvexShape targetShape,
 365        out Fixed64 lowerBoundNumerator)
 366    {
 29367        sourceShape.GetSourceBounds(
 29368            out Vector3d sourceMin,
 29369            out Vector3d sourceMax);
 29370        targetShape.GetBounds(
 29371            out Vector3d targetMin,
 29372            out Vector3d targetMax);
 373
 29374        Vector3d padding = Vector3d.One * ContactTolerance;
 29375        sourceMin -= padding;
 29376        sourceMax += padding;
 29377        return TryComputeSweepLowerBoundNumerator(
 29378            sourceMin,
 29379            sourceMax,
 29380            targetMin,
 29381            targetMax,
 29382            _displacement,
 29383            _length,
 29384            out lowerBoundNumerator);
 385    }
 386
 387    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 388    internal static bool TryComputeSweepLowerBoundNumerator(
 389        Vector3d sourceMin,
 390        Vector3d sourceMax,
 391        Vector3d targetMin,
 392        Vector3d targetMax,
 393        Vector3d displacement,
 394        Fixed64 length,
 395        out Fixed64 lowerBoundNumerator)
 396    {
 34397        lowerBoundNumerator = Fixed64.Zero;
 34398        return IncludeAxisEntryNumerator(
 34399                sourceMin.X,
 34400                sourceMax.X,
 34401                targetMin.X,
 34402                targetMax.X,
 34403                displacement.X,
 34404                length,
 34405                ref lowerBoundNumerator)
 34406            && IncludeAxisEntryNumerator(
 34407                sourceMin.Y,
 34408                sourceMax.Y,
 34409                targetMin.Y,
 34410                targetMax.Y,
 34411                displacement.Y,
 34412                length,
 34413                ref lowerBoundNumerator)
 34414            && IncludeAxisEntryNumerator(
 34415                sourceMin.Z,
 34416                sourceMax.Z,
 34417                targetMin.Z,
 34418                targetMax.Z,
 34419                displacement.Z,
 34420                length,
 34421                ref lowerBoundNumerator);
 422    }
 423
 424    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 425    private static bool IncludeAxisEntryNumerator(
 426        Fixed64 sourceMin,
 427        Fixed64 sourceMax,
 428        Fixed64 targetMin,
 429        Fixed64 targetMax,
 430        Fixed64 displacement,
 431        Fixed64 length,
 432        ref Fixed64 entryNumerator)
 433    {
 98434        if (sourceMax >= targetMin
 98435            && sourceMin <= targetMax)
 436        {
 52437            return true;
 438        }
 439
 440        Fixed64 gap;
 441        Fixed64 span;
 46442        if (sourceMax < targetMin
 46443            && displacement > Fixed64.Zero)
 444        {
 445            // Saturation is safe here: an unrepresentable positive gap is
 446            // necessarily farther than the representable sweep span.
 41447            gap = targetMin - sourceMax;
 41448            span = displacement;
 449        }
 5450        else if (sourceMin > targetMax
 5451            && displacement < Fixed64.Zero)
 452        {
 3453            gap = sourceMin - targetMax;
 454            // Prepare admits only representable chord magnitudes, so no
 455            // component can be Fixed64.MinValue here.
 3456            span = -displacement;
 457        }
 458        else
 459        {
 2460            return false;
 461        }
 462
 44463        if (gap > span)
 1464            return false;
 465
 466        // With 0 <= gap <= span, the fused result is bounded by the already
 467        // representable chord length.
 43468        _ = Fixed64.TryMultiplyDivide(
 43469            length,
 43470            gap,
 43471            span,
 43472            out Fixed64 axisNumerator);
 473
 474        // Fused conversion keeps u = numerator / chord length exact until the
 475        // final Q32.32 rounding. One raw unit makes that rounded result a
 476        // conservative lower bound without moving an underflowed zero.
 43477        axisNumerator = FixedMath.Max(
 43478            Fixed64.Zero,
 43479            axisNumerator - Fixed64.Epsilon);
 480
 43481        if (axisNumerator > entryNumerator)
 30482            entryNumerator = axisNumerator;
 43483        return true;
 484    }
 485
 486    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 487    internal static bool RemainingSweepTrianglesCannotBeat(
 488        Fixed64 candidateLowerBoundNumerator,
 489        bool found,
 490        Fixed64 closestNumerator)
 491    {
 32492        if (!found)
 19493            return false;
 494
 13495        return candidateLowerBoundNumerator
 13496            > closestNumerator + Fixed64.Epsilon;
 497    }
 498
 499    private bool TrySweepConvexTarget(
 500        ConvexShape sourceShape,
 501        ConvexShape targetShape,
 502        LSCollider targetCollider,
 503        out Physics3DHit hit,
 504        out Fixed64 hitNumerator)
 505    {
 168506        hit = default;
 168507        hitNumerator = default;
 508        // u is retained as travelNumerator / _length. Keeping the common
 509        // denominator exact preserves small chord components and physical
 510        // distance resolution without exposing wide arithmetic.
 168511        Fixed64 travelNumerator = Fixed64.Zero;
 168512        Vector3d normal = Vector3d.Zero;
 168513        GjkResult result = default;
 514
 622515        for (int i = 0; i < _maxConservativeAdvancementIterations; i++)
 516        {
 308517            ConvexShape movedSource =
 308518                sourceShape.WithSourceOffset(
 308519                    GetChordOffset(travelNumerator));
 308520            result = ComputeDistance(movedSource, targetShape);
 308521            if (result.Intersects || result.Distance <= ContactTolerance)
 522            {
 149523                ContactAnchor anchor = ResolveHitAnchor(
 149524                    targetShape,
 149525                    targetCollider,
 149526                    movedSource,
 149527                    result,
 149528                    out Vector3d point,
 149529                    out bool hasMaterializedPoint,
 149530                    out bool hasRefinedSurfaceNormal);
 149531                Vector3d hitNormal = ResolveHitNormal(
 149532                    targetShape,
 149533                    targetCollider,
 149534                    point,
 149535                    result.Normal,
 149536                    normal,
 149537                    hasRefinedSurfaceNormal,
 149538                    hasMaterializedPoint);
 539
 149540                hit = new Physics3DHit(
 149541                    targetCollider,
 149542                    anchor,
 149543                    hitNormal,
 149544                    travelNumerator,
 149545                    _outputDirection);
 149546                hitNumerator = travelNumerator;
 149547                return true;
 548            }
 549
 159550            normal = result.Normal;
 551            // Projecting a representable chord onto a unit normal is bounded
 552            // by the admitted chord length.
 159553            _ = _displacementAnchor.TryGetProjectedOffsetFrom(
 159554                ZeroAnchor,
 159555                -normal,
 159556                out Fixed64 closingPerFraction);
 159557            if (closingPerFraction <= Fixed64.Epsilon)
 558            {
 9559                return TryResolveEndpointBracket(
 9560                    sourceShape,
 9561                    targetShape,
 9562                    targetCollider,
 9563                    travelNumerator,
 9564                    out hit,
 9565                    out hitNumerator);
 566            }
 150567            Fixed64 boundedDistance =
 150568                FixedMath.Min(
 150569                    result.Distance,
 150570                    closingPerFraction);
 571            // Capping the advancement ratio at one preserves the endpoint
 572            // decision while keeping the fused result inside the chord length.
 150573            _ = Fixed64.TryMultiplyDivide(
 150574                boundedDistance,
 150575                _length,
 150576                closingPerFraction,
 150577                out Fixed64 stepNumerator);
 150578            Fixed64 nextTravelNumerator =
 150579                travelNumerator + stepNumerator;
 150580            if (result.Distance > closingPerFraction
 150581                || nextTravelNumerator > _length)
 582            {
 7583                ConvexShape endpointSource = sourceShape.WithSourceOffset(_displacement);
 7584                GjkResult endpointResult = ComputeDistance(endpointSource, targetShape);
 7585                if (!endpointResult.Intersects && endpointResult.Distance > ContactTolerance)
 6586                    return false;
 587
 1588                ContactAnchor anchor = ResolveHitAnchor(
 1589                    targetShape,
 1590                    targetCollider,
 1591                    endpointSource,
 1592                    endpointResult,
 1593                    out Vector3d point,
 1594                    out bool hasMaterializedPoint,
 1595                    out bool hasRefinedSurfaceNormal);
 1596                Vector3d hitNormal = ResolveHitNormal(
 1597                    targetShape,
 1598                    targetCollider,
 1599                    point,
 1600                    endpointResult.Normal,
 1601                    normal,
 1602                    hasRefinedSurfaceNormal,
 1603                    hasMaterializedPoint);
 1604                hit = new Physics3DHit(
 1605                    targetCollider,
 1606                    anchor,
 1607                    hitNormal,
 1608                    _length,
 1609                    _outputDirection);
 1610                hitNumerator = _length;
 1611                return true;
 612            }
 613
 143614            travelNumerator = nextTravelNumerator;
 615        }
 616
 3617        return TryResolveEndpointBracket(
 3618            sourceShape,
 3619            targetShape,
 3620            targetCollider,
 3621            travelNumerator,
 3622            out hit,
 3623            out hitNumerator);
 624    }
 625
 626    private bool TryResolveEndpointBracket(
 627        ConvexShape sourceShape,
 628        ConvexShape targetShape,
 629        LSCollider targetCollider,
 630        Fixed64 lowerNumerator,
 631        out Physics3DHit hit,
 632        out Fixed64 hitNumerator)
 633    {
 12634        hit = default;
 12635        hitNumerator = default;
 12636        Fixed64 upperNumerator = _length;
 12637        ConvexShape upperSource =
 12638            sourceShape.WithSourceOffset(_displacement);
 12639        GjkResult upperResult =
 12640            ComputeDistance(upperSource, targetShape);
 12641        if (!upperResult.Intersects
 12642            && upperResult.Distance > ContactTolerance)
 643        {
 10644            return false;
 645        }
 646
 2647        for (int iteration = 0;
 4648            iteration < _maxConservativeAdvancementIterations
 4649            && upperNumerator - lowerNumerator > DistanceTolerance;
 2650            iteration++)
 651        {
 2652            Fixed64 middleNumerator =
 2653                FixedMath.Midpoint(
 2654                    lowerNumerator,
 2655                    upperNumerator);
 2656            ConvexShape middleSource =
 2657                sourceShape.WithSourceOffset(
 2658                    GetChordOffset(middleNumerator));
 2659            GjkResult middleResult =
 2660                ComputeDistance(middleSource, targetShape);
 2661            if (middleResult.Intersects
 2662                || middleResult.Distance <= ContactTolerance)
 663            {
 1664                upperNumerator = middleNumerator;
 1665                upperSource = middleSource;
 1666                upperResult = middleResult;
 667            }
 668            else
 669            {
 1670                lowerNumerator = middleNumerator;
 671            }
 672        }
 673
 2674        ContactAnchor anchor = ResolveHitAnchor(
 2675            targetShape,
 2676            targetCollider,
 2677            upperSource,
 2678            upperResult,
 2679            out Vector3d point,
 2680            out bool hasMaterializedPoint,
 2681            out bool hasRefinedSurfaceNormal);
 2682        Vector3d hitNormal = ResolveHitNormal(
 2683            targetShape,
 2684            targetCollider,
 2685            point,
 2686            upperResult.Normal,
 2687            Vector3d.Zero,
 2688            hasRefinedSurfaceNormal,
 2689            hasMaterializedPoint);
 2690        hit = new Physics3DHit(
 2691            targetCollider,
 2692            anchor,
 2693            hitNormal,
 2694            upperNumerator,
 2695            _outputDirection);
 2696        hitNumerator = upperNumerator;
 2697        return true;
 698    }
 699
 700    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 701    private Vector3d GetChordOffset(Fixed64 numerator) =>
 310702        _chord.GetPointAtDistance(
 310703            numerator,
 310704            _length);
 705
 706    private ContactAnchor ResolveHitAnchor(
 707        ConvexShape targetShape,
 708        LSCollider targetCollider,
 709        ConvexShape movedSource,
 710        GjkResult result,
 711        out Vector3d point,
 712        out bool hasMaterializedPoint,
 713        out bool hasRefinedSurfaceNormal)
 714    {
 152715        hasMaterializedPoint = true;
 152716        hasRefinedSurfaceNormal = false;
 152717        if (targetCollider is LSSphereCollider
 152718            && movedSource.TryGetClosestPointOnSurface(
 152719                targetCollider.Center,
 152720                out Vector3d sourcePoint)
 152721            && Vector3d.TrySubtract(
 152722                sourcePoint,
 152723                targetCollider.Center,
 152724                out Vector3d centerToSource)
 152725            && centerToSource.MagnitudeSquared > Fixed64.Epsilon)
 726        {
 727            // A sphere's closest pair is defined by its center and the closest
 728            // source feature. Refining that feature removes arbitrary support
 729            // tie bias without changing the conservative TOI.
 55730            FixedPointAnchor sphereAnchor =
 55731                ConvexColliderSupport.GetSupportAnchor(
 55732                    targetCollider,
 55733                    centerToSource,
 55734                    Vector3d.Zero);
 55735            hasMaterializedPoint = sphereAnchor.TryGetPoint(out point);
 55736            hasRefinedSurfaceNormal = hasMaterializedPoint;
 55737            return new ContactAnchor(sphereAnchor);
 738        }
 739
 97740        FixedPointAnchor movedSourceCenter = movedSource.GetCenterAnchor();
 97741        FixedPointAnchor targetCenter = targetShape.GetCenterAnchor();
 97742        if (movedSourceCenter.TryGetOffsetFrom(
 97743                targetCenter,
 97744                out Vector3d centerDifference)
 97745            && centerDifference.MagnitudeSquared <= Fixed64.Epsilon)
 746        {
 7747            FixedPointAnchor fallbackAnchor =
 7748                targetShape.GetFallbackSurfaceAnchor(-_displacement);
 7749            hasMaterializedPoint = fallbackAnchor.TryGetPoint(out point);
 7750            return new ContactAnchor(fallbackAnchor);
 751        }
 752
 753        // GJK's target witness identifies the feature that stopped the sweep.
 754        // Center-to-center projection can select an unrelated feature for long,
 755        // offset shapes and therefore produce a non-physical response normal.
 90756        if (!result.PointB.TryGetPoint(out point))
 757        {
 1758            point = default;
 1759            hasMaterializedPoint = false;
 1760            return new ContactAnchor(result.PointB);
 761        }
 762
 89763        return new ContactAnchor(result.PointB);
 764    }
 765
 766    private Vector3d ResolveHitNormal(
 767        ConvexShape targetShape,
 768        LSCollider targetCollider,
 769        Vector3d point,
 770        Vector3d resultNormal,
 771        Vector3d fallbackNormal,
 772        bool hasRefinedSurfaceNormal,
 773        bool hasMaterializedPoint)
 774    {
 152775        Vector3d planarNormal = Vector3d.Zero;
 152776        if (hasMaterializedPoint)
 151777            targetShape.TryGetPlanarSurfaceNormal(point, out planarNormal);
 152778        return ConvexSweepHitPolicy.ResolveHitNormal(
 152779            targetCollider,
 152780            point,
 152781            resultNormal,
 152782            fallbackNormal,
 152783            _displacement,
 152784            planarNormal,
 152785            hasRefinedSurfaceNormal,
 152786            hasMaterializedPoint);
 787    }
 788
 789    private static ConvexShape CreateColliderShape(LSCollider collider, Vector3d offset)
 790    {
 344791        return new ConvexShape(collider, offset);
 792    }
 793
 794    private static ConvexShape CreateTriangleShape(LSMeshCollider mesh, int triangleIndex)
 795    {
 56796        mesh.Mesh.GetLocalTriangleVertices(
 56797            triangleIndex,
 56798            out Vector3d first,
 56799            out Vector3d second,
 56800            out Vector3d third);
 56801        return new ConvexShape(mesh, triangleIndex, first, second, third);
 802    }
 803
 804    private static void ThrowIfConcaveSource(LSMeshCollider source)
 805    {
 23806        if (source.Mode == MeshColliderMode.Concave)
 1807            throw CreateConcaveSourceException(source);
 22808    }
 809
 810    private void CreateSweptSourceBoundsInMeshFrame(
 811        ConvexShape sourceShape,
 812        LSMeshCollider mesh,
 813        out Vector3d min,
 814        out Vector3d max)
 815    {
 816        // Concave-target dispatch receives only the source's committed shape;
 817        // iterative chord offsets are introduced after broad-phase collection.
 19818        _ = sourceShape.TryGetBoundsRelativeTo(
 19819            mesh.Mesh.Origin,
 19820            mesh.Mesh.Rotation,
 19821            out Vector3d sourceMin,
 19822            out Vector3d sourceMax);
 823
 824        // Prepare admitted the chord magnitude, so a unit rotation preserves
 825        // representability.
 19826        _ = mesh.Mesh.Rotation.Inverse().TryRotate(
 19827            _displacement,
 19828            out Vector3d localDisplacement);
 19829        SweepBoundsUtility.CreateSweptBounds(
 19830            sourceMin,
 19831            sourceMax,
 19832            localDisplacement,
 19833            ContactTolerance,
 19834            out min,
 19835            out max);
 19836    }
 837
 838    private bool CanSweptSourceShapeReachTarget(ConvexShape sourceShape, LSCollider target)
 839    {
 169840        if (!sourceShape.CanTranslateCenter(_displacement))
 1841            return false;
 842
 168843        sourceShape.GetSourceBounds(out Vector3d sourceMin, out Vector3d sourceMax);
 168844        SweepBoundsUtility.CreateSweptBounds(
 168845            sourceMin,
 168846            sourceMax,
 168847            _displacement,
 168848            ContactTolerance,
 168849            out Vector3d min,
 168850            out Vector3d max);
 168851        return SweepBoundsUtility.OverlapsInclusive(
 168852            min,
 168853            max,
 168854            target.BoundsMin,
 168855            target.BoundsMax);
 856    }
 857
 858    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 859    private static bool ComesBeforeReducerCandidate(
 860        Fixed64 hitNumerator,
 861        int candidateOrdinal,
 862        bool found,
 863        Fixed64 closestNumerator,
 864        int closestOrdinal)
 865    {
 44866        if (!found)
 28867            return true;
 868
 16869        int numeratorCompare =
 16870            hitNumerator.CompareTo(closestNumerator);
 16871        if (numeratorCompare != 0)
 7872            return numeratorCompare < 0;
 873
 9874        return candidateOrdinal < closestOrdinal;
 875    }
 876
 877    private static ArgumentException CreateConcaveSourceException(LSMeshCollider source) =>
 1878        new("Concave mesh sources are not supported by swept query APIs. Use an LSCompoundCollider built from authored c
 879}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/3D/Sweeps/ConvexSweepQueryWorker.Gjk.cs

#LineLine coverage
 1//=======================================================================
 2// ConvexSweepQueryWorker.Gjk.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2026-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using FixedMathSharp;
 9using FixedMathSharp.Geometry;
 10
 11namespace Gravitas.Queries;
 12
 13internal sealed partial class ConvexSweepQueryWorker
 14{
 15    private GjkResult ComputeDistance(ConvexShape sourceShape, ConvexShape targetShape)
 16    {
 17        const int workingShift = 2;
 18        // Quartering both endpoints bounds every two-term Minkowski component
 19        // below half the scalar domain. Any convex simplex combination then
 20        // has a representable 3D magnitude without a lossy retry.
 32921        Fixed64 workingScale = GjkSimplexScale.GetCoordinateScale(workingShift);
 32922        Fixed64 workingDistanceTolerance = DistanceTolerance * workingScale;
 32923        int simplexCount = 0;
 32924        FixedPointAnchor sourceCenter = sourceShape.GetCenterAnchor();
 32925        FixedPointAnchor targetCenter = targetShape.GetCenterAnchor();
 26        // The shift was selected from bounds containing both centers, so this
 27        // scaled difference is representable by construction.
 32928        _ = targetCenter.TryGetScaledOffsetFrom(
 32929            sourceCenter,
 32930            workingScale,
 32931            out Vector3d direction);
 32932        if (direction == Vector3d.Zero)
 33        {
 1134            if (sourceShape.ContainsCenter && targetShape.ContainsCenter)
 35            {
 936                return GjkResult.CreateIntersection(
 937                    sourceCenter,
 938                    targetCenter);
 39            }
 40
 241            direction = Vector3d.Right;
 42        }
 43
 32044        bool hasPreviousDistance = false;
 32045        Fixed64 previousDistance = Fixed64.Zero;
 32046        ClosestSimplexResult closest = default;
 32047        Fixed64 workingDistance = Fixed64.MaxValue;
 48
 289249        for (int i = 0; i < MaxGjkIterations; i++)
 50        {
 144651            SupportPoint support = CreateSupportPoint(
 144652                sourceShape,
 144653                targetShape,
 144654                direction,
 144655                workingScale);
 144656            if (ContainsSupportPoint(_simplex, simplexCount, support.Point))
 57                break;
 58
 136359            ClosestSimplexResult previousClosest = closest;
 136360            _simplex[simplexCount++] = support;
 136361            closest = SolveClosestSimplex(_simplex, ref simplexCount, workingScale);
 136362            _ = Vector3d.TryGetMagnitude(closest.Point, out workingDistance);
 136363            if (closest.Intersects)
 64            {
 65                // Tetrahedron entry follows a valid one-to-three point simplex.
 66                // Preserve that same-pose closest pair as the deterministic
 67                // surface witness for the intersection result.
 2768                return GjkResult.CreateIntersection(previousClosest.PointA, previousClosest.PointB);
 69            }
 70
 133671            if (workingDistance <= workingDistanceTolerance)
 10172                return GjkResult.CreateIntersection(closest.PointA, closest.PointB);
 73
 123574            if (hasPreviousDistance
 123575                && previousDistance - workingDistance <= Fixed64.Epsilon)
 76            {
 77                break;
 78            }
 79
 112680            hasPreviousDistance = true;
 112681            previousDistance = workingDistance;
 112682            direction = -closest.Point;
 83        }
 84
 19285        Fixed64 distance = GjkSimplexScale.RestoreDistance(workingDistance, workingShift);
 19286        Vector3d normal = closest.Point.Normalized;
 19287        return new GjkResult(false, distance, closest.PointA, closest.PointB, normal);
 88    }
 89
 90    private static SupportPoint CreateSupportPoint(
 91        ConvexShape sourceShape,
 92        ConvexShape targetShape,
 93        Vector3d direction,
 94        Fixed64 workingScale)
 95    {
 144696        Vector3d supportDirection = direction.Normalized;
 144697        FixedPointAnchor pointA =
 144698            sourceShape.GetSupportAnchor(supportDirection);
 144699        FixedPointAnchor pointB =
 1446100            targetShape.GetSupportAnchor(-supportDirection);
 101        // The same bounds-selected shift covers every enclosed support pair.
 1446102        _ = pointA.TryGetScaledOffsetFrom(
 1446103            pointB,
 1446104            workingScale,
 1446105            out Vector3d difference);
 1446106        return new SupportPoint(pointA, pointB, difference);
 107    }
 108
 109    private static ClosestSimplexResult SolveClosestSimplex(
 110        SupportPoint[] simplex,
 111        ref int count,
 112        Fixed64 workingScale)
 113    {
 1363114        if (count == 1)
 320115            return ClosestSimplexResult.FromWeights(simplex, 1, Fixed64.One, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero);
 116
 1043117        if (count == 2)
 324118            return ReduceSegment(simplex, ref count, workingScale);
 119
 719120        if (count == 3)
 617121            return ReduceTriangle(simplex, ref count);
 122
 102123        return ReduceTetrahedron(simplex, ref count, workingScale);
 124    }
 125}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/3D/Sweeps/ConvexSweepQueryWorker.Simplex.cs

#LineLine coverage
 1//=======================================================================
 2// ConvexSweepQueryWorker.Simplex.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 System;
 11
 12namespace Gravitas.Queries;
 13
 14internal sealed partial class ConvexSweepQueryWorker
 15{
 16    private static ClosestSimplexResult ReduceSegment(
 17        SupportPoint[] simplex,
 18        ref int count,
 19        Fixed64 workingScale)
 20    {
 32421        SupportPoint a = simplex[0];
 32422        SupportPoint b = simplex[1];
 32423        Span<Vector3d> scaled = stackalloc Vector3d[2];
 32424        scaled[0] = a.Point;
 32425        scaled[1] = b.Point;
 32426        Fixed64 productScale = GjkSimplexScale.ScaleForProducts(scaled);
 32427        Vector3d scaledA = scaled[0];
 32428        Vector3d ab = scaled[1] - scaledA;
 32429        Fixed64 denominator = ab.MagnitudeSquared;
 32430        Fixed64 workingScaleSqr = workingScale * workingScale;
 32431        Fixed64 productScaleSqr = productScale * productScale;
 32432        Fixed64 denominatorTolerance = Fixed64.Epsilon * workingScaleSqr * productScaleSqr;
 32433        Fixed64 t = denominator <= denominatorTolerance
 32434            ? Fixed64.Zero
 32435            : FixedMath.Clamp(-Vector3d.Dot(scaledA, ab) / denominator, Fixed64.Zero, Fixed64.One);
 36
 32437        if (t <= Fixed64.Epsilon)
 38        {
 2439            simplex[0] = a;
 2440            count = 1;
 2441            return ClosestSimplexResult.FromWeights(simplex, count, Fixed64.One, Fixed64.Zero, Fixed64.Zero, Fixed64.Zer
 42        }
 43
 30044        if (t >= Fixed64.One - Fixed64.Epsilon)
 45        {
 4846            simplex[0] = b;
 4847            count = 1;
 4848            return ClosestSimplexResult.FromWeights(simplex, count, Fixed64.One, Fixed64.Zero, Fixed64.Zero, Fixed64.Zer
 49        }
 50
 25251        simplex[0] = a;
 25252        simplex[1] = b;
 25253        count = 2;
 25254        return ClosestSimplexResult.FromWeights(simplex, count, Fixed64.One - t, t, Fixed64.Zero, Fixed64.Zero);
 55    }
 56
 57    private static ClosestSimplexResult ReduceTriangle(SupportPoint[] simplex, ref int count)
 58    {
 61759        TriangleWeights weights = ClosestPointOnTriangleToOrigin(simplex[0].Point, simplex[1].Point, simplex[2].Point);
 61760        return ReduceByWeights(simplex, ref count, weights.A, weights.B, weights.C, Fixed64.Zero);
 61    }
 62
 63    private static ClosestSimplexResult ReduceTetrahedron(
 64        SupportPoint[] simplex,
 65        ref int count,
 66        Fixed64 workingScale)
 67    {
 10268        if (IsOriginInsideTetrahedron(
 10269                simplex[0].Point,
 10270                simplex[1].Point,
 10271                simplex[2].Point,
 10272                simplex[3].Point,
 10273                workingScale))
 74        {
 2775            count = 4;
 2776            return ClosestSimplexResult.Intersection;
 77        }
 78
 7579        ClosestSimplexResult best = default;
 7580        bool hasBest = false;
 7581        Span<int> bestIndices = stackalloc int[3];
 7582        Span<Fixed64> bestWeights = stackalloc Fixed64[3];
 7583        Span<int> face = stackalloc int[3];
 7584        Span<Fixed64> weights = stackalloc Fixed64[3];
 85
 7586        EvaluateFace(simplex, 0, 1, 2, ref best, ref hasBest, bestIndices, bestWeights, face, weights);
 7587        EvaluateFace(simplex, 0, 3, 1, ref best, ref hasBest, bestIndices, bestWeights, face, weights);
 7588        EvaluateFace(simplex, 0, 2, 3, ref best, ref hasBest, bestIndices, bestWeights, face, weights);
 7589        EvaluateFace(simplex, 1, 3, 2, ref best, ref hasBest, bestIndices, bestWeights, face, weights);
 90
 7591        SupportPoint first = simplex[bestIndices[0]];
 7592        SupportPoint second = simplex[bestIndices[1]];
 7593        SupportPoint third = simplex[bestIndices[2]];
 7594        simplex[0] = first;
 7595        simplex[1] = second;
 7596        simplex[2] = third;
 7597        count = 3;
 7598        return ClosestSimplexResult.FromWeights(simplex, count, bestWeights[0], bestWeights[1], bestWeights[2], Fixed64.
 99    }
 100
 101    private static void EvaluateFace(
 102        SupportPoint[] simplex,
 103        int first,
 104        int second,
 105        int third,
 106        ref ClosestSimplexResult best,
 107        ref bool hasBest,
 108        Span<int> bestIndices,
 109        Span<Fixed64> bestWeights,
 110        Span<int> face,
 111        Span<Fixed64> weights)
 112    {
 300113        face[0] = first;
 300114        face[1] = second;
 300115        face[2] = third;
 300116        TriangleWeights triangleWeights = ClosestPointOnTriangleToOrigin(
 300117            simplex[first].Point,
 300118            simplex[second].Point,
 300119            simplex[third].Point);
 300120        weights[0] = triangleWeights.A;
 300121        weights[1] = triangleWeights.B;
 300122        weights[2] = triangleWeights.C;
 300123        ClosestSimplexResult candidate = ClosestSimplexResult.FromIndexedWeights(simplex, face, weights);
 300124        if (hasBest && !IsCloser(
 300125                candidate.DistanceSqr,
 300126                candidate.Point,
 300127                best.DistanceSqr,
 300128                best.Point))
 165129            return;
 130
 135131        best = candidate;
 135132        hasBest = true;
 135133        bestIndices[0] = first;
 135134        bestIndices[1] = second;
 135135        bestIndices[2] = third;
 135136        bestWeights[0] = weights[0];
 135137        bestWeights[1] = weights[1];
 135138        bestWeights[2] = weights[2];
 135139    }
 140
 141    internal static bool IsCloser(
 142        Fixed64 candidateDistanceSqr,
 143        Vector3d candidatePoint,
 144        Fixed64 bestDistanceSqr,
 145        Vector3d bestPoint)
 146    {
 235147        if (candidateDistanceSqr != bestDistanceSqr || candidateDistanceSqr != Fixed64.MaxValue)
 222148            return candidateDistanceSqr < bestDistanceSqr;
 149
 13150        return Vector3d.CompareMagnitudeSquared(candidatePoint, bestPoint) < 0;
 151    }
 152
 153    private static ClosestSimplexResult ReduceByWeights(
 154        SupportPoint[] simplex,
 155        ref int count,
 156        Fixed64 firstWeight,
 157        Fixed64 secondWeight,
 158        Fixed64 thirdWeight,
 159        Fixed64 fourthWeight)
 160    {
 617161        Span<SupportPoint> reduced = stackalloc SupportPoint[4];
 617162        Span<Fixed64> weights = stackalloc Fixed64[4];
 617163        int reducedCount = 0;
 617164        AddWeightedPoint(simplex[0], firstWeight, reduced, weights, ref reducedCount);
 617165        AddWeightedPoint(simplex[1], secondWeight, reduced, weights, ref reducedCount);
 617166        AddWeightedPoint(simplex[2], thirdWeight, reduced, weights, ref reducedCount);
 617167        AddWeightedPoint(simplex[3], fourthWeight, reduced, weights, ref reducedCount);
 168
 3828169        for (int i = 0; i < reducedCount; i++)
 1297170            simplex[i] = reduced[i];
 171
 617172        count = reducedCount;
 617173        return ClosestSimplexResult.FromSpanWeights(reduced, weights, reducedCount);
 174    }
 175
 176    private static void AddWeightedPoint(
 177        SupportPoint point,
 178        Fixed64 weight,
 179        Span<SupportPoint> reduced,
 180        Span<Fixed64> weights,
 181        ref int count)
 182    {
 2468183        if (weight <= Fixed64.Epsilon)
 1171184            return;
 185
 1297186        reduced[count] = point;
 1297187        weights[count] = weight;
 1297188        count++;
 1297189    }
 190
 191    /// <summary>
 192    /// Returns barycentric weights for the point on a triangle closest to the origin.
 193    /// </summary>
 194    internal static TriangleWeights ClosestPointOnTriangleToOrigin(Vector3d a, Vector3d b, Vector3d c)
 195    {
 932196        Span<Vector3d> scaled = stackalloc Vector3d[3];
 932197        scaled[0] = a;
 932198        scaled[1] = b;
 932199        scaled[2] = c;
 932200        GjkSimplexScale.ScaleForProducts(scaled);
 932201        a = scaled[0];
 932202        b = scaled[1];
 932203        c = scaled[2];
 204
 932205        var triangle = new FixedTriangle(a, b, c);
 932206        Vector3d closest = triangle.ClosestPoint(Vector3d.Zero);
 932207        if (!triangle.TryGetProjectedBarycentricWeights(
 932208                closest,
 932209                out _,
 932210                out Fixed64 weightB,
 932211                out Fixed64 weightC))
 212        {
 330213            return ClosestPointOnDegenerateTriangleToOrigin(a, b, c);
 214        }
 215
 602216        weightB = FixedMath.Clamp(
 602217            weightB,
 602218            Fixed64.Zero,
 602219            Fixed64.One);
 602220        weightC = FixedMath.Clamp(
 602221            weightC,
 602222            Fixed64.Zero,
 602223            Fixed64.One - weightB);
 602224        return new TriangleWeights(
 602225            Fixed64.One - weightB - weightC,
 602226            weightB,
 602227            weightC);
 228    }
 229
 230    private static TriangleWeights ClosestPointOnDegenerateTriangleToOrigin(
 231        Vector3d first,
 232        Vector3d second,
 233        Vector3d third)
 234    {
 330235        GetClosestSegmentWeights(
 330236            first,
 330237            second,
 330238            out Fixed64 firstWeight,
 330239            out Fixed64 secondWeight,
 330240            out Vector3d bestPoint);
 330241        TriangleWeights best = new(
 330242            firstWeight,
 330243            secondWeight,
 330244            Fixed64.Zero);
 245
 330246        GetClosestSegmentWeights(
 330247            first,
 330248            third,
 330249            out firstWeight,
 330250            out Fixed64 thirdWeight,
 330251            out Vector3d candidatePoint);
 330252        if (Vector3d.CompareMagnitudeSquared(
 330253                candidatePoint,
 330254                bestPoint) < 0)
 255        {
 230256            bestPoint = candidatePoint;
 230257            best = new TriangleWeights(
 230258                firstWeight,
 230259                Fixed64.Zero,
 230260                thirdWeight);
 261        }
 262
 330263        GetClosestSegmentWeights(
 330264            second,
 330265            third,
 330266            out secondWeight,
 330267            out thirdWeight,
 330268            out candidatePoint);
 330269        if (Vector3d.CompareMagnitudeSquared(
 330270                candidatePoint,
 330271                bestPoint) < 0)
 272        {
 95273            best = new TriangleWeights(
 95274                Fixed64.Zero,
 95275                secondWeight,
 95276                thirdWeight);
 277        }
 278
 330279        return best;
 280    }
 281
 282    private static void GetClosestSegmentWeights(
 283        Vector3d start,
 284        Vector3d end,
 285        out Fixed64 startWeight,
 286        out Fixed64 endWeight,
 287        out Vector3d point)
 288    {
 990289        Vector3d delta = end - start;
 990290        Fixed64 denominator = Vector3d.Dot(delta, delta);
 990291        Fixed64 numerator = -Vector3d.Dot(start, delta);
 990292        if (denominator == Fixed64.Zero || numerator <= Fixed64.Zero)
 293        {
 52294            startWeight = Fixed64.One;
 52295            endWeight = Fixed64.Zero;
 52296            point = start;
 52297            return;
 298        }
 938299        if (numerator >= denominator)
 300        {
 301301            startWeight = Fixed64.Zero;
 301302            endWeight = Fixed64.One;
 301303            point = end;
 301304            return;
 305        }
 306
 637307        endWeight = numerator / denominator;
 637308        startWeight = Fixed64.One - endWeight;
 637309        point = start * startWeight + end * endWeight;
 637310    }
 311
 312    private static bool IsOriginInsideTetrahedron(
 313        Vector3d a,
 314        Vector3d b,
 315        Vector3d c,
 316        Vector3d d,
 317        Fixed64 workingScale)
 318    {
 102319        Span<Vector3d> scaled = stackalloc Vector3d[4];
 102320        scaled[0] = a;
 102321        scaled[1] = b;
 102322        scaled[2] = c;
 102323        scaled[3] = d;
 102324        Fixed64 productScale = GjkSimplexScale.ScaleForProducts(scaled);
 102325        a = scaled[0];
 102326        b = scaled[1];
 102327        c = scaled[2];
 102328        d = scaled[3];
 329
 102330        Fixed64 productScaleSqr = productScale * productScale;
 102331        Fixed64 productScaleCubed = productScaleSqr * productScale;
 102332        Fixed64 workingScaleSqr = workingScale * workingScale;
 102333        Fixed64 workingScaleCubed = workingScaleSqr * workingScale;
 102334        Fixed64 volumeTolerance = DistanceTolerance * workingScaleCubed * productScaleCubed;
 102335        if (SignedTetrahedronVolume6(a, b, c, d).Abs() <= volumeTolerance)
 19336            return false;
 337
 83338        Fixed64 workingScaleSixth = workingScaleCubed * workingScaleCubed;
 83339        Fixed64 sideTolerance =
 83340            DistanceTolerance * workingScaleSixth * productScaleCubed * productScaleCubed;
 83341        return IsSameSideOfFace(Vector3d.Zero, d, a, b, c, sideTolerance)
 83342            && IsSameSideOfFace(Vector3d.Zero, c, a, d, b, sideTolerance)
 83343            && IsSameSideOfFace(Vector3d.Zero, b, a, c, d, sideTolerance)
 83344            && IsSameSideOfFace(Vector3d.Zero, a, b, d, c, sideTolerance);
 345    }
 346
 347    private static bool IsSameSideOfFace(
 348        Vector3d point,
 349        Vector3d opposite,
 350        Vector3d a,
 351        Vector3d b,
 352        Vector3d c,
 353        Fixed64 sideTolerance)
 354    {
 254355        Vector3d normal = Vector3d.Cross(b - a, c - a);
 254356        if (normal.MagnitudeSquared <= Fixed64.Zero)
 4357            return false;
 358
 250359        Fixed64 pointSide = Vector3d.Dot(normal, point - a);
 250360        Fixed64 oppositeSide = Vector3d.Dot(normal, opposite - a);
 250361        return pointSide * oppositeSide >= -sideTolerance;
 362    }
 363
 364    private static Fixed64 SignedTetrahedronVolume6(Vector3d a, Vector3d b, Vector3d c, Vector3d d) =>
 102365        Vector3d.Dot(b - a, Vector3d.Cross(c - a, d - a));
 366
 367    private static bool ContainsSupportPoint(
 368        SupportPoint[] simplex,
 369        int count,
 370        Vector3d point)
 371    {
 6624372        for (int i = 0; i < count; i++)
 373        {
 1949374            Vector3d difference = simplex[i].Point - point;
 1949375            if (Vector3d.TryGetMagnitude(difference, out Fixed64 distance)
 1949376                && distance <= Fixed64.Epsilon)
 83377                return true;
 378        }
 379
 1363380        return false;
 381    }
 382}

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Queries/3D/Sweeps/ConvexSweepQueryWorker.State.cs

#LineLine coverage
 1//=======================================================================
 2// ConvexSweepQueryWorker.State.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 System;
 11using System.Collections.Generic;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas.Queries;
 15
 16internal sealed partial class ConvexSweepQueryWorker
 17{
 18    private readonly struct SupportPoint
 19    {
 20        public SupportPoint(
 21            FixedPointAnchor pointA,
 22            FixedPointAnchor pointB,
 23            Vector3d point)
 24        {
 144625            PointA = pointA;
 144626            PointB = pointB;
 144627            Point = point;
 144628        }
 29
 30        public FixedPointAnchor PointA { get; }
 31
 32        public FixedPointAnchor PointB { get; }
 33
 34        public Vector3d Point { get; }
 35    }
 36
 37    private readonly struct SweepTriangleCandidate
 38    {
 39        public SweepTriangleCandidate(
 40            int triangleIndex,
 41            Fixed64 lowerBoundNumerator)
 42        {
 2843            TriangleIndex = triangleIndex;
 2844            LowerBoundNumerator = lowerBoundNumerator;
 2845        }
 46
 47        public int TriangleIndex { get; }
 48
 49        public Fixed64 LowerBoundNumerator { get; }
 50    }
 51
 52    private sealed class SweepTriangleCandidateComparer : IComparer<SweepTriangleCandidate>
 53    {
 54        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 55        public int Compare(SweepTriangleCandidate left, SweepTriangleCandidate right)
 56        {
 1257            int lowerBoundCompare =
 1258                left.LowerBoundNumerator.CompareTo(
 1259                    right.LowerBoundNumerator);
 1260            return lowerBoundCompare != 0
 1261                ? lowerBoundCompare
 1262                : left.TriangleIndex.CompareTo(right.TriangleIndex);
 63        }
 64    }
 65
 66    private readonly struct GjkResult
 67    {
 68        public GjkResult(
 69            bool intersects,
 70            Fixed64 distance,
 71            FixedPointAnchor pointA,
 72            FixedPointAnchor pointB,
 73            Vector3d normal)
 74        {
 32975            Intersects = intersects;
 32976            Distance = distance;
 32977            PointA = pointA;
 32978            PointB = pointB;
 32979            Normal = normal;
 32980        }
 81
 82        public bool Intersects { get; }
 83
 84        public Fixed64 Distance { get; }
 85
 86        public FixedPointAnchor PointA { get; }
 87
 88        public FixedPointAnchor PointB { get; }
 89
 90        public Vector3d Normal { get; }
 91
 92        public static GjkResult CreateIntersection(
 93            FixedPointAnchor pointA,
 94            FixedPointAnchor pointB) =>
 13795            new(true, Fixed64.Zero, pointA, pointB, Vector3d.Zero);
 96    }
 97
 98    private readonly struct ClosestSimplexResult
 99    {
 100        public ClosestSimplexResult(
 101            bool intersects,
 102            Vector3d point,
 103            FixedPointAnchor pointA,
 104            FixedPointAnchor pointB)
 105        {
 1663106            Intersects = intersects;
 1663107            Point = point;
 1663108            PointA = pointA;
 1663109            PointB = pointB;
 1663110            DistanceSqr = point.MagnitudeSquared;
 1663111        }
 112
 113        public bool Intersects { get; }
 114
 115        public Vector3d Point { get; }
 116
 117        public FixedPointAnchor PointA { get; }
 118
 119        public FixedPointAnchor PointB { get; }
 120
 121        public Fixed64 DistanceSqr { get; }
 122
 123        public static ClosestSimplexResult Intersection =>
 27124            new(true, Vector3d.Zero, default, default);
 125
 126        public static ClosestSimplexResult FromWeights(
 127            SupportPoint[] simplex,
 128            int count,
 129            Fixed64 first,
 130            Fixed64 second,
 131            Fixed64 third,
 132            Fixed64 fourth)
 133        {
 719134            Span<Fixed64> weights = stackalloc Fixed64[4];
 719135            weights[0] = first;
 719136            weights[1] = second;
 719137            weights[2] = third;
 719138            weights[3] = fourth;
 719139            return FromArrayWeights(simplex, weights, count);
 140        }
 141
 142        public static ClosestSimplexResult FromIndexedWeights(
 143            SupportPoint[] simplex,
 144            Span<int> indices,
 145            Span<Fixed64> weights)
 146        {
 300147            Span<SupportPoint> selected = stackalloc SupportPoint[3];
 2400148            for (int i = 0; i < 3; i++)
 900149                selected[i] = simplex[indices[i]];
 150
 300151            return FromSpanWeights(selected, weights, 3);
 152        }
 153
 154        public static ClosestSimplexResult FromSpanWeights(
 155            Span<SupportPoint> simplex,
 156            Span<Fixed64> weights,
 157            int count)
 158        {
 1636159            Span<Vector3d> points = stackalloc Vector3d[4];
 1636160            Span<Vector3d> firstLocalPoints = stackalloc Vector3d[4];
 1636161            Span<Vector3d> firstLocalDisplacements = stackalloc Vector3d[4];
 1636162            Span<Vector3d> secondLocalPoints = stackalloc Vector3d[4];
 1636163            Span<Vector3d> secondLocalDisplacements = stackalloc Vector3d[4];
 1636164            int frameIndex = 0;
 9908165            for (int i = 0; i < count; i++)
 166            {
 3318167                points[i] = simplex[i].Point;
 3318168                firstLocalPoints[i] = simplex[i].PointA.LocalPoint;
 3318169                firstLocalDisplacements[i] =
 3318170                    simplex[i].PointA.LocalDisplacement;
 3318171                secondLocalPoints[i] = simplex[i].PointB.LocalPoint;
 3318172                secondLocalDisplacements[i] =
 3318173                    simplex[i].PointB.LocalDisplacement;
 3318174                if (weights[frameIndex] <= Fixed64.Zero
 3318175                    && weights[i] > Fixed64.Zero)
 176                {
 51177                    frameIndex = i;
 178                }
 179            }
 180
 1636181            _ = Vector3d.TryGetWeightedAverage(
 1636182                points[..count],
 1636183                weights[..count],
 1636184                out Vector3d point);
 1636185            _ = Vector3d.TryGetWeightedAverage(
 1636186                firstLocalPoints[..count],
 1636187                weights[..count],
 1636188                out Vector3d firstLocalPoint);
 1636189            _ = Vector3d.TryGetWeightedAverage(
 1636190                firstLocalDisplacements[..count],
 1636191                weights[..count],
 1636192                out Vector3d firstLocalDisplacement);
 1636193            _ = Vector3d.TryGetWeightedAverage(
 1636194                secondLocalPoints[..count],
 1636195                weights[..count],
 1636196                out Vector3d secondLocalPoint);
 1636197            _ = Vector3d.TryGetWeightedAverage(
 1636198                secondLocalDisplacements[..count],
 1636199                weights[..count],
 1636200                out Vector3d secondLocalDisplacement);
 1636201            FixedPointAnchor firstFrame = simplex[frameIndex].PointA;
 1636202            FixedPointAnchor secondFrame = simplex[frameIndex].PointB;
 1636203            return new ClosestSimplexResult(
 1636204                false,
 1636205                point,
 1636206                new FixedPointAnchor(
 1636207                    firstFrame.Origin,
 1636208                    firstFrame.Rotation,
 1636209                    firstLocalPoint,
 1636210                    firstLocalDisplacement),
 1636211                new FixedPointAnchor(
 1636212                    secondFrame.Origin,
 1636213                    secondFrame.Rotation,
 1636214                    secondLocalPoint,
 1636215                    secondLocalDisplacement));
 216        }
 217
 218        private static ClosestSimplexResult FromArrayWeights(
 219            SupportPoint[] simplex,
 220            Span<Fixed64> weights,
 221            int count)
 222        {
 719223            return FromSpanWeights(simplex.AsSpan(), weights, count);
 224        }
 225    }
 226
 227    internal readonly struct TriangleWeights
 228    {
 229        public TriangleWeights(Fixed64 a, Fixed64 b, Fixed64 c)
 230        {
 1257231            A = a;
 1257232            B = b;
 1257233            C = c;
 1257234        }
 235
 236        public Fixed64 A { get; }
 237
 238        public Fixed64 B { get; }
 239
 240        public Fixed64 C { get; }
 241    }
 242}

Methods/Properties

.cctor()
.ctor(System.Int32)
PrepareConvexMeshSource(Gravitas.Colliders.LSMeshCollider,FixedMathSharp.Vector3d)
PrepareCompoundSource(Gravitas.Colliders.LSCompoundCollider,FixedMathSharp.Vector3d)
PreparePrimitiveSource(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d)
PrepareCircleSlabSource(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
PrepareSphereSource(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
TrySweepPreparedSource(Gravitas.Colliders.LSCollider,Gravitas.Queries.Physics3DHit&)
Prepare(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d)
Prepare(Gravitas.Queries.ConvexShape,FixedMathSharp.Vector3d)
TrySweepCompoundSource(Gravitas.Colliders.LSCompoundCollider,Gravitas.Colliders.LSCollider,Gravitas.Queries.Physics3DHit&)
TrySweepSourceShape(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSCollider,Gravitas.Queries.Physics3DHit&,FixedMathSharp.Fixed64&)
TrySweepTargetCompound(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSCompoundCollider,Gravitas.Queries.Physics3DHit&,FixedMathSharp.Fixed64&)
TrySweepConcaveMeshTarget(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSMeshCollider,Gravitas.Queries.Physics3DHit&,FixedMathSharp.Fixed64&)
BuildOrderedSweepTriangleCandidates(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSMeshCollider)
TryComputeSweepLowerBoundNumerator(Gravitas.Queries.ConvexShape,Gravitas.Queries.ConvexShape,FixedMathSharp.Fixed64&)
TryComputeSweepLowerBoundNumerator(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
IncludeAxisEntryNumerator(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
RemainingSweepTrianglesCannotBeat(FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Fixed64)
TrySweepConvexTarget(Gravitas.Queries.ConvexShape,Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSCollider,Gravitas.Queries.Physics3DHit&,FixedMathSharp.Fixed64&)
TryResolveEndpointBracket(Gravitas.Queries.ConvexShape,Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSCollider,FixedMathSharp.Fixed64,Gravitas.Queries.Physics3DHit&,FixedMathSharp.Fixed64&)
GetChordOffset(FixedMathSharp.Fixed64)
ResolveHitAnchor(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSCollider,Gravitas.Queries.ConvexShape,Gravitas.Queries.ConvexSweepQueryWorker/GjkResult,FixedMathSharp.Vector3d&,System.Boolean&,System.Boolean&)
ResolveHitNormal(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,System.Boolean,System.Boolean)
CreateColliderShape(Gravitas.Colliders.LSCollider,FixedMathSharp.Vector3d)
CreateTriangleShape(Gravitas.Colliders.LSMeshCollider,System.Int32)
ThrowIfConcaveSource(Gravitas.Colliders.LSMeshCollider)
CreateSweptSourceBoundsInMeshFrame(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSMeshCollider,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
CanSweptSourceShapeReachTarget(Gravitas.Queries.ConvexShape,Gravitas.Colliders.LSCollider)
ComesBeforeReducerCandidate(FixedMathSharp.Fixed64,System.Int32,System.Boolean,FixedMathSharp.Fixed64,System.Int32)
CreateConcaveSourceException(Gravitas.Colliders.LSMeshCollider)
ComputeDistance(Gravitas.Queries.ConvexShape,Gravitas.Queries.ConvexShape)
CreateSupportPoint(Gravitas.Queries.ConvexShape,Gravitas.Queries.ConvexShape,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
SolveClosestSimplex(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32&,FixedMathSharp.Fixed64)
ReduceSegment(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32&,FixedMathSharp.Fixed64)
ReduceTriangle(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32&)
ReduceTetrahedron(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32&,FixedMathSharp.Fixed64)
EvaluateFace(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32,System.Int32,System.Int32,Gravitas.Queries.ConvexSweepQueryWorker/ClosestSimplexResult&,System.Boolean&,System.Span`1<System.Int32>,System.Span`1<FixedMathSharp.Fixed64>,System.Span`1<System.Int32>,System.Span`1<FixedMathSharp.Fixed64>)
IsCloser(FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
ReduceByWeights(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
AddWeightedPoint(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint,FixedMathSharp.Fixed64,System.Span`1<Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint>,System.Span`1<FixedMathSharp.Fixed64>,System.Int32&)
ClosestPointOnTriangleToOrigin(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
ClosestPointOnDegenerateTriangleToOrigin(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetClosestSegmentWeights(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,FixedMathSharp.Vector3d&)
IsOriginInsideTetrahedron(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
IsSameSideOfFace(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
SignedTetrahedronVolume6(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
ContainsSupportPoint(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32,FixedMathSharp.Vector3d)
.ctor(FixedMathSharp.Geometry.FixedPointAnchor,FixedMathSharp.Geometry.FixedPointAnchor,FixedMathSharp.Vector3d)
.ctor(System.Int32,FixedMathSharp.Fixed64)
Compare(Gravitas.Queries.ConvexSweepQueryWorker/SweepTriangleCandidate,Gravitas.Queries.ConvexSweepQueryWorker/SweepTriangleCandidate)
.ctor(System.Boolean,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedPointAnchor,FixedMathSharp.Geometry.FixedPointAnchor,FixedMathSharp.Vector3d)
CreateIntersection(FixedMathSharp.Geometry.FixedPointAnchor,FixedMathSharp.Geometry.FixedPointAnchor)
.ctor(System.Boolean,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedPointAnchor,FixedMathSharp.Geometry.FixedPointAnchor)
get_Intersection()
FromWeights(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Int32,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FromIndexedWeights(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Span`1<System.Int32>,System.Span`1<FixedMathSharp.Fixed64>)
FromSpanWeights(System.Span`1<Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint>,System.Span`1<FixedMathSharp.Fixed64>,System.Int32)
FromArrayWeights(Gravitas.Queries.ConvexSweepQueryWorker/SupportPoint[],System.Span`1<FixedMathSharp.Fixed64>,System.Int32)
.ctor(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)