< Summary

Information
Class: Gravitas.CollisionHandling.ContactWarmStartCache
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Contacts/3D/ContactWarmStartCache.cs
Line coverage
100%
Covered lines: 77
Uncovered lines: 0
Coverable lines: 77
Total lines: 184
Line coverage: 100%
Branch coverage
100%
Covered branches: 32
Total branches: 32
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Clear()100%11100%
Set(...)100%66100%
TryGet(...)100%44100%
Remove(...)100%66100%
GetContactIdForReplayHash(...)100%11100%
GetImpulseForReplayHash(...)100%11100%
GetContactId(...)100%44100%
GetImpulseUnchecked(...)100%44100%
SetContactIdUnchecked(...)100%44100%
SetImpulseUnchecked(...)100%44100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Contacts/3D/ContactWarmStartCache.cs

#LineLine coverage
 1//=======================================================================
 2// ContactWarmStartCache.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 System.Runtime.CompilerServices;
 10
 11namespace Gravitas.CollisionHandling;
 12
 13/// <summary>
 14/// Fixed-size 3D warm-start cache keyed by stable manifold contact identity.
 15/// </summary>
 16internal struct ContactWarmStartCache
 17{
 18    private ulong _contactId0;
 19    private ulong _contactId1;
 20    private ulong _contactId2;
 21    private ulong _contactId3;
 22    private ContactWarmStartImpulse _impulse0;
 23    private ContactWarmStartImpulse _impulse1;
 24    private ContactWarmStartImpulse _impulse2;
 25    private ContactWarmStartImpulse _impulse3;
 26
 27    public int Count { get; private set; }
 28
 29    public void Clear()
 30    {
 251031        Count = 0;
 251032        _contactId0 = 0UL;
 251033        _contactId1 = 0UL;
 251034        _contactId2 = 0UL;
 251035        _contactId3 = 0UL;
 251036        _impulse0 = default;
 251037        _impulse1 = default;
 251038        _impulse2 = default;
 251039        _impulse3 = default;
 251040    }
 41
 42    public void Set(
 43        ulong contactId,
 44        Vector3d normal,
 45        Fixed64 normalImpulse,
 46        Fixed64 tangentImpulse,
 47        Fixed64 secondaryTangentImpulse = default)
 48    {
 912149        ContactWarmStartImpulse impulse = new(normal, normalImpulse, tangentImpulse, secondaryTangentImpulse);
 6630450        for (int i = 0; i < Count; i++)
 51        {
 3204652            if (GetContactId(i) != contactId)
 53                continue;
 54
 801555            SetImpulseUnchecked(i, impulse);
 801556            return;
 57        }
 58
 110659        if (Count < ContactManifold.MaxContactCount)
 60        {
 38061            SetContactIdUnchecked(Count, contactId);
 38062            SetImpulseUnchecked(Count, impulse);
 38063            Count++;
 38064            return;
 65        }
 66
 67        // Contact manifolds are already reduced to four stable contacts; replacement preserves bounded state if an olde
 72668        SetContactIdUnchecked(ContactManifold.MaxContactCount - 1, contactId);
 72669        SetImpulseUnchecked(ContactManifold.MaxContactCount - 1, impulse);
 72670    }
 71
 72    public bool TryGet(ulong contactId, out ContactWarmStartImpulse impulse)
 73    {
 6612474        for (int i = 0; i < Count; i++)
 75        {
 3202276            if (GetContactId(i) != contactId)
 77                continue;
 78
 809379            impulse = GetImpulseUnchecked(i);
 809380            return true;
 81        }
 82
 104083        impulse = default;
 104084        return false;
 85    }
 86
 87    public bool Remove(ulong contactId)
 88    {
 3289        for (int index = 0; index < Count; index++)
 90        {
 1291            if (GetContactId(index) != contactId)
 92                continue;
 93
 994            Count--;
 2095            for (int shift = index; shift < Count; shift++)
 96            {
 197                SetContactIdUnchecked(
 198                    shift,
 199                    GetContactId(shift + 1));
 1100                SetImpulseUnchecked(
 1101                    shift,
 1102                    GetImpulseUnchecked(shift + 1));
 103            }
 104
 9105            SetContactIdUnchecked(Count, 0UL);
 9106            SetImpulseUnchecked(Count, default);
 9107            return true;
 108        }
 109
 4110        return false;
 111    }
 112
 113    internal ulong GetContactIdForReplayHash(int index)
 114    {
 3115        SwiftThrowHelper.ThrowIfArrayIndexInvalid(index, Count, nameof(index));
 3116        return GetContactId(index);
 117    }
 118
 119    internal ContactWarmStartImpulse GetImpulseForReplayHash(int index)
 120    {
 1121        SwiftThrowHelper.ThrowIfArrayIndexInvalid(index, Count, nameof(index));
 1122        return GetImpulseUnchecked(index);
 123    }
 124
 125    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 126    private ulong GetContactId(int index) =>
 64084127        index switch
 64084128        {
 17808129            0 => _contactId0,
 16377130            1 => _contactId1,
 15387131            2 => _contactId2,
 14512132            _ => _contactId3
 64084133        };
 134
 135    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 136    private ContactWarmStartImpulse GetImpulseUnchecked(int index) =>
 8095137        index switch
 8095138        {
 681139            0 => _impulse0,
 450140            1 => _impulse1,
 404141            2 => _impulse2,
 6560142            _ => _impulse3
 8095143        };
 144
 145    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 146    private void SetContactIdUnchecked(int index, ulong contactId)
 147    {
 148        switch (index)
 149        {
 150            case 0:
 237151                _contactId0 = contactId;
 237152                break;
 153            case 1:
 62154                _contactId1 = contactId;
 62155                break;
 156            case 2:
 51157                _contactId2 = contactId;
 51158                break;
 159            default:
 766160                _contactId3 = contactId;
 161                break;
 162        }
 766163    }
 164
 165    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 166    private void SetImpulseUnchecked(int index, ContactWarmStartImpulse impulse)
 167    {
 168        switch (index)
 169        {
 170            case 0:
 869171                _impulse0 = impulse;
 869172                break;
 173            case 1:
 512174                _impulse1 = impulse;
 512175                break;
 176            case 2:
 454177                _impulse2 = impulse;
 454178                break;
 179            default:
 7296180                _impulse3 = impulse;
 181                break;
 182        }
 7296183    }
 184}