< Summary

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
TryGetSidePoint(...)100%3232100%
GetCapPoint(...)100%11100%
TryGetCapPoint(...)100%44100%
GetCapWeights(...)100%88100%
GetEdgeWeights(...)100%66100%
GetFaceWeights(...)100%2424100%
SetEdgeWeights(...)100%11100%
RoundTriangle(...)100%11100%
RoundComponent(...)100%66100%
SumWeights(...)100%11100%
SumCoordinates(...)100%22100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// TriangleCylinderWitnesses.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 static FixedMathSharp.Geometry.CylinderContactAlgebra;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <summary>Selected triangle-feature witnesses, with one final coordinate rounding.</summary>
 14internal static class TriangleCylinderWitnesses
 15{
 16    /// <summary>
 17    /// Intersects the selected support feature with the radial tangent plane,
 18    /// then with the finite axial interval. No cap endpoint is used as a proxy
 19    /// for the free side segment, and barycentric parameters never round first.
 20    /// Shifted core-region coordinates are below 232 bits and analytic
 21    /// directions below 630. Tangent weights stay below 864 bits, axial
 22    /// numerators below 1098, and blended weights below 1964. Their coordinate
 23    /// products remain below 2200 bits, inside forty-word candidate storage.
 24    /// </summary>
 25    internal static bool TryGetSidePoint(in TriangleCircularGeometry geometry, FixedTriangle triangle,
 26        int mask, ReadOnlySpan<ulong> normal, ReadOnlySpan<int> normalSigns,
 27        out Vector3d point, out Fixed64 axial, out int cap)
 28    {
 50429        point = default; axial = default; cap = 0;
 16830        Span<ulong> tangent = stackalloc ulong[3 * Words];
 16831        Span<int> tangentSigns = stackalloc int[3];
 33632        tangent.Clear(); tangentSigns.Clear();
 33633        Slot(normal, 2).CopyTo(Slot(tangent, 0)); tangentSigns[0] = -normalSigns[2];
 33634        Slot(normal, 0).CopyTo(Slot(tangent, 2)); tangentSigns[2] = normalSigns[0];
 16835        Span<ulong> distances = stackalloc ulong[3 * Words];
 16836        Span<int> distanceSigns = stackalloc int[3];
 16837        Span<ulong> candidates = stackalloc ulong[6 * Words];
 16838        Span<ulong> axialValues = stackalloc ulong[2 * Words];
 16839        Span<int> axialSigns = stackalloc int[2];
 16840        candidates.Clear();
 134441        for (int vertex = 0; vertex < 3; vertex++)
 50442            Dot(geometry.Vertex(vertex), tangent, tangentSigns, Slot(distances, vertex), out distanceSigns[vertex]);
 16843        int count = 0;
 134444        for (int vertex = 0; vertex < 3; vertex++)
 45        {
 50446            if ((mask & (1 << vertex)) != 0 && distanceSigns[vertex] == 0)
 1347                candidates[(count++ * 3 + vertex) * Words] = 1;
 48        }
 134449        for (int edge = 0; edge < 3; edge++)
 50        {
 50451            int next = (edge + 1) % 3;
 50452            if ((mask & (1 << edge)) == 0 || (mask & (1 << next)) == 0
 50453                || distanceSigns[edge] * distanceSigns[next] >= 0)
 54                continue;
 29055            Slot(distances, next).CopyTo(Slot(candidates, 3 * count + edge));
 29056            Slot(distances, edge).CopyTo(Slot(candidates, 3 * count + next));
 29057            count++;
 58        }
 17059        if (count == 0) return false;
 60        System.Diagnostics.Debug.Assert(count <= 2);
 16661        Span<ulong> weights = stackalloc ulong[3 * Words];
 16662        Span<ulong> denominator = stackalloc ulong[Words];
 16663        Span<ulong> bound = stackalloc ulong[Words];
 16664        Span<ulong> half = stackalloc ulong[Words];
 16665        Import(geometry.HalfHeight, half);
 16666        int selected = -1;
 16667        cap = 0;
 46468        for (int index = 0; index < count; index++)
 69        {
 21770            ReadOnlySpan<ulong> candidate = candidates.Slice(index * 3 * Words, 3 * Words);
 21771            SumWeights(candidate, denominator);
 21772            SumCoordinates(geometry, candidate, 1, Slot(axialValues, index), out axialSigns[index]);
 21773            WideArithmetic.MultiplyMagnitudes(denominator, half, bound);
 21774            int comparison = WideArithmetic.CompareMagnitudeEqualLength(Slot(axialValues, index), bound);
 21775            if (comparison <= 0)
 76            {
 15177                selected = index;
 15178                if (comparison == 0)
 1379                    cap = -axialSigns[index];
 1380                break;
 81            }
 82        }
 16683        if (selected >= 0)
 15184            candidates.Slice(selected * 3 * Words, 3 * Words).CopyTo(weights);
 85        else
 86        {
 87            // The minimizing side support proves an admitted axial point.
 88            // If neither endpoint is in the interval, they straddle it. Blend
 89            // directly at Y=0: w=w0*|Y1 numerator|+w1*|Y0 numerator|.
 1790            if (count != 2 || axialSigns[0] * axialSigns[1] >= 0) return false;
 1391            Span<ulong> product = stackalloc ulong[Words];
 10492            for (int vertex = 0; vertex < 3; vertex++)
 93            {
 3994                WideArithmetic.MultiplyMagnitudes(Slot(candidates, vertex), Slot(axialValues, 1), Slot(weights, vertex))
 3995                WideArithmetic.MultiplyMagnitudes(Slot(candidates, vertex + 3), Slot(axialValues, 0), product);
 3996                WideArithmetic.AddMagnitudeInto(product, Slot(weights, vertex));
 97            }
 98        }
 16499        SumWeights(weights, denominator);
 164100        SumCoordinates(geometry, weights, 1, bound, out int axialSign);
 164101        Import(Signed576.ExtendValue(geometry.RawScale), half);
 164102        Span<ulong> scaledDenominator = stackalloc ulong[Words];
 164103        WideArithmetic.MultiplyMagnitudes(denominator, half, scaledDenominator);
 164104        bool represented = Fixed64.TryGetSignedRawRatio(bound, scaledDenominator, axialSign < 0, out axial);
 105        System.Diagnostics.Debug.Assert(represented);
 164106        point = RoundTriangle(geometry, triangle, weights, denominator);
 164107        return true;
 108    }
 109
 110    /// <summary>
 111    /// Projects the origin onto the selected cap-pole feature, retaining its exact barycentric
 112    /// weights until both authored and cylinder-local coordinates round.
 113    /// A cap-pole minimum proves the projected radial point is in the disk;
 114    /// its final-rounded coordinates must not be normalized a second time.
 115    /// </summary>
 116    internal static Vector3d GetCapPoint(in TriangleCircularGeometry geometry, FixedTriangle triangle,
 117        int mask, out Vector3d projectedRadial)
 118    {
 68119        bool admitted = TryGetCapPoint(geometry, triangle, mask, out Vector3d point, out projectedRadial, false);
 120        System.Diagnostics.Debug.Assert(admitted);
 68121        return point;
 122    }
 123
 124    internal static bool TryGetCapPoint(in TriangleCircularGeometry geometry, FixedTriangle triangle,
 125        int mask, out Vector3d point, out Vector3d projectedRadial, bool requireAdmission = true)
 126    {
 106127        point = projectedRadial = default;
 106128        Span<ulong> weights = stackalloc ulong[3 * Words];
 106129        Span<ulong> denominator = stackalloc ulong[Words];
 106130        GetCapWeights(geometry, mask, weights);
 106131        SumWeights(weights, denominator);
 106132        if (requireAdmission)
 133        {
 38134            Span<ulong> work = stackalloc ulong[3 * Words];
 38135            SumCoordinates(geometry, weights, 0, Slot(work, 0), out _);
 38136            SumCoordinates(geometry, weights, 2, Slot(work, 1), out _);
 38137            Span<ulong> radial = stackalloc ulong[Words];
 38138            Import(geometry.Radius, radial);
 38139            WideArithmetic.MultiplyMagnitudes(denominator, radial, Slot(work, 2));
 140            // Affine barycentric numerators fit the existing bank; their
 141            // squares need complete products plus the radial-sum carry.
 38142            int squareWords = 2 * Math.Max(WideArithmetic.GetActiveMagnitudeLength(Slot(work, 0)),
 38143                Math.Max(WideArithmetic.GetActiveMagnitudeLength(Slot(work, 1)), WideArithmetic.GetActiveMagnitudeLength
 38144            Span<ulong> squares = stackalloc ulong[3 * squareWords];
 114145            Span<ulong> sum = squares[..squareWords], term = squares.Slice(squareWords, squareWords), limit = squares.Sl
 38146            WideArithmetic.MultiplyMagnitudes(Slot(work, 0), Slot(work, 0), sum);
 38147            WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 1), term);
 38148            WideArithmetic.AddMagnitudeInto(term, sum);
 38149            WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), limit);
 42150            if (WideArithmetic.CompareMagnitudeEqualLength(sum, limit) > 0) return false;
 151        }
 102152        point = RoundTriangle(geometry, triangle, weights, denominator);
 102153        Span<ulong> scale = stackalloc ulong[Words];
 102154        Span<ulong> scaledDenominator = stackalloc ulong[Words];
 102155        Span<ulong> coordinate = stackalloc ulong[Words];
 102156        Import(Signed576.ExtendValue(geometry.RawScale), scale);
 102157        WideArithmetic.MultiplyMagnitudes(denominator, scale, scaledDenominator);
 102158        SumCoordinates(geometry, weights, 0, coordinate, out int sign);
 102159        bool representedX = Fixed64.TryGetSignedRawRatio(coordinate, scaledDenominator, sign < 0, out Fixed64 x);
 102160        SumCoordinates(geometry, weights, 2, coordinate, out sign);
 102161        bool representedZ = Fixed64.TryGetSignedRawRatio(coordinate, scaledDenominator, sign < 0, out Fixed64 z);
 162        System.Diagnostics.Debug.Assert(representedX && representedZ); // Both bounded by the authored radius.
 102163        projectedRadial = new Vector3d(x, Fixed64.Zero, z);
 102164        return true;
 165    }
 166
 167    internal static void GetCapWeights(in TriangleCircularGeometry geometry, int mask, Span<ulong> weights)
 168    {
 123169        weights.Clear();
 123170        if (mask == 7)
 97171            GetFaceWeights(geometry, weights);
 26172        else if ((mask & (mask - 1)) == 0)
 19173            weights[((mask & 1) != 0 ? 0 : (mask & 2) != 0 ? 1 : 2) * Words] = 1;
 174        else
 7175            GetEdgeWeights(geometry, mask, weights);
 7176    }
 177
 178    private static void GetEdgeWeights(in TriangleCircularGeometry geometry, int mask, Span<ulong> weights)
 179    {
 7180        int first = (mask & 1) != 0 ? 0 : 1;
 7181        int second = (mask & 4) != 0 ? 2 : 1;
 7182        WideDirection3d edge = geometry.EdgeFromTo(first, second);
 183        // A pole's selected feature has constant local Y. Projecting the
 184        // origin is identical to projecting either cap center onto it.
 7185        Signed832 parameter = WideDirection3d.Dot(TriangleCircularGeometry.Subtract(default, geometry.Vertex(first)), ed
 7186        Signed832 denominatorValue = WideArithmetic.MultiplySigned832(edge.SquaredLength, geometry.EdgeScale);
 7187        if (parameter.Sign <= 0)
 188        {
 2189            weights[first * Words] = 1;
 2190            return;
 191        }
 5192        Signed832 remainder = WideArithmetic.SubtractSigned832(denominatorValue, parameter);
 5193        if (remainder.Sign <= 0)
 194        {
 1195            weights[second * Words] = 1;
 1196            return;
 197        }
 8198        Import(remainder, Slot(weights, first)); Import(parameter, Slot(weights, second));
 4199    }
 200
 201    private static void GetFaceWeights(in TriangleCircularGeometry geometry, Span<ulong> weights)
 202    {
 194203        WideDirection3d e = geometry.Edge(0), f = -geometry.Edge(2);
 97204        WideDirection3d offset = TriangleCircularGeometry.Subtract(default, geometry.A);
 194205        Signed832 d1 = WideDirection3d.Dot(e, offset), d2 = WideDirection3d.Dot(f, offset);
 97206        if (d1.Sign <= 0 && d2.Sign <= 0)
 207        {
 8208            weights[0] = 1;
 8209            return;
 210        }
 267211        Signed832 ee = e.SquaredLength, ff = f.SquaredLength, ef = WideDirection3d.Dot(e, f);
 89212        Signed832 d3 = WideArithmetic.SubtractSigned832(d1, WideArithmetic.MultiplySigned832(ee, geometry.EdgeScale));
 89213        Signed832 d4 = WideArithmetic.SubtractSigned832(d2, WideArithmetic.MultiplySigned832(ef, geometry.EdgeScale));
 89214        if (d3.Sign >= 0 && WideArithmetic.SubtractSigned832(d4, d3).Sign <= 0)
 215        {
 3216            weights[Words] = 1;
 3217            return;
 218        }
 219        // Retained edges are <196 bits even for shifted core regions. Gram
 220        // dots are <394, offset dots <430; cancelled barycentric minors are
 221        // <825 and the EdgeScale-adjusted alpha <827, within Signed832.
 86222        Span<ulong> minors = stackalloc ulong[4 * Words];
 344223        Span<ulong> vc = Slot(minors, 0), vb = Slot(minors, 1), va = Slot(minors, 2), determinant = Slot(minors, 3);
 86224        DifferenceOfProducts(ee, d2, ef, d1, vc, out int vcSign);
 86225        if (vcSign <= 0 && d1.Sign >= 0 && d3.Sign <= 0)
 226        {
 10227            SetEdgeWeights(0, 1, WideArithmetic.SubtractSigned832(default, d3), d1, weights);
 10228            return;
 229        }
 76230        Signed832 d5 = WideArithmetic.SubtractSigned832(d1, WideArithmetic.MultiplySigned832(ef, geometry.EdgeScale));
 76231        Signed832 d6 = WideArithmetic.SubtractSigned832(d2, WideArithmetic.MultiplySigned832(ff, geometry.EdgeScale));
 76232        if (d6.Sign >= 0 && WideArithmetic.SubtractSigned832(d5, d6).Sign <= 0)
 233        {
 3234            weights[2 * Words] = 1;
 3235            return;
 236        }
 73237        DifferenceOfProducts(ff, d1, ef, d2, vb, out int vbSign);
 238        // Once A/B/AB are excluded, vb<=0 proves d2>=0: the opposite
 239        // signs would force the earlier AB or B Voronoi region (Gram det>0).
 73240        if (vbSign <= 0 && d6.Sign <= 0)
 241        {
 20242            SetEdgeWeights(0, 2, WideArithmetic.SubtractSigned832(default, d6), d2, weights);
 20243            return;
 244        }
 53245        DifferenceOfProducts(ee, ff, ef, ef, determinant, out int vaSign);
 53246        Span<ulong> factor = stackalloc ulong[Words];
 53247        Import(Signed576.ExtendValue(geometry.EdgeScale), factor);
 53248        WideArithmetic.MultiplyMagnitudes(determinant, factor, va);
 106249        Add(vb, -vbSign, va, ref vaSign); Add(vc, -vcSign, va, ref vaSign);
 106250        Signed832 first = WideArithmetic.SubtractSigned832(d4, d3), second = WideArithmetic.SubtractSigned832(d5, d6);
 251        // The preceding vertex and edge regions already prove both BC
 252        // weights nonnegative whenever va<=0 for this nondegenerate face.
 53253        if (vaSign <= 0)
 254        {
 8255            SetEdgeWeights(1, 2, second, first, weights);
 8256            return;
 257        }
 135258        va.CopyTo(Slot(weights, 0)); vb.CopyTo(Slot(weights, 1)); vc.CopyTo(Slot(weights, 2));
 45259    }
 260
 261    private static void SetEdgeWeights(int first, int second,
 262        Signed832 firstWeight, Signed832 secondWeight, Span<ulong> weights)
 263    {
 76264        Import(firstWeight, Slot(weights, first)); Import(secondWeight, Slot(weights, second));
 38265    }
 266
 267    private static Vector3d RoundTriangle(in TriangleCircularGeometry geometry, FixedTriangle triangle, ReadOnlySpan<ulo
 266268        ReadOnlySpan<ulong> denominator) => new(
 266269        RoundComponent(geometry, triangle, 0, weights, denominator),
 266270        RoundComponent(geometry, triangle, 1, weights, denominator),
 266271        RoundComponent(geometry, triangle, 2, weights, denominator));
 272
 273    private static Fixed64 RoundComponent(in TriangleCircularGeometry geometry, FixedTriangle triangle, int component,
 274        ReadOnlySpan<ulong> weights, ReadOnlySpan<ulong> denominator)
 275    {
 798276        Span<ulong> sum = stackalloc ulong[Words];
 798277        Span<ulong> value = stackalloc ulong[Words];
 798278        Span<ulong> product = stackalloc ulong[Words];
 1596279        sum.Clear(); int sign = 0;
 6384280        for (int vertex = 0; vertex < 3; vertex++)
 281        {
 2394282            Signed576 coordinate = geometry.WitnessCoordinate(triangle, vertex, component);
 2394283            Import(coordinate, value);
 2394284            WideArithmetic.MultiplyMagnitudes(value, Slot(weights, vertex), product);
 2394285            Add(product, coordinate.Sign, sum, ref sign);
 286        }
 798287        Span<ulong> scaledDenominator = stackalloc ulong[geometry.IsAffine ? Words : 0];
 798288        scoped ReadOnlySpan<ulong> witnessDenominator = denominator;
 798289        if (geometry.IsAffine)
 290        {
 60291            Span<ulong> scale = stackalloc ulong[5];
 60292            WideArithmetic.GetMagnitude(geometry.RawScale, out scale[4], out scale[3], out scale[2], out scale[1], out s
 60293            WideArithmetic.MultiplyMagnitudes(denominator, scale, scaledDenominator);
 60294            witnessDenominator = scaledDenominator;
 295        }
 798296        bool represented = Fixed64.TryGetSignedRawRatio(sum, witnessDenominator, sign < 0, out Fixed64 result);
 297        System.Diagnostics.Debug.Assert(represented); // A convex combination of representable authored vertices.
 798298        return result;
 299    }
 300
 301    private static void SumWeights(ReadOnlySpan<ulong> weights, Span<ulong> denominator)
 302    {
 487303        WideArithmetic.AddEqualMagnitudes(Slot(weights, 0), Slot(weights, 1), denominator);
 487304        WideArithmetic.AddMagnitudeInto(Slot(weights, 2), denominator);
 487305    }
 306
 307    private static void SumCoordinates(in TriangleCircularGeometry geometry, ReadOnlySpan<ulong> weights,
 308        int component, Span<ulong> result, out int sign)
 309    {
 661310        Span<ulong> coordinate = stackalloc ulong[Words];
 661311        Span<ulong> product = stackalloc ulong[Words];
 1322312        result.Clear(); sign = 0;
 5288313        for (int vertex = 0; vertex < 3; vertex++)
 314        {
 1983315            Signed576 value = Component(geometry.Vertex(vertex), component);
 1983316            Import(value, coordinate);
 1983317            WideArithmetic.MultiplyMagnitudes(coordinate, Slot(weights, vertex), product);
 1983318            Add(product, value.Sign, result, ref sign);
 319        }
 661320    }
 321}

Methods/Properties

TryGetSidePoint(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Int32&)
GetCapPoint(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,System.Int32,FixedMathSharp.Vector3d&)
TryGetCapPoint(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,System.Int32,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,System.Boolean)
GetCapWeights(FixedMathSharp.Geometry.TriangleCircularGeometry&,System.Int32,System.Span`1<System.UInt64>)
GetEdgeWeights(FixedMathSharp.Geometry.TriangleCircularGeometry&,System.Int32,System.Span`1<System.UInt64>)
GetFaceWeights(FixedMathSharp.Geometry.TriangleCircularGeometry&,System.Span`1<System.UInt64>)
SetEdgeWeights(System.Int32,System.Int32,FixedMathSharp.Signed832,FixedMathSharp.Signed832,System.Span`1<System.UInt64>)
RoundTriangle(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
RoundComponent(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.FixedTriangle,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
SumWeights(System.ReadOnlySpan`1<System.UInt64>,System.Span`1<System.UInt64>)
SumCoordinates(FixedMathSharp.Geometry.TriangleCircularGeometry&,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>,System.Int32&)