< Summary

Information
Class: Gravitas.PhysicsPartition2D
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Partitions/2D/PhysicsPartition2D.cs
Line coverage
100%
Covered lines: 152
Uncovered lines: 0
Coverable lines: 152
Total lines: 332
Line coverage: 100%
Branch coverage
100%
Covered branches: 110
Total branches: 110
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_IsAllocated()100%11100%
get_IsEmpty()100%1010100%
get_EmptySinceFrame()100%11100%
get_RetainedIndex()100%11100%
get_AwakeDynamicObjectCount()100%22100%
get_Owner()100%11100%
OnAddToVoxel(...)100%11100%
Distribute(...)100%1818100%
CopyAllColliderIds(...)100%11100%
CopyStaticStyleColliderIds(...)100%11100%
CopySortedStaticStyleIds(...)100%11100%
CopyIds(...)100%44100%
AddDynamicObject(...)100%66100%
AddStaticObject(...)100%44100%
AddKinematicObject(...)100%44100%
RemoveDynamicObject(...)100%1010100%
RemoveStaticObject(...)100%66100%
RemoveKinematicObject(...)100%66100%
ContainsAwakeDynamicObject(...)100%22100%
SetDynamicObjectAwake(...)100%1010100%
IsDynamicObjectAwake(...)100%44100%
OnRemoveFromVoxel(...)100%11100%
SetOwner(...)100%22100%
IsOwnedBy(...)100%11100%
SetRetainedIndex(...)100%11100%
ClearRetainedIndex()100%11100%
ResetRetainedMembership()100%1010100%
ResetForPool()100%1010100%
MarkOccupied()100%11100%
MarkEmptyIfUnoccupied()100%22100%
MarkEmpty(...)100%11100%
SetParentIndex(...)100%11100%
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.get_RetainedIndex()100%11100%
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.get_IsEmpty()100%11100%
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.get_EmptySinceFrame()100%11100%
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.IsOwnedBy(...)100%11100%
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.SetRetainedIndex(...)100%11100%
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.ClearRetainedIndex()100%11100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Partitions/2D/PhysicsPartition2D.cs

#LineLine coverage
 1//=======================================================================
 2// PhysicsPartition2D.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 GridForge.Grids;
 10using GridForge.Spatial;
 11using SwiftCollections;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas;
 15
 16/// <summary>
 17/// Stores the pure 2D collider IDs assigned to one GridForge voxel.
 18/// </summary>
 19public sealed class PhysicsPartition2D : IVoxelPartition, IRetainedPhysicsPartition<GravitasCollision2DService>
 20{
 21    private GravitasCollision2DService? _owner;
 4652622    private int _emptySinceFrame = -1;
 4652623    private int _retainedIndex = -1;
 24
 25    /// <summary>Creates an inactive 2D physics partition.</summary>
 4652626    public PhysicsPartition2D()
 27    {
 4652628        ActivationId = -1;
 4652629    }
 30
 31    /// <summary>Gets or sets the world voxel occupied by this partition.</summary>
 32    public WorldVoxelIndex WorldIndex { get; set; }
 33
 34    /// <summary>Gets or sets whether this partition is attached to a voxel.</summary>
 35    public bool IsPartitioned { get; set; }
 36
 37    /// <summary>Stores context-local dynamic collider IDs.</summary>
 38    public SwiftSparseSet? ContainedDynamicObjects;
 39
 40    /// <summary>Stores the awake subset of the dynamic collider IDs.</summary>
 41    public SwiftSparseSet? ContainedAwakeDynamicObjects;
 42
 43    /// <summary>Stores context-local kinematic collider IDs.</summary>
 44    public SwiftSparseSet? ContainedKinematicObjects;
 45
 46    /// <summary>Stores context-local static collider IDs.</summary>
 47    public SwiftSparseSet? ContainedStaticObjects;
 48
 49    /// <summary>Gets the active-partition slot, or <c>-1</c> when inactive.</summary>
 50    public int ActivationId { get; private set; }
 51
 52    /// <summary>Gets whether this partition has an active-partition slot.</summary>
 1373353    public bool IsAllocated => ActivationId != -1;
 54
 55    internal bool IsEmpty =>
 1133617356        (ContainedDynamicObjects?.Count ?? 0) == 0
 1133617357        && (ContainedKinematicObjects?.Count ?? 0) == 0
 1133617358        && (ContainedStaticObjects?.Count ?? 0) == 0;
 59
 1958560    internal int EmptySinceFrame => _emptySinceFrame;
 61
 10092162    internal int RetainedIndex => _retainedIndex;
 63
 64    /// <summary>Gets the number of awake dynamic IDs in this partition.</summary>
 665    public int AwakeDynamicObjectCount => ContainedAwakeDynamicObjects?.Count ?? 0;
 66
 67    /// <summary>Gets the collision service that owns this partition.</summary>
 68    public GravitasCollision2DService Owner
 69    {
 70        get
 71        {
 26977072            SwiftThrowHelper.ThrowIfTrue(
 26977073                _owner == null,
 26977074                nameof(PhysicsPartition2D),
 26977075                "PhysicsPartition2D is missing its owner collision service.");
 26976876            return _owner!;
 77        }
 78    }
 79
 80    /// <summary>Attaches this partition to a voxel.</summary>
 81    public void OnAddToVoxel(Voxel voxel)
 82    {
 5045883        SwiftThrowHelper.ThrowIfTrue(
 5045884            _owner == null,
 5045885            nameof(PhysicsPartition2D),
 5045886            "PhysicsPartition2D is missing its owner collision service.");
 5045887        WorldIndex = voxel.WorldIndex;
 5045888        IsPartitioned = true;
 5045889    }
 90
 91    internal void Distribute(
 92        SwiftList<int> dynamicIds,
 93        SwiftList<int> staticIds)
 94    {
 1555095        int dynamicCount = ContainedDynamicObjects?.Count ?? 0;
 1555096        int awakeDynamicCount = ContainedAwakeDynamicObjects?.Count ?? 0;
 1555097        if (ContainedDynamicObjects == null || dynamicCount == 0 || ContainedAwakeDynamicObjects == null || awakeDynamic
 40898            return;
 99
 15142100        ContainedDynamicObjects.CopySortedKeysTo(dynamicIds);
 15142101        CopySortedStaticStyleIds(staticIds);
 102
 64152103        for (int j = 0; j < dynamicIds.Count; j++)
 104        {
 16934105            int id1 = dynamicIds[j];
 38640106            for (int k = j + 1; k < dynamicIds.Count; k++)
 2386107                Owner.Context.Physics2D.ProcessPartitionCandidate(id1, dynamicIds[k], WorldIndex);
 108
 44544109            for (int k = 0; k < staticIds.Count; k++)
 5338110                Owner.Context.Physics2D.ProcessPartitionCandidate(id1, staticIds[k], WorldIndex);
 111        }
 15142112    }
 113
 114    internal void CopyAllColliderIds(SwiftList<int> destination)
 115    {
 120424116        destination.FastClear();
 120424117        CopyIds(ContainedDynamicObjects, destination);
 120424118        CopyIds(ContainedKinematicObjects, destination);
 120424119        CopyIds(ContainedStaticObjects, destination);
 120424120        destination.SortInPlace();
 120424121    }
 122
 123    internal void CopyStaticStyleColliderIds(SwiftList<int> destination)
 124    {
 49904125        destination.FastClear();
 49904126        CopyIds(ContainedKinematicObjects, destination);
 49904127        CopyIds(ContainedStaticObjects, destination);
 49904128        destination.SortInPlace();
 49904129    }
 130
 131    private void CopySortedStaticStyleIds(SwiftList<int> destination)
 132    {
 15142133        destination.FastClear();
 15142134        CopyIds(ContainedKinematicObjects, destination);
 15142135        CopyIds(ContainedStaticObjects, destination);
 15142136        destination.SortInPlace();
 15142137    }
 138
 139    private static void CopyIds(SwiftSparseSet? source, SwiftList<int> destination)
 140    {
 491364141        if (source == null)
 360707142            return;
 143
 971050144        for (int i = 0; i < source.Count; i++)
 354868145            destination.Add(source.DenseKeys[i]);
 130657146    }
 147
 148    /// <summary>Adds a dynamic collider ID to this partition.</summary>
 149    public void AddDynamicObject(int item)
 150    {
 40804151        ContainedDynamicObjects ??= new();
 40804152        if (!ContainedDynamicObjects.Add(item))
 2153            return;
 154
 40802155        MarkOccupied();
 40802156        SetDynamicObjectAwake(item, IsDynamicObjectAwake(item));
 157
 40802158        if (ContainedDynamicObjects.Count == 1)
 36445159            ActivationId = Owner.ActivatePartition(this);
 40802160    }
 161
 162    /// <summary>Adds a static collider ID to this partition.</summary>
 163    public void AddStaticObject(int item)
 164    {
 22188165        ContainedStaticObjects ??= new();
 22188166        if (ContainedStaticObjects.Add(item))
 22186167            MarkOccupied();
 22188168    }
 169
 170    /// <summary>Adds a kinematic collider ID to this partition.</summary>
 171    public void AddKinematicObject(int item)
 172    {
 3811173        ContainedKinematicObjects ??= new();
 3811174        if (ContainedKinematicObjects.Add(item))
 3809175            MarkOccupied();
 3811176    }
 177
 178    /// <summary>Removes a dynamic collider ID from this partition.</summary>
 179    public void RemoveDynamicObject(int item)
 180    {
 13458181        if (ContainedDynamicObjects?.Remove(item) != true)
 182        {
 4183            GravitasLogger.DebugChannel.Info($"2D dynamic item not removed - {item}");
 4184            return;
 185        }
 186
 13454187        ContainedAwakeDynamicObjects?.Remove(item);
 188
 13454189        if (ContainedDynamicObjects.Count > 0)
 3288190            return;
 191
 10166192        Owner.DeactivatePartition(ActivationId);
 10166193        ActivationId = -1;
 10166194        MarkEmptyIfUnoccupied();
 10166195    }
 196
 197    /// <summary>Removes a static collider ID from this partition.</summary>
 198    public void RemoveStaticObject(int item)
 199    {
 1799200        if (ContainedStaticObjects?.Remove(item) != true)
 201        {
 5202            GravitasLogger.DebugChannel.Info($"2D static item not removed - {item}");
 5203            return;
 204        }
 205
 1794206        MarkEmptyIfUnoccupied();
 1794207    }
 208
 209    /// <summary>Removes a kinematic collider ID from this partition.</summary>
 210    public void RemoveKinematicObject(int item)
 211    {
 2095212        if (ContainedKinematicObjects?.Remove(item) != true)
 213        {
 5214            GravitasLogger.DebugChannel.Info($"2D kinematic item not removed - {item}");
 5215            return;
 216        }
 217
 2090218        MarkEmptyIfUnoccupied();
 2090219    }
 220
 221    /// <summary>Returns whether a dynamic collider ID is marked awake.</summary>
 10222    public bool ContainsAwakeDynamicObject(int item) => ContainedAwakeDynamicObjects?.Contains(item) == true;
 223
 224    /// <summary>Updates the awake state for a dynamic collider in this partition.</summary>
 225    public void SetDynamicObjectAwake(int item, bool awake)
 226    {
 43059227        if (ContainedDynamicObjects?.Contains(item) != true)
 2228            return;
 229
 43057230        if (awake)
 231        {
 41433232            ContainedAwakeDynamicObjects ??= new();
 41433233            ContainedAwakeDynamicObjects.Add(item);
 41433234            return;
 235        }
 236
 1624237        ContainedAwakeDynamicObjects?.Remove(item);
 1429238    }
 239
 240    private bool IsDynamicObjectAwake(int item)
 241    {
 40802242        if (!Owner.Context.Physics2D.TryGetColliderById(item, out LSCollider2D? collider))
 89243            return true;
 244
 40713245        SolidBody2D? body = collider!.Body;
 40713246        return body == null || body.IsAwakeForCollision;
 247    }
 248
 249    /// <summary>Releases this partition when it is removed from a voxel.</summary>
 250    public void OnRemoveFromVoxel(Voxel voxel)
 251    {
 50458252        Owner.ReleasePartition(this);
 50458253    }
 254
 255    internal void SetOwner(GravitasCollision2DService owner)
 256    {
 50473257        SwiftThrowHelper.ThrowIfNull(owner, nameof(owner));
 50473258        SwiftThrowHelper.ThrowIfArgument(
 50473259            _owner != null && !ReferenceEquals(_owner, owner),
 50473260            nameof(owner),
 50473261            "PhysicsPartition2D is already owned by a different collision service.");
 262
 50472263        _owner = owner;
 50472264    }
 265
 11151233266    internal bool IsOwnedBy(GravitasCollision2DService owner) => ReferenceEquals(_owner, owner);
 267
 268    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 269    internal void SetRetainedIndex(int index)
 270    {
 77820271        SwiftThrowHelper.ThrowIfNegative(index, nameof(index));
 77820272        _retainedIndex = index;
 77820273    }
 274
 275    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50463276    internal void ClearRetainedIndex() => _retainedIndex = -1;
 277
 278    internal void ResetRetainedMembership()
 279    {
 3280        ContainedDynamicObjects?.Clear();
 3281        ContainedAwakeDynamicObjects?.Clear();
 3282        ContainedKinematicObjects?.Clear();
 3283        ContainedStaticObjects?.Clear();
 3284        ActivationId = -1;
 3285        MarkEmpty(_owner?.Context.FrameCount ?? 0);
 3286    }
 287
 288    internal void ResetForPool()
 289    {
 50465290        ContainedDynamicObjects?.Clear();
 50465291        ContainedAwakeDynamicObjects?.Clear();
 50465292        ContainedKinematicObjects?.Clear();
 50465293        ContainedStaticObjects?.Clear();
 294
 50465295        if (ActivationId != -1)
 26277296            Owner.DeactivatePartition(ActivationId);
 297
 50465298        _owner = null;
 50465299        ActivationId = -1;
 50465300        IsPartitioned = false;
 50465301        _emptySinceFrame = -1;
 50465302        _retainedIndex = -1;
 50465303    }
 304
 305    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 66797306    private void MarkOccupied() => _emptySinceFrame = -1;
 307
 308    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 309    private void MarkEmptyIfUnoccupied()
 310    {
 14050311        if (IsEmpty)
 10987312            MarkEmpty(Owner.Context.FrameCount);
 14050313    }
 314
 315    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 10990316    private void MarkEmpty(int frame) => _emptySinceFrame = frame;
 317
 318    /// <summary>Updates the world voxel index used by this partition.</summary>
 50458319    public void SetParentIndex(WorldVoxelIndex parentIndex) => WorldIndex = parentIndex;
 320
 100921321    int IRetainedPhysicsPartition<GravitasCollision2DService>.RetainedIndex => RetainedIndex;
 322
 11150941323    bool IRetainedPhysicsPartition<GravitasCollision2DService>.IsEmpty => IsEmpty;
 324
 19578325    int IRetainedPhysicsPartition<GravitasCollision2DService>.EmptySinceFrame => EmptySinceFrame;
 326
 11151233327    bool IRetainedPhysicsPartition<GravitasCollision2DService>.IsOwnedBy(GravitasCollision2DService owner) => IsOwnedBy(
 328
 77820329    void IRetainedPhysicsPartition<GravitasCollision2DService>.SetRetainedIndex(int index) => SetRetainedIndex(index);
 330
 50463331    void IRetainedPhysicsPartition<GravitasCollision2DService>.ClearRetainedIndex() => ClearRetainedIndex();
 332}

Methods/Properties

.ctor()
get_IsAllocated()
get_IsEmpty()
get_EmptySinceFrame()
get_RetainedIndex()
get_AwakeDynamicObjectCount()
get_Owner()
OnAddToVoxel(GridForge.Grids.Voxel)
Distribute(SwiftCollections.SwiftList`1<System.Int32>,SwiftCollections.SwiftList`1<System.Int32>)
CopyAllColliderIds(SwiftCollections.SwiftList`1<System.Int32>)
CopyStaticStyleColliderIds(SwiftCollections.SwiftList`1<System.Int32>)
CopySortedStaticStyleIds(SwiftCollections.SwiftList`1<System.Int32>)
CopyIds(SwiftCollections.SwiftSparseSet,SwiftCollections.SwiftList`1<System.Int32>)
AddDynamicObject(System.Int32)
AddStaticObject(System.Int32)
AddKinematicObject(System.Int32)
RemoveDynamicObject(System.Int32)
RemoveStaticObject(System.Int32)
RemoveKinematicObject(System.Int32)
ContainsAwakeDynamicObject(System.Int32)
SetDynamicObjectAwake(System.Int32,System.Boolean)
IsDynamicObjectAwake(System.Int32)
OnRemoveFromVoxel(GridForge.Grids.Voxel)
SetOwner(Gravitas.GravitasCollision2DService)
IsOwnedBy(Gravitas.GravitasCollision2DService)
SetRetainedIndex(System.Int32)
ClearRetainedIndex()
ResetRetainedMembership()
ResetForPool()
MarkOccupied()
MarkEmptyIfUnoccupied()
MarkEmpty(System.Int32)
SetParentIndex(GridForge.Spatial.WorldVoxelIndex)
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.get_RetainedIndex()
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.get_IsEmpty()
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.get_EmptySinceFrame()
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.IsOwnedBy(Gravitas.GravitasCollision2DService)
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.SetRetainedIndex(System.Int32)
Gravitas.IRetainedPhysicsPartition<Gravitas.GravitasCollision2DService>.ClearRetainedIndex()