< Summary

Information
Class: FixedMathSharp.Geometry.CylinderPairAnalyticFeatures
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/CylinderPairAnalyticFeatures.cs
Line coverage
100%
Covered lines: 68
Uncovered lines: 0
Coverable lines: 68
Total lines: 119
Line coverage: 100%
Branch coverage
100%
Covered branches: 24
Total branches: 24
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Build(...)100%1616100%
WriteNormal(...)100%66100%
Axis(...)100%11100%
Abs(...)100%22100%
IsZero(...)100%11100%
Slot(...)100%11100%
Import(...)100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/CylinderPairAnalyticFeatures.cs

#LineLine coverage
 1//=======================================================================
 2// CylinderPairAnalyticFeatures.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <summary>
 13/// Cap-axis and common-perpendicular cylinder support candidates, represented
 14/// in the shared exact raw-gap and world-normal contact format.
 15/// </summary>
 16internal static class CylinderPairAnalyticFeatures
 17{
 18    private const int Words = ConvexContactCandidate.Words;
 19
 20    /// <summary>
 21    /// Builds axis 0 (first cap), 1 (second cap), or 2 (axis cross product)
 22    /// for a nonparallel positive-radius pair. Storage is borrowed by
 23    /// ConvexContactCandidate; its shared ranking and rounding need no radius offset.
 24    /// </summary>
 25    internal static void Build(in CylinderPairGeometry geometry, int axisIndex,
 26        Span<ulong> values, Span<int> signs, out int gapSign)
 27    {
 28        System.Diagnostics.Debug.Assert((uint)axisIndex <= 2);
 116029        WideAxis3 direction = axisIndex == 0 ? geometry.FirstAxis
 116030            : axisIndex == 1 ? geometry.SecondAxis : WideAxis3.Cross(geometry.FirstAxis, geometry.SecondAxis);
 31        System.Diagnostics.Debug.Assert(!direction.IsZero);
 116032        values.Clear();
 116033        signs.Clear();
 116034        Signed576 center = WideAxis3.Dot(geometry.CenterDifference, direction);
 116035        Signed576 rational = WideArithmetic.SubtractSigned576(
 116036            WideArithmetic.AddSigned576(Abs(WideAxis3.Dot(geometry.FirstHalf, direction)),
 116037                Abs(WideAxis3.Dot(geometry.SecondHalf, direction))), Abs(center));
 116038        WriteNormal(geometry.WorldBasis, direction, center.Sign < 0 ? -1 : 1, values, signs);
 39
 40        // |a_i|<2^33, |b_i|<2^163, |(a x b)_i|<2^197. The cap
 41        // rational terms are <2^264/<2^394, and the cross term <2^428.
 42        // Thus A<2^857. Coupled (B,C) bounds are (491,724), (621,464),
 43        // (656,396) bits respectively. D, including RawScale², is <2^724.
 44        // All construction intermediates fit the existing 2560-bit slots.
 116045        Span<ulong> work = stackalloc ulong[9 * Words];
 116046        Span<ulong> delta = Slot(work, 0);
 116047        Span<ulong> squaredDirection = Slot(work, 1);
 116048        Span<ulong> projection = Slot(work, 2);
 116049        Span<ulong> rationalMagnitude = Slot(work, 3);
 116050        Span<ulong> radius = Slot(work, 4);
 116051        Span<ulong> rationalSquaredDelta = Slot(work, 5);
 116052        Span<ulong> radiusSquaredProjection = Slot(work, 6);
 116053        Span<ulong> temporary = Slot(work, 7);
 116054        Span<ulong> product = Slot(work, 8);
 116055        Import(direction.SquaredLength, squaredDirection);
 116056        if (axisIndex == 2)
 57        {
 35358            delta.Clear();
 35359            delta[0] = 1;
 35360            squaredDirection.CopyTo(projection);
 35361            Import(Signed576.ExtendValue(WideArithmetic.AddSigned320(geometry.FirstRadius, geometry.SecondRadius)), radi
 62        }
 63        else
 64        {
 80765            WideAxis3 opposite = axisIndex == 0 ? geometry.SecondAxis : geometry.FirstAxis;
 80766            Import(opposite.SquaredLength, delta);
 80767            WideArithmetic.MultiplyMagnitudes(delta, squaredDirection, product);
 80768            Import(WideAxis3.Dot(opposite, direction), temporary);
 80769            WideArithmetic.MultiplyMagnitudes(temporary, temporary, projection);
 80770            WideArithmetic.SubtractEqualMagnitudes(product, projection, projection);
 80771            Import(Signed576.ExtendValue(axisIndex == 0 ? geometry.SecondRadius : geometry.FirstRadius), radius);
 72        }
 73
 116074        Import(rational, rationalMagnitude);
 116075        WideArithmetic.MultiplyMagnitudes(rationalMagnitude, rationalMagnitude, temporary);
 116076        WideArithmetic.MultiplyMagnitudes(temporary, delta, rationalSquaredDelta);
 116077        WideArithmetic.MultiplyMagnitudes(radius, radius, temporary);
 116078        WideArithmetic.MultiplyMagnitudes(temporary, projection, radiusSquaredProjection);
 116079        WideArithmetic.AddEqualMagnitudes(rationalSquaredDelta, radiusSquaredProjection, Slot(values, 7));
 116080        WideArithmetic.MultiplyMagnitudes(rationalMagnitude, radius, temporary);
 116081        WideArithmetic.AddEqualMagnitudes(temporary, temporary, Slot(values, 8));
 116082        WideArithmetic.MultiplyMagnitudes(projection, delta, Slot(values, 9));
 116083        Import(Signed576.ExtendValue(Signed320.ExtendValue(geometry.RawScale)), rationalMagnitude);
 116084        WideArithmetic.MultiplyMagnitudes(rationalMagnitude, rationalMagnitude, temporary);
 116085        WideArithmetic.MultiplyMagnitudes(delta, squaredDirection, product);
 116086        WideArithmetic.MultiplyMagnitudes(temporary, product, Slot(values, 10));
 116087        signs[7] = 1;
 116088        signs[8] = IsZero(Slot(values, 8)) ? 0 : rational.Sign;
 116089        signs[9] = 1;
 116090        signs[10] = 1;
 116091        gapSign = rational.Sign < 0
 116092            ? WideArithmetic.CompareMagnitudeEqualLength(radiusSquaredProjection, rationalSquaredDelta)
 116093            : 1; // Every family has strictly positive radial support.
 116094    }
 95
 96    private static void WriteNormal(in WideRationalBasis3d basis, WideAxis3 direction,
 97        int orientation, Span<ulong> values, Span<int> signs)
 98    {
 928099        for (int component = 0; component < 3; component++)
 100        {
 3480101            WideAxis3 row = component == 0 ? Axis(basis.Xx, basis.Yx, basis.Zx)
 3480102                : component == 1 ? Axis(basis.Xy, basis.Yy, basis.Zy) : Axis(basis.Xz, basis.Yz, basis.Zz);
 3480103            Signed576 value = WideAxis3.Dot(direction, row);
 3480104            Import(value, Slot(values, component));
 3480105            signs[component] = orientation * value.Sign;
 106        }
 1160107    }
 108
 3480109    private static WideAxis3 Axis(Signed192 x, Signed192 y, Signed192 z) => new(
 3480110        Signed320.ExtendValue(x), Signed320.ExtendValue(y), Signed320.ExtendValue(z));
 3480111    private static Signed576 Abs(Signed576 value) => value.Sign < 0 ? WideArithmetic.SubtractSigned576(default, value) :
 1160112    private static bool IsZero(ReadOnlySpan<ulong> value) => WideArithmetic.GetActiveMagnitudeLength(value) == 0;
 19720113    private static Span<ulong> Slot(Span<ulong> values, int slot) => values.Slice(slot * Words, Words);
 114    private static void Import(Signed576 value, Span<ulong> magnitude)
 115    {
 9734116        magnitude.Clear();
 9734117        WideArithmetic.GetMagnitude(value, magnitude[..9]);
 9734118    }
 119}