< Summary

Information
Class: Gravitas.CollisionHandling.MixedContact
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Contacts/Mixed/MixedContact.cs
Line coverage
100%
Covered lines: 97
Uncovered lines: 0
Coverable lines: 97
Total lines: 247
Line coverage: 100%
Branch coverage
100%
Covered branches: 14
Total branches: 14
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%11100%
.ctor(...)100%11100%
.ctor(...)100%88100%
get_Point3D()100%11100%
get_Point2D()100%11100%
TryGetPoint3D(...)100%11100%
TryGetPoint2D(...)100%11100%
TryGetPlanarOffset2DFrom(...)100%11100%
TryGetPlanarOffset2DFrom(...)100%22100%
GetPlanarXZLeverFrom(...)100%11100%
WithMaterialOverride(...)100%11100%
WithFallbackMaterials(...)100%22100%
CreatePlanarReference(...)100%11100%
GetRequiredWorldPoint(...)100%22100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Contacts/Mixed/MixedContact.cs

#LineLine coverage
 1//=======================================================================
 2// MixedContact.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.Materials;
 11using System;
 12using System.Runtime.CompilerServices;
 13
 14namespace Gravitas.CollisionHandling;
 15
 16/// <summary>
 17/// Contact generated between one 3D collider and one embedded 2D collider.
 18/// </summary>
 19public readonly struct MixedContact
 20{
 21    /// <summary>Creates a mixed contact from world-space witness points.</summary>
 22    public MixedContact(
 23        Vector3d point3D,
 24        Vector3d point2D,
 25        Vector3d normal3DTo2D,
 26        Fixed64 depth,
 27        bool depthIsClamped = false)
 4728        : this(
 4729            ContactAnchor.FromWorldPoint(point3D),
 4730            ContactAnchor.FromWorldPoint(point2D),
 4731            normal3DTo2D,
 4732            depth,
 4733            depthIsClamped,
 4734            hasMaterialOverride: false,
 4735            default,
 4736            default)
 4737    { }
 38
 39    /// <summary>
 40    /// Creates a mixed contact with authoritative rigid-frame witnesses.
 41    /// </summary>
 42    /// <exception cref="ArgumentException">
 43    /// Either rigid-frame anchor is invalid.
 44    /// </exception>
 45    public MixedContact(
 46        ContactAnchor anchor3D,
 47        ContactAnchor anchor2D,
 48        Vector3d normal3DTo2D,
 49        Fixed64 depth,
 50        bool depthIsClamped = false)
 347651        : this(
 347652            anchor3D,
 347653            anchor2D,
 347654            normal3DTo2D,
 347655            depth,
 347656            depthIsClamped,
 347657            hasMaterialOverride: false,
 347658            default,
 347659            default)
 347460    { }
 61
 62    internal MixedContact(
 63        Vector3d point3D,
 64        Vector3d point2D,
 65        Vector3d normal3DTo2D,
 66        Fixed64 depth,
 67        PhysicsMaterial material3D,
 68        PhysicsMaterial material2D,
 69        bool depthIsClamped = false)
 170        : this(
 171            ContactAnchor.FromWorldPoint(point3D),
 172            ContactAnchor.FromWorldPoint(point2D),
 173            normal3DTo2D,
 174            depth,
 175            depthIsClamped,
 176            hasMaterialOverride: true,
 177            material3D,
 178            material2D)
 179    { }
 80
 81    internal MixedContact(
 82        ContactAnchor anchor3D,
 83        ContactAnchor anchor2D,
 84        Vector3d normal3DTo2D,
 85        Fixed64 depth,
 86        PhysicsMaterial material3D,
 87        PhysicsMaterial material2D,
 88        bool depthIsClamped = false)
 1489        : this(
 1490            anchor3D,
 1491            anchor2D,
 1492            normal3DTo2D,
 1493            depth,
 1494            depthIsClamped,
 1495            hasMaterialOverride: true,
 1496            material3D,
 1497            material2D)
 1498    { }
 99
 100    private MixedContact(
 101        ContactAnchor anchor3D,
 102        ContactAnchor anchor2D,
 103        Vector3d normal3DTo2D,
 104        Fixed64 depth,
 105        bool depthIsClamped,
 106        bool hasMaterialOverride,
 107        PhysicsMaterial material3D,
 108        PhysicsMaterial material2D)
 109    {
 3538110        if (!anchor3D.IsValid)
 1111            throw new ArgumentException("The 3D contact anchor must be valid.", nameof(anchor3D));
 3537112        if (!anchor2D.IsValid)
 1113            throw new ArgumentException("The 2D contact anchor must be valid.", nameof(anchor2D));
 114
 3536115        Anchor3D = anchor3D;
 3536116        Anchor2D = anchor2D;
 3536117        Normal3DTo2D = normal3DTo2D;
 3536118        Depth = depth;
 3536119        DepthIsClamped = depthIsClamped;
 3536120        HasMaterialOverride = hasMaterialOverride;
 3536121        Material3D = hasMaterialOverride ? material3D : PhysicsMaterial.Default;
 3536122        Material2D = hasMaterialOverride ? material2D : PhysicsMaterial.Default;
 3536123        HasContact = true;
 3536124    }
 125
 126    /// <summary>Gets whether this value contains a valid mixed contact.</summary>
 127    public bool HasContact { get; }
 128
 129    /// <summary>Gets the authoritative contact anchor on the 3D collider.</summary>
 130    public ContactAnchor Anchor3D { get; }
 131
 132    /// <summary>Gets the authoritative contact anchor on the embedded 2D collider.</summary>
 133    public ContactAnchor Anchor2D { get; }
 134
 135    /// <summary>Gets the world-space witness point on the 3D collider.</summary>
 20136    public Vector3d Point3D => GetRequiredWorldPoint(Anchor3D, nameof(Point3D));
 137
 138    /// <summary>Gets the world-space witness point on the embedded 2D collider.</summary>
 25139    public Vector3d Point2D => GetRequiredWorldPoint(Anchor2D, nameof(Point2D));
 140
 141    /// <summary>Attempts to resolve the 3D anchor to a world-space point.</summary>
 142    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 170143    public bool TryGetPoint3D(out Vector3d point) => Anchor3D.TryGetWorldPoint(out point);
 144
 145    /// <summary>Attempts to resolve the embedded 2D anchor to a world-space point.</summary>
 146    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 173147    public bool TryGetPoint2D(out Vector3d point) => Anchor2D.TryGetWorldPoint(out point);
 148
 149    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 150    internal bool TryGetPlanarOffset2DFrom(
 151        Vector2d origin,
 152        out Vector2d offset) =>
 1153        TryGetPlanarOffset2DFrom(
 1154            origin,
 1155            Fixed64.Zero,
 1156            Vector2d.Zero,
 1157            out offset);
 158
 159    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 160    internal bool TryGetPlanarOffset2DFrom(
 161        Vector2d origin,
 162        Fixed64 rotation,
 163        Vector2d localPoint,
 164        out Vector2d offset)
 165    {
 379166        ContactAnchor reference =
 379167            CreatePlanarReference(origin, rotation, localPoint);
 379168        if (!Anchor2D.TryGetOffsetFrom(
 379169                reference,
 379170                out Vector3d offset3D))
 171        {
 2172            offset = default;
 2173            return false;
 174        }
 175
 377176        offset = new Vector2d(offset3D.X, offset3D.Z);
 377177        return true;
 178    }
 179
 180    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 181    internal ExactLever3D GetPlanarXZLeverFrom(
 182        Vector2d origin,
 183        Fixed64 rotation,
 184        Vector2d localPoint) =>
 149185        Anchor2D.GetLeverFrom(
 149186            CreatePlanarReference(origin, rotation, localPoint));
 187
 188    /// <summary>
 189    /// Contact normal pointing from the 3D collider toward the embedded 2D collider volume.
 190    /// </summary>
 191    public Vector3d Normal3DTo2D { get; }
 192
 193    /// <summary>Gets the penetration depth along <see cref="Normal3DTo2D"/>.</summary>
 194    public Fixed64 Depth { get; }
 195
 196    /// <summary>
 197    /// Gets whether the exact penetration depth exceeded the scalar domain and
 198    /// <see cref="Depth"/> therefore contains <see cref="Fixed64.MaxValue"/>.
 199    /// </summary>
 200    public bool DepthIsClamped { get; }
 201
 202    /// <summary>Gets whether the contact carries per-feature material values.</summary>
 203    public bool HasMaterialOverride { get; }
 204
 205    /// <summary>Gets the material sampled from the 3D contact feature.</summary>
 206    public PhysicsMaterial Material3D { get; }
 207
 208    /// <summary>Gets the material sampled from the embedded 2D contact feature.</summary>
 209    public PhysicsMaterial Material2D { get; }
 210
 211    internal MixedContact WithMaterialOverride(PhysicsMaterial material3D, PhysicsMaterial material2D) =>
 14212        new(
 14213            Anchor3D,
 14214            Anchor2D,
 14215            Normal3DTo2D,
 14216            Depth,
 14217            material3D,
 14218            material2D,
 14219            DepthIsClamped);
 220
 221    internal MixedContact WithFallbackMaterials(PhysicsMaterial material3D, PhysicsMaterial material2D) =>
 13222        HasMaterialOverride ? this : WithMaterialOverride(material3D, material2D);
 223
 224    private ContactAnchor CreatePlanarReference(
 225        Vector2d origin,
 226        Fixed64 rotation,
 227        Vector2d localPoint) =>
 528228        new(
 528229            new Vector3d(
 528230                origin.X,
 528231                Anchor2D.Origin.Y,
 528232                origin.Y),
 528233            FixedQuaternion.FromAxisAngle(Vector3d.Up, -rotation),
 528234            new Vector3d(
 528235                localPoint.X,
 528236                Fixed64.Zero,
 528237                localPoint.Y));
 238
 239    private static Vector3d GetRequiredWorldPoint(ContactAnchor anchor, string propertyName)
 240    {
 45241        if (anchor.TryGetWorldPoint(out Vector3d point))
 43242            return point;
 243
 2244        throw new InvalidOperationException(
 2245            $"{propertyName} is outside the representable coordinate range. Use the corresponding TryGet method.");
 246    }
 247}

Methods/Properties

.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean)
.ctor(Gravitas.CollisionHandling.ContactAnchor,Gravitas.CollisionHandling.ContactAnchor,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean)
.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Materials.PhysicsMaterial,Gravitas.Materials.PhysicsMaterial,System.Boolean)
.ctor(Gravitas.CollisionHandling.ContactAnchor,Gravitas.CollisionHandling.ContactAnchor,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Gravitas.Materials.PhysicsMaterial,Gravitas.Materials.PhysicsMaterial,System.Boolean)
.ctor(Gravitas.CollisionHandling.ContactAnchor,Gravitas.CollisionHandling.ContactAnchor,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean,System.Boolean,Gravitas.Materials.PhysicsMaterial,Gravitas.Materials.PhysicsMaterial)
get_Point3D()
get_Point2D()
TryGetPoint3D(FixedMathSharp.Vector3d&)
TryGetPoint2D(FixedMathSharp.Vector3d&)
TryGetPlanarOffset2DFrom(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d&)
TryGetPlanarOffset2DFrom(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d&)
GetPlanarXZLeverFrom(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d)
WithMaterialOverride(Gravitas.Materials.PhysicsMaterial,Gravitas.Materials.PhysicsMaterial)
WithFallbackMaterials(Gravitas.Materials.PhysicsMaterial,Gravitas.Materials.PhysicsMaterial)
CreatePlanarReference(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d)
GetRequiredWorldPoint(Gravitas.CollisionHandling.ContactAnchor,System.String)