< Summary

Information
Class: Gravitas.CollisionHandling.ManifoldContact
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Contacts/3D/ManifoldContact.cs
Line coverage
100%
Covered lines: 50
Uncovered lines: 0
Coverable lines: 50
Total lines: 189
Line coverage: 100%
Branch coverage
100%
Covered branches: 12
Total branches: 12
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%
.ctor(...)100%11100%
.ctor(...)100%1010100%
get_PointA()100%11100%
get_PointB()100%11100%
TryGetPointA(...)100%11100%
TryGetPointB(...)100%11100%
GetRequiredWorldPoint(...)100%22100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// ManifoldContact.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2026–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using FixedMathSharp;
 9using Gravitas.Materials;
 10using System;
 11using System.Runtime.CompilerServices;
 12
 13namespace Gravitas.CollisionHandling;
 14
 15/// <summary>
 16/// A deterministic narrow-phase contact point for a collision manifold.
 17/// </summary>
 18public readonly struct ManifoldContact
 19{
 20    /// <summary>Creates a deterministic contact from world-space witness points.</summary>
 21    public ManifoldContact(
 22        ulong contactId,
 23        Vector3d pointA,
 24        Vector3d pointB,
 25        Fixed64 depth,
 26        Vector3d normal,
 27        bool hasMaterialOverride = false,
 28        PhysicsMaterial materialA = default,
 29        PhysicsMaterial materialB = default,
 30        bool depthIsClamped = false)
 331        : this(
 332            contactId,
 333            ContactAnchor.FromWorldPoint(pointA),
 334            ContactAnchor.FromWorldPoint(pointB),
 335            depth,
 336            normal,
 337            hasMaterialOverride,
 338            materialA,
 339            materialB,
 340            depthIsClamped)
 341    { }
 42
 43    /// <summary>
 44    /// Creates a deterministic contact from authoritative rigid-frame anchors.
 45    /// </summary>
 46    public ManifoldContact(
 47        ulong contactId,
 48        ContactAnchor anchorA,
 49        ContactAnchor anchorB,
 50        Fixed64 depth,
 51        Vector3d normal,
 52        bool hasMaterialOverride = false,
 53        PhysicsMaterial materialA = default,
 54        PhysicsMaterial materialB = default,
 55        bool depthIsClamped = false)
 956        : this(
 957            contactId,
 958            anchorA,
 959            anchorB,
 960            depth,
 961            normal,
 962            hasMaterialOverride,
 963            materialA,
 964            materialB,
 965            depthIsClamped,
 966            featureNamespaceA: 0,
 967            featureNamespaceB: 0)
 768    { }
 69
 70    internal ManifoldContact(
 71        ulong contactId,
 72        ContactAnchor anchorA,
 73        ContactAnchor anchorB,
 74        Fixed64 depth,
 75        Vector3d normal,
 76        bool hasMaterialOverride,
 77        PhysicsMaterial materialA,
 78        PhysicsMaterial materialB,
 79        bool depthIsClamped,
 80        int featureNamespaceA,
 81        int featureNamespaceB)
 82    {
 3038983        if (!anchorA.IsValid)
 184            throw new ArgumentException("Contact anchor A must be valid.", nameof(anchorA));
 3038885        if (!anchorB.IsValid)
 186            throw new ArgumentException("Contact anchor B must be valid.", nameof(anchorB));
 87
 3038788        ContactId = contactId;
 3038789        AnchorA = anchorA;
 3038790        AnchorB = anchorB;
 3038791        FeatureNamespaceA = featureNamespaceA;
 3038792        FeatureNamespaceB = featureNamespaceB;
 3038793        Depth = depth.Abs();
 3038794        DepthIsClamped = depthIsClamped;
 3038795        Normal = normal.MagnitudeSquared > Fixed64.Epsilon
 3038796            ? normal.Normalized
 3038797            : Vector3d.Zero;
 3038798        HasMaterialOverride = hasMaterialOverride;
 3038799        MaterialA = hasMaterialOverride ? materialA : PhysicsMaterial.Default;
 30387100        MaterialB = hasMaterialOverride ? materialB : PhysicsMaterial.Default;
 30387101    }
 102
 103    /// <summary>
 104    /// Stable identity derived from the unordered pair of pose-invariant
 105    /// canonical local feature terms.
 106    /// </summary>
 107    public ulong ContactId { get; }
 108
 109    /// <summary>
 110    /// Authoritative canonical contact anchor on collider A.
 111    /// </summary>
 112    public ContactAnchor AnchorA { get; }
 113
 114    /// <summary>
 115    /// Authoritative canonical contact anchor on collider B.
 116    /// </summary>
 117    public ContactAnchor AnchorB { get; }
 118
 119    internal int FeatureNamespaceA { get; }
 120
 121    internal int FeatureNamespaceB { get; }
 122
 123    /// <summary>
 124    /// Gets the world-space contact point on collider A.
 125    /// </summary>
 126    /// <exception cref="InvalidOperationException">
 127    /// The conceptual world point is outside the scalar range.
 128    /// </exception>
 103129    public Vector3d PointA => GetRequiredWorldPoint(AnchorA, nameof(PointA));
 130
 131    /// <summary>
 132    /// Gets the world-space contact point on collider B.
 133    /// </summary>
 134    /// <exception cref="InvalidOperationException">
 135    /// The conceptual world point is outside the scalar range.
 136    /// </exception>
 74137    public Vector3d PointB => GetRequiredWorldPoint(AnchorB, nameof(PointB));
 138
 139    /// <summary>
 140    /// Attempts to materialize the world-space contact point on collider A.
 141    /// </summary>
 142    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 40143    public bool TryGetPointA(out Vector3d point) => AnchorA.TryGetWorldPoint(out point);
 144
 145    /// <summary>
 146    /// Attempts to materialize the world-space contact point on collider B.
 147    /// </summary>
 148    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 38149    public bool TryGetPointB(out Vector3d point) => AnchorB.TryGetWorldPoint(out point);
 150
 151    /// <summary>
 152    /// Penetration depth along <see cref="Normal"/>.
 153    /// </summary>
 154    public Fixed64 Depth { get; }
 155
 156    /// <summary>
 157    /// Gets whether the conceptual penetration depth exceeded the scalar range.
 158    /// </summary>
 159    public bool DepthIsClamped { get; }
 160
 161    /// <summary>
 162    /// Unit normal pointing from collider A toward collider B.
 163    /// </summary>
 164    public Vector3d Normal { get; }
 165
 166    /// <summary>
 167    /// Gets whether this contact carries materials from private compound parts.
 168    /// </summary>
 169    public bool HasMaterialOverride { get; }
 170
 171    /// <summary>
 172    /// Material for collider A when <see cref="HasMaterialOverride"/> is true.
 173    /// </summary>
 174    public PhysicsMaterial MaterialA { get; }
 175
 176    /// <summary>
 177    /// Material for collider B when <see cref="HasMaterialOverride"/> is true.
 178    /// </summary>
 179    public PhysicsMaterial MaterialB { get; }
 180
 181    private static Vector3d GetRequiredWorldPoint(ContactAnchor anchor, string propertyName)
 182    {
 177183        if (anchor.TryGetWorldPoint(out Vector3d point))
 176184            return point;
 185
 1186        throw new InvalidOperationException(
 1187            $"{propertyName} is outside the representable coordinate range. Use the corresponding TryGet method.");
 188    }
 189}