< Summary

Information
Class: FixedMathSharp.Geometry.TriangleConeRimContacts
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Triangles/TriangleConeRimContacts.cs
Line coverage
100%
Covered lines: 141
Uncovered lines: 0
Coverable lines: 141
Total lines: 255
Line coverage: 100%
Branch coverage
100%
Covered branches: 66
Total branches: 66
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
TryGetContact(...)100%44100%
TryKeepContacts(...)100%2222100%
GetContactMaterials(...)100%22100%
TryChart(...)100%3030100%
BuildConeParameter(...)100%11100%
BuildConeParameter(...)100%88100%
GetMaterials(...)100%11100%
Restore(...)100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Triangles/TriangleConeRimContacts.cs

#LineLine coverage
 1//=======================================================================
 2// TriangleConeRimContacts.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;
 10using static FixedMathSharp.Geometry.CylinderContactAlgebra;
 11using static FixedMathSharp.Geometry.CircularRimContactAlgebra;
 12
 13namespace FixedMathSharp.Geometry;
 14
 15/// <summary>Admitted stationary triangle-edge/base-rim contacts of a finite cone.</summary>
 16internal static class TriangleConeRimContacts
 17{
 18    private const int ParameterSlots = 33;
 19    internal const int ValueCellWords = (16 * (ValueWords * 64 + 11) + 255) / 64;
 20
 21    private struct Selection
 22    {
 23        internal bool HasValue, IsZero, IsRational;
 24        internal WideDirection3d First, Second;
 25        internal int Edge, ParameterOrdinal, ValueOrdinal, ValueShift;
 26    }
 27
 28    internal static bool TryGetContact(in TriangleCircularGeometry geometry, FixedTriangle triangle,
 29        Fixed64 height, Fixed64 radius, scoped ConvexContactCandidate analytic, Fixed64 analyticDepth,
 30        out bool hasBetter, out Vector3d normal, out Vector3d radialPoint,
 31        out Vector3d trianglePoint, out Fixed64 depth, out bool clamped)
 32    {
 8033        int ValueWords = geometry.ValueWords;
 8034        int ValueCellWords = (16 * (geometry.ValueWords * 64 + 11) + 255) / 64;
 16035        hasBetter = false; normal = radialPoint = trianglePoint = default;
 16036        depth = analyticDepth; clamped = false;
 8037        Span<ulong> bestValues = stackalloc ulong[5 * ValueWords];
 8038        Span<sbyte> bestSigns = stackalloc sbyte[5];
 8039        Span<ulong> bestCell = stackalloc ulong[ValueCellWords];
 8040        Selection best = default;
 8041        if (!TryKeepContacts(geometry, height, radius, analytic, analyticDepth,
 8042                bestValues, bestSigns, bestCell, ref best))
 143            return false;
 7944        if (!best.HasValue)
 6445            return true;
 1546        hasBetter = true;
 1547        GetContactMaterials(geometry, triangle, height, radius, bestValues, bestSigns, bestCell, best,
 1548            out normal, out radialPoint, out trianglePoint, out depth, out clamped);
 1549        return true;
 50    }
 51
 52    private static bool TryKeepContacts(in TriangleCircularGeometry geometry,
 53        Fixed64 height, Fixed64 radius, scoped ConvexContactCandidate analytic, Fixed64 analyticDepth,
 54        scoped Span<ulong> bestValues, scoped Span<sbyte> bestSigns, scoped Span<ulong> bestCell,
 55        ref Selection best)
 56    {
 57
 58        // The caller's exact face-width proof excludes a clamped analytic gap.
 59        // Its rounded value d bounds the original gap by d+1/2 raw, including Max.
 8060        ulong upperTwiceRaw = ((ulong)analyticDepth.m_rawValue << 1) | 1UL;
 63461        for (int edge = 0; edge < 3; edge++)
 62        {
 23863            WideDirection3d e = geometry.Edge(edge);
 64            // Horizontal edges are meridional; vertical edges are circular.
 65            // Their complete boundaries/principal radial directions are analytic.
 23866            if (e.Y.IsZero || e.X.IsZero && e.Z.IsZero)
 67                continue;
 20768            GetBasis(e, out WideDirection3d u, out WideDirection3d v);
 123869            for (int firstSign = -1; firstSign <= 1; firstSign += 2)
 247670                for (int secondSign = -1; secondSign <= 1; secondSign += 2)
 495271                    for (int chart = 0; chart < 2; chart++)
 72                    {
 165173                        WideDirection3d first = firstSign > 0 ? u : -u;
 165174                        WideDirection3d second = secondSign > 0 ? v : -v;
 247675                        if (chart != 0) (first, second) = (second, first);
 165176                        if (!TryChart(geometry, height, radius, edge, first, second,
 165177                                analytic, upperTwiceRaw, bestValues, bestSigns, bestCell, ref best))
 178                            return false;
 79                    }
 80        }
 7981        return true;
 82    }
 83
 84    private static void GetContactMaterials(in TriangleCircularGeometry geometry, FixedTriangle triangle,
 85        Fixed64 height, Fixed64 radius, ReadOnlySpan<ulong> bestValues, ReadOnlySpan<sbyte> bestSigns,
 86        Span<ulong> bestCell, in Selection best, out Vector3d normal, out Vector3d radialPoint,
 87        out Vector3d trianglePoint, out Fixed64 depth, out bool clamped)
 88    {
 89
 1590        clamped = false;
 1591        GetMaterials(geometry, triangle, height, radius, best, out normal, out radialPoint, out trianglePoint);
 1592        if (best.IsZero)
 193            depth = Fixed64.Zero;
 94        else
 95        {
 1496            FiniteAxisValueRoot root = Restore(bestValues, bestSigns, bestCell, best);
 1497            ConvexContactValueRoot.GetRoundedDepth(geometry.RawScale, geometry.ValueShift,
 1498                ref root, out depth, out clamped);
 99        }
 14100    }
 101
 102    [MethodImpl(MethodImplOptions.NoInlining)]
 103    private static bool TryChart(in TriangleCircularGeometry geometry, Fixed64 height, Fixed64 radius,
 104        int edge, in WideDirection3d first, in WideDirection3d second, scoped ConvexContactCandidate analytic, ulong upp
 105        scoped Span<ulong> bestValues, scoped Span<sbyte> bestSigns, scoped Span<ulong> bestCell,
 106        ref Selection best)
 107    {
 1651108        int Words = geometry.ParameterWords;
 1651109        int ValueWords = geometry.ValueWords;
 1651110        int ValueCellWords = (16 * (geometry.ValueWords * 64 + 11) + 255) / 64;
 1651111        WideDirection3d outward = geometry.EdgeFromTo((edge + 2) % 3, edge);
 3302112        Signed832 a = WideDirection3d.Dot(first, outward), b = WideDirection3d.Dot(second, outward);
 113        // Each root needs positive outward projection a+t*b on t in (0,1].
 114        // Nonpositive endpoints exclude the complete affine chart before construction.
 1651115        if (a.Sign <= 0 && WideArithmetic.AddSigned832(a, b).Sign <= 0)
 649116            return true;
 1002117        Span<ulong> data = stackalloc ulong[ParameterSlots * Words];
 1002118        Span<sbyte> signs = stackalloc sbyte[ParameterSlots];
 1002119        BuildConeParameter(geometry, height, radius, edge, first, second, data, signs);
 1002120        Span<ulong> batchCells = stackalloc ulong[64];
 1002121        Span<int> batchShifts = stackalloc int[8];
 1002122        FiniteAxisValueRoots roots = WideFiniteAxisIntersection.GetFiniteValueRoots(
 1002123            data[..(5 * Words)], signs[..5], batchCells, batchShifts);
 1002124        Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords(
 1002125            data[..(5 * Words)], signs[..5])];
 1002126        Span<ulong> values = stackalloc ulong[5 * ValueWords];
 1002127        Span<sbyte> valueSigns = stackalloc sbyte[5];
 1002128        Span<ulong> valueCell = stackalloc ulong[ValueCellWords];
 3012129        for (int ordinal = 0; ordinal < roots.Count; ordinal++)
 130        {
 505131            scoped FiniteAxisValueRoot root = FiniteAxisValueRoots.GetRoot(roots, ordinal,
 505132                data[..(5 * Words)], signs[..5], cell);
 505133            if (Sign(ref root, data, signs, 31, 2) <= 0)
 134                continue;
 135            // For negative n.Y, squaring is equivalent only with this sign
 136            // guard. Equality belongs to the earlier lateral-generator fan.
 413137            if (Sign(ref root, data, signs, 26, 2) < 0
 413138                && Sign(ref root, data, signs, 28, 3) <= 0)
 139                continue;
 318140            int kSign = Sign(ref root, data, signs, 8, 2);
 318141            if (kSign == 0 || Sign(ref root, data, signs, 10, 3) != -kSign)
 142                continue;
 167143            int gapSign = Sign(ref root, data, signs, 13, 3) * kSign;
 167144            if (gapSign < 0)
 1145                return false;
 166146            if (best.IsZero || analytic.GapSign == 0)
 147                continue;
 156148            var candidate = new Selection
 156149            {
 156150                HasValue = true, First = first, Second = second, Edge = edge,
 156151                ParameterOrdinal = ordinal
 156152            };
 156153            if (gapSign == 0)
 154            {
 2155                candidate.IsZero = true; best = candidate;
 1156                continue;
 157            }
 158            // Positive roots at or above the analytic half-raw upper bound
 159            // cannot win. Negative/zero gaps have already retained classification.
 155160            if (ConvexContactValueRoot.CompareSquaredGapToTwiceRaw(ref root,
 155161                    data.Slice(16 * Words, 5 * Words), signs.Slice(16, 5),
 155162                    data.Slice(21 * Words, 5 * Words), signs.Slice(21, 5), upperTwiceRaw) >= 0)
 163                continue;
 164            // Materialize only roots that survive feature admission and the
 165            // analytic bound. Rebuilding is valid for any number of survivors.
 20166            BuildValues(geometry.Edge(edge), geometry.CapOffset(edge, 1), geometry.Radius, geometry.ValueShift, values, 
 20167            scoped FiniteAxisValueRoot value = ConvexContactValueRoot.MapSquaredValue(
 20168                geometry.RawScale, geometry.ValueShift, ref root,
 20169                data.Slice(16 * Words, 5 * Words), signs.Slice(16, 5),
 20170                data.Slice(21 * Words, 5 * Words), signs.Slice(21, 5), values, valueSigns, valueCell);
 20171            int comparison = best.HasValue
 20172                ? WideFiniteAxisIntersection.CompareFiniteValueRoots(value, Restore(bestValues, bestSigns, bestCell, bes
 20173                : ConvexContactValueRoot.CompareRootSquared(geometry.RawScale, geometry.ValueShift, ref value, analytic)
 20174            if (comparison >= 0)
 175                continue;
 42176            values.CopyTo(bestValues); valueSigns.CopyTo(bestSigns); valueCell.CopyTo(bestCell);
 28177            candidate.ValueOrdinal = value.Ordinal; candidate.ValueShift = value.DenominatorShift;
 28178            candidate.IsRational = value.IsRational; best = candidate;
 179        }
 1001180        return true;
 181    }
 182
 183    [MethodImpl(MethodImplOptions.NoInlining)]
 184    private static void BuildConeParameter(in TriangleCircularGeometry geometry, Fixed64 height, Fixed64 radius,
 185        int edge, in WideDirection3d first, in WideDirection3d second, Span<ulong> data, Span<sbyte> signs)
 186    {
 187
 1017188        BuildConeParameter(geometry.CapOffset(edge, 1), geometry.Radius, geometry.RawScale,
 1017189            height, radius, first, second, data, signs, useRigidBounds: !geometry.IsAffine);
 1017190        WideDirection3d outward = geometry.EdgeFromTo((edge + 2) % 3, edge);
 1017191        Write(data, signs, 31, WideDirection3d.Dot(outward, first));
 1017192        Write(data, signs, 32, WideDirection3d.Dot(outward, second));
 1017193    }
 194
 195    // Writes the shared rim stationary chart and cone support switch into
 196    // slots 0..30. Triangle-only support walls occupy the following two slots.
 197    internal static void BuildConeParameter(in WideDirection3d offset, Signed576 scaledRadius, Signed320 rawScale,
 198        Fixed64 height, Fixed64 radius, in WideDirection3d first, in WideDirection3d second,
 199        Span<ulong> data, Span<sbyte> signs, bool useRigidBounds = false)
 200    {
 1255201        int Words = data.Length / signs.Length;
 1255202        BuildParameter(offset, scaledRadius, rawScale, first, second, data, signs, useRigidBounds);
 1255203        Write(data, signs, 26, Signed832.ExtendValue(first.Y));
 1255204        Write(data, signs, 27, Signed832.ExtendValue(second.Y));
 2510205        Signed192 h = Signed192.Raw(height), r = Signed192.Raw(radius);
 2510206        Signed320 hh = WideArithmetic.MultiplySigned192(h, h), rr = WideArithmetic.MultiplySigned192(r, r);
 1255207        Span<ulong> work = stackalloc ulong[3 * Words];
 10040208        for (int index = 0; index < 3; index++)
 209        {
 7530210            WideDirection3d left = index < 2 ? first : second, right = index == 0 ? first : second;
 211            // Triangle axes <198 bits; segment affine-chart axes <200 bits.
 212            // Raw dimensions <63 give region coefficients <532 bits. The
 213            // forty-word magnitude owner retains both without narrowing.
 7530214            Signed832 radial = RadialDot(left, right), axial = WideArithmetic.MultiplySigned576(left.Y, right.Y);
 7530215            Import(radial, Slot(work, 0, Words)); Import(rr, Slot(work, 1, Words));
 3765216            WideArithmetic.MultiplyMagnitudes(Slot(work, 0, Words), Slot(work, 1, Words), Slot(data, 28 + index, Words))
 3765217            int sign = radial.Sign;
 7530218            Import(axial, Slot(work, 0, Words)); Import(hh, Slot(work, 1, Words));
 3765219            WideArithmetic.MultiplyMagnitudes(Slot(work, 0, Words), Slot(work, 1, Words), Slot(work, 2, Words));
 3765220            Add(Slot(work, 2, Words), -axial.Sign, Slot(data, 28 + index, Words), ref sign);
 5020221            if (index == 1) WideArithmetic.AddMagnitudeInto(Slot(data, 29, Words), Slot(data, 29, Words));
 3765222            signs[28 + index] = (sbyte)sign;
 223        }
 1255224    }
 225
 226    [MethodImpl(MethodImplOptions.NoInlining)]
 227    private static void GetMaterials(in TriangleCircularGeometry geometry, FixedTriangle triangle,
 228        Fixed64 height, Fixed64 radius, in Selection best,
 229        out Vector3d normal, out Vector3d radialPoint, out Vector3d trianglePoint)
 230    {
 15231        int Words = geometry.ParameterWords;
 15232        Span<ulong> data = stackalloc ulong[ParameterSlots * Words];
 15233        Span<sbyte> signs = stackalloc sbyte[ParameterSlots];
 15234        BuildConeParameter(geometry, height, radius, best.Edge, best.First, best.Second, data, signs);
 15235        Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords(data[..(5 * Words)], 
 15236        bool found = WideFiniteAxisIntersection.TryGetFiniteValueRoot(data[..(5 * Words)], signs[..5],
 15237            best.ParameterOrdinal, cell, out FiniteAxisValueRoot root);
 238        System.Diagnostics.Debug.Assert(found);
 15239        trianglePoint = TriangleCylinderRimWitnesses.GetRootPoint(geometry, triangle, best.Edge, best.First, best.Second
 15240        Span<ulong> gradient = stackalloc ulong[54];
 15241        Span<sbyte> gradientSigns = stackalloc sbyte[6];
 15242        WriteGradient(best.First, best.Second, gradient, gradientSigns);
 30243        gradient.Slice(18, 18).Clear(); gradientSigns.Slice(2, 2).Clear();
 15244        radialPoint = ConvexContactValueRoot.GetScaledNormalizedDirection(ref root, gradient, gradientSigns, radius, -1)
 15245        WriteGradient(Transform(geometry.WorldBasis, best.First), Transform(geometry.WorldBasis, best.Second), gradient,
 15246        normal = ConvexContactValueRoot.GetNormalizedDirection(ref root, gradient, gradientSigns, 1);
 15247    }
 248
 249    private static FiniteAxisValueRoot Restore(ReadOnlySpan<ulong> values, ReadOnlySpan<sbyte> signs,
 20250        Span<ulong> cell, in Selection selection) => new()
 20251    {
 20252        Coefficients = values, Signs = signs, LowerNumerator = cell,
 20253        DenominatorShift = selection.ValueShift, Ordinal = selection.ValueOrdinal, IsRational = selection.IsRational
 20254    };
 255}

Methods/Properties

TryGetContact(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Fixed64,System.Boolean&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
TryKeepContacts(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Fixed64,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.TriangleConeRimContacts/Selection&)
GetContactMaterials(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.TriangleConeRimContacts/Selection&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
TryChart(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Int32,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.ConvexContactCandidate,System.UInt64,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.TriangleConeRimContacts/Selection&)
BuildConeParameter(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Int32,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
BuildConeParameter(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Boolean)
GetMaterials(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.TriangleConeRimContacts/Selection&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
Restore(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.TriangleConeRimContacts/Selection&)