< Summary

Information
Class: Gravitas.Colliders.LSConeCollider
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/3D/LSConeCollider.cs
Line coverage
100%
Covered lines: 138
Uncovered lines: 0
Coverable lines: 138
Total lines: 258
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/3D/LSConeCollider.cs

#LineLine coverage
 1//=======================================================================
 2// LSConeCollider.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.Queries;
 11using SwiftCollections;
 12using System;
 13
 14namespace Gravitas.Colliders;
 15
 16/// <summary>
 17/// Represents a finite circular cone whose local Y axis runs from base plane
 18/// to apex and whose local origin is the cone's bounding center.
 19/// </summary>
 20public sealed class LSConeCollider : LSCollider
 21{
 52422    private Fixed64 _scaledRadius = Fixed64.Half;
 23    private Fixed64 _preparedRadius;
 24    private Fixed64 _preparedHeight;
 25    private Vector3d _preparedAxis;
 26    private Fixed64 _preparedVolume;
 27
 28    /// <summary>Creates a cone collider with default dimensions.</summary>
 98229    public LSConeCollider() { }
 30
 31    /// <summary>Creates a runtime cone from an authored shape definition.</summary>
 3332    public LSConeCollider(ColliderShapeDefinition definition)
 33    {
 3334        definition.EnsureKind(ColliderShapeDefinitionKind.Cone);
 3335        Material = definition.Material;
 3336        Radius = definition.Radius;
 3337        Size = definition.Size;
 3338    }
 39
 40    /// <inheritdoc/>
 60641    public override ColliderType Shape => ColliderType.Cone;
 42
 43    /// <inheritdoc/>
 6744    public override int Priority => ColliderSettings.GetPriority(Shape);
 45
 46    /// <inheritdoc/>
 555547    public override Fixed64 ScaledRadius => _scaledRadius;
 48
 49    /// <summary>
 50    /// Gets the derived normalized world-space base-to-apex axis. Exact
 51    /// geometry remains authoritative in the collider's rigid frame.
 52    /// </summary>
 53    public Vector3d WorldAxis { get; private set; } = Vector3d.Up;
 54
 55    /// <summary>
 56    /// Gets the full physical distance from the cone's base plane to its apex.
 57    /// </summary>
 58    public Fixed64 Height { get; private set; }
 59
 60    /// <summary>
 61    /// Gets the solid cone volume used for mass weighting and diagnostics.
 62    /// </summary>
 63    public Fixed64 Volume { get; private set; }
 64
 65    /// <inheritdoc/>
 66    protected override void OnRadiusChanged()
 67    {
 2268        Fixed64 diameter = _radius * 2;
 2269        _size = new Vector3d(diameter, _size.Y, diameter);
 2270    }
 71
 72    /// <inheritdoc/>
 73    protected override Vector3d NormalizeSize(Vector3d value) =>
 49374        new(_radius * 2, value.Y, _radius * 2);
 75
 76    private protected override void PrepareShape(in ColliderShapeSnapshot snapshot)
 77    {
 53478        Fixed64 radiusX = ColliderScalePolicy.ScalePositive(
 53479            snapshot.Radius,
 53480            snapshot.OwnerScale.X,
 53481            snapshot.PartScale.X);
 53482        Fixed64 radiusZ = ColliderScalePolicy.ScalePositive(
 53483            snapshot.Radius,
 53484            snapshot.OwnerScale.Z,
 53485            snapshot.PartScale.Z);
 53486        _preparedRadius = FixedMath.Max(radiusX, radiusZ);
 53487        _preparedHeight = ColliderScalePolicy.ScalePositive(
 53488            snapshot.Size.Y,
 53489            snapshot.OwnerScale.Y,
 53490            snapshot.PartScale.Y);
 53491        _preparedAxis = (snapshot.Rotation * Vector3d.Up).Normalized;
 53492        _preparedVolume = Fixed64.Pi * _preparedRadius * _preparedRadius
 53493            * _preparedHeight / (Fixed64)3;
 53494        if (CompoundOwner == null
 53495            && !CalculatePreparedLocalMassPoint().TryGetPoint(out _))
 96        {
 197            throw new InvalidOperationException(
 198                "Prepared collider mass-property point is outside the Fixed64 coordinate domain.");
 99        }
 533100        SetPreparedBounds(FixedBoundBox.FromCenteredFiniteConeClippedToDomain(
 533101            snapshot.Center,
 533102            snapshot.Rotation,
 533103            Vector3d.Up,
 533104            _preparedHeight,
 533105            _preparedRadius));
 533106    }
 107
 108    private protected override void PublishShape()
 109    {
 530110        _scaledRadius = _preparedRadius;
 530111        Height = _preparedHeight;
 530112        WorldAxis = _preparedAxis;
 530113        Volume = _preparedVolume;
 530114        Area = _preparedVolume;
 530115    }
 116
 117    internal override ExactMassWeight CalculateMassPropertyWeight()
 118    {
 60119        Fixed64 radius = HasCommittedShape
 60120            ? ScaledRadius
 60121            : GetCurrentScaledRadius();
 60122        Fixed64 height = HasCommittedShape
 60123            ? Height
 60124            : GetCurrentHeight();
 60125        return ExactMassWeight.FromProduct(
 60126            Fixed64.Pi / (Fixed64)3,
 60127            radius,
 60128            radius,
 60129            height);
 130    }
 131
 132    internal override ExactMassWeight CalculatePreparedMassPropertyWeight() =>
 73133        ExactMassWeight.FromProduct(
 73134            Fixed64.Pi / (Fixed64)3,
 73135            _preparedRadius,
 73136            _preparedRadius,
 73137            _preparedHeight);
 138
 139    internal override ExactMassPoint3D CalculateLocalMassPoint() =>
 1015140        TransformRelativeMassPropertyPointExact(
 1015141            GetLocalCenterOfMass(
 1015142                HasCommittedShape
 1015143                    ? Height
 1015144                    : GetCurrentHeight()));
 145
 146    internal override ExactMassPoint3D CalculatePreparedLocalMassPoint() =>
 1040147        TransformPreparedRelativeMassPropertyPointExact(
 1040148            GetLocalCenterOfMass(_preparedHeight));
 149
 150    private Fixed64 GetCurrentHeight()
 151    {
 2152        GetCurrentShapeScales(
 2153            out Vector3d ownerScale,
 2154            out Vector3d partScale);
 2155        return ColliderScalePolicy.ScalePositive(
 2156            Size.Y,
 2157            ownerScale.Y,
 2158            partScale.Y);
 159    }
 160
 161    private static Vector3d GetLocalCenterOfMass(Fixed64 height) =>
 2055162        new(
 2055163            Fixed64.Zero,
 2055164            -height * Fixed64.FromFraction(1, 4),
 2055165            Fixed64.Zero);
 166
 167    internal override Fixed3x3 CalculateCenterOfMassInertiaTensor(Fixed64 mass)
 168    {
 484169        Fixed64 radiusSqr = ScaledRadiusSqr;
 484170        Fixed64 heightSqr = Height * Height;
 484171        Fixed64 inertiaXZ = mass
 484172            * ((Fixed64.FromFraction(3, 20) * radiusSqr)
 484173                + (Fixed64.FromFraction(3, 80) * heightSqr));
 484174        Fixed64 inertiaY = Fixed64.FromFraction(3, 10) * mass * radiusSqr;
 175
 484176        Fixed3x3 tensor = new(
 484177            inertiaXZ, Fixed64.Zero, Fixed64.Zero,
 484178            Fixed64.Zero, inertiaY, Fixed64.Zero,
 484179            Fixed64.Zero, Fixed64.Zero, inertiaXZ);
 484180        return tensor;
 181    }
 182
 183    /// <inheritdoc/>
 184    public override Fixed64 GetFrontalArea(Vector3d direction)
 185    {
 4186        Fixed64 directionMagnitude = direction.Magnitude;
 4187        if (directionMagnitude <= Fixed64.Epsilon)
 1188            return Area;
 189
 3190        Vector3d normalizedDirection = direction / directionMagnitude;
 3191        Fixed64 axial = Rotation.Inverse()
 3192            .Rotate(normalizedDirection).Y.Abs();
 3193        Fixed64 radialFactorSqr = Fixed64.One - axial * axial;
 3194        Fixed64 radialFactor = radialFactorSqr <= Fixed64.Zero ? Fixed64.Zero : FixedMath.Sqrt(radialFactorSqr);
 195
 3196        Fixed64 baseArea = Fixed64.Pi * ScaledRadiusSqr;
 3197        Fixed64 triangularProfile = ScaledRadius * Height;
 3198        return axial * baseArea + radialFactor * triangularProfile;
 199    }
 200
 201    /// <inheritdoc/>
 202    public override Vector3d ClosestPointOnSurface(Vector3d other)
 203    {
 85204        FixedPointAnchor surfaceAnchor =
 85205            GetClosestSurfaceAnchor(other, out _);
 85206        if (!surfaceAnchor.TryGetPoint(out Vector3d surfacePoint))
 207        {
 1208            throw new InvalidOperationException(
 1209                "The closest cone surface point is outside the representable coordinate domain.");
 210        }
 211
 84212        return surfacePoint;
 213    }
 214
 215    /// <inheritdoc/>
 216    public override Vector3d GetNormalAtPoint(Vector3d point)
 217    {
 489218        _ = GetClosestSurfaceAnchor(
 489219            point,
 489220            out Vector3d outwardNormal);
 489221        return outwardNormal;
 222    }
 223
 224    internal override FixedPointAnchor GetClosestSurfaceAnchor(
 225        Vector3d point,
 226        out Vector3d normal) =>
 787227        WideFiniteAxisIntersection
 787228            .GetClosestCenteredFiniteConeSurfaceAnchor(
 787229                point,
 787230                Center,
 787231                Rotation,
 787232                Vector3d.Up,
 787233                Height,
 787234                ScaledRadius,
 787235                Vector3d.Right,
 787236                out normal,
 787237                out _);
 238
 239    /// <inheritdoc/>
 240    public override bool ColliderOverlapsRay(RaycastSegmentWorker worker, ref SwiftList<Vector3d> outputIntersectionPoin
 477241        worker.CheckConeOverlaps(this, ref outputIntersectionPoints);
 242
 243    internal bool ContainsWorldPoint(
 244        Vector3d point,
 245        Fixed64 tolerance = default) =>
 6246        FixedSegment.TryGetClosestCenteredFiniteConeSurfaceAnchor(
 6247            point,
 6248            Center,
 6249            Rotation,
 6250            Vector3d.Up,
 6251            Height,
 6252            ScaledRadius,
 6253            Vector3d.Right,
 6254            out _,
 6255            out _,
 6256            out Fixed64 signedDistance)
 6257        && signedDistance <= tolerance;
 258}