< Summary

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
BuildValues(...)100%11100%
GetSimpleValueSlopeSign(...)100%22100%
TryAdmitSimple(...)100%66100%
GetSimpleNormal(...)100%66100%
GetDerivativeSign(...)100%66100%
BuildDerivative(...)100%11100%
WriteDerivativeInvariant(...)100%11100%
Axis(...)100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// CylinderPairRimFeatures.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;
 9using System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <summary>
 14/// Exact admission and materialization of cylinder rim stationary values.
 15/// Construction scratch is released before any algebraic-root evaluation.
 16/// </summary>
 17internal static class CylinderPairRimFeatures
 18{
 19    // In the primitive first-rotation frame, weighted coefficient heights
 20    // are <7372 bits (value), <7376 bits (cap-direction derivative), and
 21    // <7212 bits (world-direction derivative), after S=2^ValueShift*t.
 22    internal const int CoefficientWords = 116;
 23    private const int Count = CylinderPairValuePolynomial.CoefficientCount;
 24    internal const int AdmissionCoefficientCount = 4 * Count;
 25
 26    [MethodImpl(MethodImplOptions.NoInlining)]
 27    internal static int BuildValues(in CylinderPairGeometry geometry, int firstSign, int secondSign,
 28        Span<ulong> coefficients, Span<sbyte> signs)
 29    {
 126930        Span<ulong> invariants = stackalloc ulong[CylinderPairValuePolynomial.InvariantCount * CoefficientWords];
 126931        Span<sbyte> invariantSigns = stackalloc sbyte[CylinderPairValuePolynomial.InvariantCount];
 126932        Span<ulong> scratch = stackalloc ulong[CylinderPairValuePolynomial.ScratchCoefficientCount * CoefficientWords];
 126933        Span<sbyte> scratchSigns = stackalloc sbyte[CylinderPairValuePolynomial.ScratchCoefficientCount];
 126934        geometry.WriteInvariants(firstSign, secondSign, invariants, invariantSigns, CoefficientWords);
 126935        int degree = CylinderPairValuePolynomial.Build(invariants, invariantSigns, CoefficientWords,
 126936            coefficients, signs, scratch, scratchSigns);
 126937        WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(coefficients, Count, geometry.ValueShift);
 126938        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(coefficients, signs);
 126939        return degree;
 40    }
 41
 42    /// <summary>
 43    /// Returns the derivative sign after the caller has excluded repeated roots.
 44    /// Isolation guarantees nonroot cell endpoints, so a simple crossing has
 45    /// the opposite sign to its lower endpoint. Exact dyadic roots use F' directly.
 46    /// </summary>
 47    internal static int GetSimpleValueSlopeSign(FiniteAxisValueRoot root) =>
 438248        (root.IsRational ? 1 : -1) * WideFiniteAxisIntersection.EvaluateFiniteRootPolynomial(
 438249            root.Coefficients, root.Signs, root.LowerNumerator, root.DenominatorShift,
 438250            root.IsRational ? 1 : 0);
 51
 52    /// <summary>
 53    /// Reuses four caller-owned directional polynomials across this cap pair's
 54    /// simple roots. Reset readyMask to zero whenever geometry or cap signs change.
 55    /// Query storage has AdmissionCoefficientCount equal-width slots and signs.
 56    /// </summary>
 57    internal static bool TryAdmitSimple(in CylinderPairGeometry geometry, int firstSign, int secondSign,
 58        ref FiniteAxisValueRoot root, int valueSlopeSign, scoped Span<ulong> derivatives,
 59        scoped Span<sbyte> derivativeSigns, ref int readyMask, out int gapSign)
 60    {
 61        // For a simple value root, v=-W_c/(2W_S), p.v=-rho*W_rho/W_S,
 62        // q.v=-tau*W_tau/W_S. Both radii must choose the same support side.
 439563        int firstRadial = GetDerivativeSign(geometry, firstSign, secondSign, ref root,
 439564            valueSlopeSign, 0, derivatives, derivativeSigns, ref readyMask);
 439565        gapSign = -firstRadial * valueSlopeSign;
 66        // Zero radial support is inadmissible, regardless of the other rim
 67        // or cap signs; do not construct their polynomials for this boundary.
 439568        if (firstRadial == 0)
 669            return false;
 438970        int secondRadial = GetDerivativeSign(geometry, firstSign, secondSign, ref root,
 438971            valueSlopeSign, 1, derivatives, derivativeSigns, ref readyMask);
 438972        if (firstRadial != secondRadial)
 178673            return false;
 260374        int firstCap = GetDerivativeSign(geometry, firstSign, secondSign, ref root,
 260375            valueSlopeSign, 2, derivatives, derivativeSigns, ref readyMask);
 260376        if (firstRadial * firstSign * firstCap < 0)
 116377            return false;
 144078        int secondCap = GetDerivativeSign(geometry, firstSign, secondSign, ref root,
 144079            valueSlopeSign, 3, derivatives, derivativeSigns, ref readyMask);
 144080        return firstRadial * secondSign * secondCap >= 0;
 81    }
 82
 83    internal static Vector3d GetSimpleNormal(in CylinderPairGeometry geometry, int firstSign, int secondSign,
 84        ref FiniteAxisValueRoot root, int valueSlopeSign, int gapSign)
 85    {
 14286        Span<ulong> gradient = stackalloc ulong[3 * Count * CoefficientWords];
 14287        Span<sbyte> gradientSigns = stackalloc sbyte[3 * Count];
 14288        WideRationalBasis3d basis = geometry.WorldBasis;
 113689        for (int axis = 0; axis < 3; axis++)
 90        {
 91            // Rows of the rotation map the first-frame gradient to world
 92            // coordinates. A shared positive denominator cancels on normalization.
 42693            WideAxis3 direction = axis == 0
 42694                ? Axis(basis.Xx, basis.Yx, basis.Zx)
 42695                : axis == 1 ? Axis(basis.Xy, basis.Yy, basis.Zy) : Axis(basis.Xz, basis.Yz, basis.Zz);
 42696            BuildDerivative(geometry, firstSign, secondSign, 0, 0, direction,
 42697                gradient.Slice(axis * Count * CoefficientWords, Count * CoefficientWords),
 42698                gradientSigns.Slice(axis * Count, Count));
 99        }
 100        // All three derivatives retain the same raw polynomial factor. Remove
 101        // only common content, never an independent scale per component.
 142102        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(gradient, gradientSigns);
 142103        return ConvexContactValueRoot.GetNormalizedDirection(ref root, gradient, gradientSigns, -gapSign * valueSlopeSig
 104    }
 105
 106    private static int GetDerivativeSign(in CylinderPairGeometry geometry, int firstSign, int secondSign,
 107        ref FiniteAxisValueRoot root, int valueSlopeSign, int query, scoped Span<ulong> derivatives,
 108        scoped Span<sbyte> derivativeSigns, ref int readyMask)
 109    {
 12827110        Span<ulong> derivative = derivatives.Slice(query * Count * CoefficientWords, Count * CoefficientWords);
 12827111        Span<sbyte> signs = derivativeSigns.Slice(query * Count, Count);
 12827112        int bit = 1 << query;
 12827113        if ((readyMask & bit) == 0)
 114        {
 4690115            WideAxis3 direction = query == 2 ? geometry.FirstHalf : query == 3 ? geometry.SecondHalf : default;
 4690116            BuildDerivative(geometry, firstSign, secondSign, query == 0 ? 1 : 0, query == 1 ? 1 : 0,
 4690117                direction, derivative, signs);
 118            // These independent positive scales preserve signs. World-gradient
 119            // components still use one common scale in GetSimpleNormal.
 4690120            WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(derivative, signs);
 4690121            readyMask |= bit;
 122        }
 123        // Isolation exports nonzero endpoints. A simple root has lower sign
 124        // -slope in that cell and every nonrational descendant. Reuse that
 125        // proof instead of reevaluating both defining endpoints;
 126        // the ordinary refinement certificates and exact fallbacks still apply.
 12827127        return WideFiniteAxisIntersection.GetSignAtFiniteValueRootAndRefine(ref root, derivative, signs, -valueSlopeSign
 128    }
 129
 130    [MethodImpl(MethodImplOptions.NoInlining)]
 131    private static void BuildDerivative(in CylinderPairGeometry geometry, int firstSign, int secondSign,
 132        int rhoDirection, int tauDirection, WideAxis3 direction, Span<ulong> coefficients, Span<sbyte> signs)
 133    {
 5116134        Span<ulong> invariants = stackalloc ulong[CylinderPairValuePolynomial.InvariantCount * CoefficientWords];
 5116135        Span<sbyte> invariantSigns = stackalloc sbyte[CylinderPairValuePolynomial.InvariantCount];
 5116136        Span<ulong> values = stackalloc ulong[Count * CoefficientWords];
 5116137        Span<sbyte> valueSigns = stackalloc sbyte[Count];
 5116138        Span<ulong> scratch = stackalloc ulong[CylinderPairValuePolynomial.ScratchCoefficientCount * CoefficientWords];
 5116139        Span<sbyte> scratchSigns = stackalloc sbyte[CylinderPairValuePolynomial.ScratchCoefficientCount];
 5116140        Span<ulong> derivativeInvariants = stackalloc ulong[CylinderPairValueDerivative.InvariantCount * CoefficientWord
 5116141        Span<sbyte> derivativeInvariantSigns = stackalloc sbyte[CylinderPairValueDerivative.InvariantCount];
 5116142        Span<ulong> derivativeScratch = stackalloc ulong[CylinderPairValueDerivative.ScratchCoefficientCount * Coefficie
 5116143        Span<sbyte> derivativeScratchSigns = stackalloc sbyte[CylinderPairValueDerivative.ScratchCoefficientCount];
 5116144        geometry.WriteInvariants(firstSign, secondSign, invariants, invariantSigns, CoefficientWords);
 5116145        derivativeInvariants.Clear();
 5116146        derivativeInvariantSigns.Clear();
 5116147        derivativeInvariants[0] = (ulong)rhoDirection;
 5116148        derivativeInvariantSigns[0] = (sbyte)rhoDirection;
 5116149        derivativeInvariants[CoefficientWords] = (ulong)tauDirection;
 5116150        derivativeInvariantSigns[1] = (sbyte)tauDirection;
 5116151        Signed576 center = WideAxis3.Dot(geometry.GetCapOffset(firstSign, secondSign), direction);
 5116152        WriteDerivativeInvariant(WideArithmetic.AddSigned576(center, center), derivativeInvariants, derivativeInvariantS
 5116153        WriteDerivativeInvariant(WideAxis3.Dot(geometry.PlaneU, direction), derivativeInvariants, derivativeInvariantSig
 5116154        WriteDerivativeInvariant(WideAxis3.Dot(geometry.PlaneW, direction), derivativeInvariants, derivativeInvariantSig
 5116155        WriteDerivativeInvariant(WideAxis3.Dot(geometry.SecondAxis, direction), derivativeInvariants, derivativeInvarian
 5116156        var derivative = new CylinderPairValueDerivative(derivativeInvariants, derivativeInvariantSigns,
 5116157            coefficients, signs, derivativeScratch, derivativeScratchSigns);
 5116158        CylinderPairValuePolynomial.Build(invariants, invariantSigns, CoefficientWords,
 5116159            values, valueSigns, scratch, scratchSigns, derivative);
 5116160        WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(coefficients, Count, geometry.ValueShift);
 5116161    }
 162
 163    private static void WriteDerivativeInvariant(Signed576 value, Span<ulong> values, Span<sbyte> signs, int index)
 164    {
 20464165        WideArithmetic.GetMagnitude(value, values.Slice(index * CoefficientWords, 9));
 20464166        signs[index] = (sbyte)value.Sign;
 20464167    }
 168
 426169    private static WideAxis3 Axis(Signed192 x, Signed192 y, Signed192 z) => new(
 426170        Signed320.ExtendValue(x), Signed320.ExtendValue(y), Signed320.ExtendValue(z));
 171
 172}