< Summary

Information
Class: Gravitas.CollisionHandling.ContinuousCollisionContactPolicy
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Continuous/ContinuousCollisionContactPolicy.cs
Line coverage
100%
Covered lines: 116
Uncovered lines: 0
Coverable lines: 116
Total lines: 178
Line coverage: 100%
Branch coverage
100%
Covered branches: 38
Total branches: 38
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
TryResolveSweptSphereContact(...)100%3232100%
ResolveLegacyNormal(...)100%66100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/CollisionHandling/Continuous/ContinuousCollisionContactPolicy.cs

#LineLine coverage
 1//=======================================================================
 2// ContinuousCollisionContactPolicy.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;
 11
 12namespace Gravitas.CollisionHandling;
 13
 14internal static class ContinuousCollisionContactPolicy
 15{
 16    internal static bool TryResolveSweptSphereContact(
 17        LSCollider target,
 18        Vector3d sphereCenterAtImpact,
 19        Vector3d direction,
 20        out ContactAnchor targetAnchor,
 21        out Vector3d normal)
 22    {
 720623        Vector3d fallbackNormal = direction.MagnitudeSquared > Fixed64.Epsilon
 720624            ? -direction.Normalized
 720625            : Vector3d.Right;
 26
 720627        if (target is LSCapsuleCollider capsule)
 28        {
 2529            normal = capsule.GetNormalAtPoint(sphereCenterAtImpact);
 2530            Vector3d localNormal =
 2531                capsule.Rotation.Inverse().Rotate(normal).Normalized;
 2532            targetAnchor = new ContactAnchor(
 2533                FixedSegment.GetSurfaceAnchorOnCenteredCapsule(
 2534                    sphereCenterAtImpact,
 2535                    capsule.Center,
 2536                    capsule.Rotation,
 2537                    Vector3d.Up,
 2538                    capsule.AxisLength,
 2539                    capsule.ScaledRadius,
 2540                    localNormal));
 2541            return true;
 42        }
 43
 718144        if (target is LSSphereCollider sphere)
 45        {
 664846            normal = Vector3d.GetDirection(sphere.Center, sphereCenterAtImpact);
 664847            if (normal == Vector3d.Zero)
 348                normal = fallbackNormal;
 664849            targetAnchor = new ContactAnchor(
 664850                FixedSegment.GetCenteredCapsuleSupportAnchor(
 664851                    sphere.Center,
 664852                    sphere.Rotation,
 664853                    Fixed64.Zero,
 664854                    sphere.ScaledRadius,
 664855                    normal));
 664856            return true;
 57        }
 58
 53359        if (target is LSCylinderCollider cylinder)
 60        {
 2361            if (!FixedSegment.TryGetClosestCenteredFiniteCylinderSurfaceAnchor(
 2362                    sphereCenterAtImpact,
 2363                    cylinder.Center,
 2364                    cylinder.Rotation,
 2365                    Vector3d.Up,
 2366                    cylinder.Height,
 2367                    cylinder.ScaledRadius,
 2368                    Vector3d.Right,
 2369                    out FixedPointAnchor targetPoint,
 2370                    out Vector3d outwardNormal,
 2371                    out Fixed64 signedDistance))
 72            {
 173                targetAnchor = default;
 174                normal = default;
 175                return false;
 76            }
 77
 2278            targetAnchor = new ContactAnchor(targetPoint);
 2279            normal = signedDistance < Fixed64.Zero ? -outwardNormal : outwardNormal;
 2280            return true;
 81        }
 82
 51083        if (target is LSConeCollider cone)
 84        {
 1585            if (!FixedSegment.TryGetClosestCenteredFiniteConeSurfaceAnchor(
 1586                    sphereCenterAtImpact,
 1587                    cone.Center,
 1588                    cone.Rotation,
 1589                    Vector3d.Up,
 1590                    cone.Height,
 1591                    cone.ScaledRadius,
 1592                    Vector3d.Right,
 1593                    out FixedPointAnchor targetPoint,
 1594                    out Vector3d outwardNormal,
 1595                    out Fixed64 signedDistance))
 96            {
 197                targetAnchor = default;
 198                normal = default;
 199                return false;
 100            }
 101
 14102            targetAnchor = new ContactAnchor(targetPoint);
 14103            normal = signedDistance < Fixed64.Zero ? -outwardNormal : outwardNormal;
 14104            return true;
 105        }
 106
 495107        if (target is LSCuboidCollider cuboid)
 108        {
 430109            FixedPointAnchor targetPoint =
 430110                cuboid.OrientedBox.GetClosestPointAnchor(sphereCenterAtImpact);
 430111            var sphereCenter = new FixedPointAnchor(
 430112                sphereCenterAtImpact,
 430113                FixedQuaternion.Identity,
 430114                Vector3d.Zero);
 430115            normal = sphereCenter.TryGetOffsetFrom(
 430116                    targetPoint,
 430117                    out Vector3d fromSurface)
 430118                && fromSurface.MagnitudeSquared > Fixed64.Epsilon
 430119                    ? fromSurface.Normalized
 430120                    : cuboid.GetNormalAtPoint(sphereCenterAtImpact);
 430121            targetAnchor = new ContactAnchor(targetPoint);
 430122            return true;
 123        }
 124
 65125        if (target is LSMeshCollider mesh)
 126        {
 49127            mesh.FindClosestPointAnchor(
 49128                sphereCenterAtImpact,
 49129                out FixedPointAnchor meshPoint,
 49130                out normal);
 49131            if (Vector3d.Dot(normal, direction) > Fixed64.Zero)
 4132                normal = -normal;
 49133            targetAnchor = new ContactAnchor(meshPoint);
 49134            return true;
 135        }
 136
 16137        Vector3d point = target.ClosestPointOnSurface(sphereCenterAtImpact);
 16138        normal = ResolveLegacyNormal(target, point, sphereCenterAtImpact, direction);
 16139        var worldPoint = new FixedPointAnchor(
 16140            point,
 16141            FixedQuaternion.Identity,
 16142            Vector3d.Zero);
 16143        if (worldPoint.TryGetLocalPointIn(
 16144                target.Center,
 16145                target.Rotation,
 16146                out Vector3d localPoint))
 147        {
 13148            targetAnchor = new ContactAnchor(
 13149                target.Center,
 13150                target.Rotation,
 13151                localPoint);
 13152            return true;
 153        }
 154
 3155        targetAnchor = default;
 3156        normal = default;
 3157        return false;
 158    }
 159
 160    private static Vector3d ResolveLegacyNormal(
 161        LSCollider target,
 162        Vector3d point,
 163        Vector3d sphereCenterAtImpact,
 164        Vector3d direction)
 165    {
 16166        Vector3d fromPointToSphereCenter = sphereCenterAtImpact - point;
 16167        Vector3d normal = target.GetNormalAtPoint(point);
 16168        if (normal.MagnitudeSquared > Fixed64.Epsilon)
 12169            return normal.Normalized;
 170
 4171        if (fromPointToSphereCenter.MagnitudeSquared > Fixed64.Epsilon)
 1172            return fromPointToSphereCenter.Normalized;
 173
 3174        return direction.MagnitudeSquared > Fixed64.Epsilon
 3175            ? -direction.Normalized
 3176            : Vector3d.Zero;
 177    }
 178}