< Summary

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_ParameterWords()100%22100%
get_ValueWords()100%22100%
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%88100%
.ctor(...)100%11100%
WithCoreChart(...)100%11100%
WithValueShift(...)100%11100%
TransformHullVertex(...)100%44100%
GetSupportMask(...)100%66100%
.ctor(...)100%11100%
WitnessCoordinate(...)100%1010100%
WitnessVertex(...)100%66100%
RoundWitnessRatio(...)100%11100%
WithCore(...)100%22100%
AtCoreRegion(...)100%22100%
EdgeFromTo(...)100%22100%
Multiply(...)100%11100%
Multiply(...)100%11100%
Add(...)100%11100%
GetValueShift(...)100%22100%
GetValueShift(...)100%22100%
HasFaceMinimumCertificate()100%1818100%
HasNonnegativeGramWeights(...)100%44100%
HasFaceDiskMinimumCertificate(...)100%66100%
HasFaceDiskClearance(...)100%11100%
ProductDifference(...)100%11100%
GetAnalyticDepth(...)100%1616100%
Vertex(...)100%44100%
Edge(...)100%44100%
CapOffset(...)100%22100%
Subtract(...)100%11100%
GetSupport(...)100%1010100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// TriangleCircularGeometry.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>Exact triangle coordinates and circular extents in a canonical +Y frame.</summary>
 14internal readonly struct TriangleCircularGeometry
 15{
 16    internal readonly WideRationalBasis3d WorldBasis;
 17    internal readonly WideDirection3d A, B, C, FaceNormal;
 18    internal readonly Signed576 HalfHeight, Radius;
 19    internal readonly Signed320 RawScale;
 20    internal readonly bool IsAffine;
 5259621    internal int ParameterWords => IsAffine ? 80 : CylinderContactAlgebra.Words;
 3896222    internal int ValueWords => IsAffine ? 112 : CircularRimContactAlgebra.ValueWords;
 23    internal readonly Signed192 EdgeScale;
 24    internal readonly int ValueShift;
 25    private readonly WideDirection3d firstEdge, secondEdge;
 26    private readonly WideDirection3d materialOffset;
 27
 28    internal TriangleCircularGeometry(FixedTriangle triangle, in ScaledRigidFrame3d sourceFrame,
 29        in CylinderHullSupport hull, int a, int b, int c)
 30    {
 872931        materialOffset = default;
 1745832        IsAffine = hull.IsAffine; WorldBasis = hull.WorldBasis;
 2618733        A = hull.Vertex(a); B = hull.Vertex(b); C = hull.Vertex(c);
 2618734        RawScale = hull.RawScale; HalfHeight = hull.HalfHeight; Radius = hull.Radius;
 1745835        firstEdge = Subtract(B, A); secondEdge = Subtract(C, B);
 1745836        EdgeScale = Signed192.Signed(1); ValueShift = hull.ValueShift;
 872937        WideDirection3d localNormal = WideDirection3d.Cross(
 872938            WideDirection3d.GetLocalEdge(sourceFrame, triangle.A, triangle.B),
 872939            WideDirection3d.GetLocalEdge(sourceFrame, triangle.A, triangle.C));
 872940        FaceNormal = localNormal.Transform(hull.Frame.Basis);
 872941    }
 42
 43    internal TriangleCircularGeometry(FixedTriangle triangle, Vector3d origin, FixedQuaternion rotation,
 44        Vector3d cylinderCenter, FixedQuaternion cylinderRotation, Signed192 height, Fixed64 radius)
 45    {
 58446        materialOffset = default;
 58447        IsAffine = false;
 58448        WorldBasis = new WideRationalBasis3d(cylinderRotation);
 58449        var frame = new CylinderPolytopeFrame(cylinderCenter, cylinderRotation,
 58450            height, radius, origin, rotation);
 175251        WideAxis3 a = frame.Transform(triangle.A), b = frame.Transform(triangle.B), c = frame.Transform(triangle.C);
 58452        Span<Signed320> coordinates = stackalloc Signed320[12]
 58453        {
 58454            Signed320.ExtendValue(WideArithmetic.AddSigned192(frame.Denominator, frame.Denominator)),
 58455            a.X, a.Y, a.Z, b.X, b.Y, b.Z, c.X, c.Y, c.Z,
 58456            frame.Cap, WideArithmetic.MultiplySigned192(frame.Radius, frame.Denominator)
 58457        };
 58458        WideArithmetic.ReduceCommonScale(coordinates);
 58459        RawScale = coordinates[0];
 58460        A = new WideDirection3d(coordinates[1], coordinates[2], coordinates[3]);
 58461        B = new WideDirection3d(coordinates[4], coordinates[5], coordinates[6]);
 58462        C = new WideDirection3d(coordinates[7], coordinates[8], coordinates[9]);
 63        // IsNormalized bounds each exact quaternion norm denominator by
 64        // Q^2+(EpsilonRaw+2)*Q: four rounded squares add at most two raw
 65        // units of error. Thus their product D<2^129. Rational bases are
 66        // exactly orthogonal, so an unreduced edge 2D*R*(Braw-Araw) has norm
 67        // <2*2^129*sqrt(3)*2^64<2^195 (and conservatively <2^196).
 68        // Translation cancels; common reduction can only shrink this bound.
 116869        firstEdge = Subtract(B, A); secondEdge = Subtract(C, B);
 58470        EdgeScale = Signed192.Signed(1);
 116871        HalfHeight = Signed576.ExtendValue(coordinates[10]); Radius = Signed576.ExtendValue(coordinates[11]);
 58472        triangle.GetExactNormal(out Signed192 nx, out Signed192 ny, out Signed192 nz, out _);
 58473        FaceNormal = new WideDirection3d(WideRigidProjection.TransformLocalAxis(frame.Basis, nx, ny, nz));
 74        // Each quaternion denominator is <2^65. Relative transformed full-domain
 75        // points, including doubled coordinates and translated origin differences,
 76        // are <2^197. Edge/cap differences are <2^198. Squared residual values
 77        // are below 2^(2*(bits+4)); the common reduction only lowers these bounds.
 58478        ValueShift = GetValueShift(coordinates[1..], 4);
 58479    }
 80
 81    internal TriangleCircularGeometry(FixedTriangle triangle, in ScaledRigidFrame3d sourceFrame,
 82        Vector3d cylinderCenter, FixedQuaternion cylinderRotation, Signed192 height, Fixed64 radius)
 83    {
 84        // Entry points dispatch rigid frames before constructing affine geometry.
 48685        if (height.Sign < 0 || WideArithmetic.SubtractSigned192(height, Signed192.Raw((Fixed64)8192)).Sign > 0)
 286            throw new ArgumentOutOfRangeException(nameof(height));
 48487        if (radius < Fixed64.Zero || radius > (Fixed64)4096)
 288            throw new ArgumentOutOfRangeException(nameof(radius));
 48289        materialOffset = default;
 48290        IsAffine = true;
 48291        WorldBasis = new WideRationalBasis3d(cylinderRotation);
 48292        var frame = new CylinderPolytopeFrame(sourceFrame, cylinderCenter, cylinderRotation, height, radius);
 144693        WideAxis3 a = frame.Transform(triangle.A), b = frame.Transform(triangle.B), c = frame.Transform(triangle.C);
 48294        Span<Signed320> coordinates = stackalloc Signed320[12]
 48295        {
 48296            WideArithmetic.AddSigned320(frame.CoordinateDenominator, frame.CoordinateDenominator),
 48297            a.X, a.Y, a.Z, b.X, b.Y, b.Z, c.X, c.Y, c.Z,
 48298            frame.Cap, Signed320.NarrowValue(WideArithmetic.MultiplySigned320(frame.CoordinateDenominator, frame.Radius)
 48299        };
 482100        WideArithmetic.ReduceCommonScale(coordinates);
 482101        RawScale = coordinates[0];
 482102        A = new WideDirection3d(coordinates[1], coordinates[2], coordinates[3]);
 482103        B = new WideDirection3d(coordinates[4], coordinates[5], coordinates[6]);
 482104        C = new WideDirection3d(coordinates[7], coordinates[8], coordinates[9]);
 964105        HalfHeight = Signed576.ExtendValue(coordinates[10]); Radius = Signed576.ExtendValue(coordinates[11]);
 964106        firstEdge = Subtract(B, A); secondEdge = Subtract(C, B);
 482107        EdgeScale = Signed192.Signed(1);
 482108        WideDirection3d localNormal = WideDirection3d.Cross(
 482109            WideDirection3d.GetLocalEdge(sourceFrame, triangle.A, triangle.B),
 482110            WideDirection3d.GetLocalEdge(sourceFrame, triangle.A, triangle.C));
 482111        FaceNormal = localNormal.Transform(frame.Basis);
 482112        ValueShift = GetValueShift(coordinates[1..], 4);
 482113    }
 114
 115    private TriangleCircularGeometry(in TriangleCircularGeometry source, Signed192 multiplier, in WideDirection3d coreOf
 116        int valueShift = 0)
 117    {
 3823118        materialOffset = Multiply(source.materialOffset, multiplier);
 3823119        IsAffine = source.IsAffine;
 7646120        WorldBasis = source.WorldBasis; FaceNormal = source.FaceNormal;
 3823121        A = Multiply(source.A, multiplier);
 3823122        B = Multiply(source.B, multiplier);
 3823123        C = Multiply(source.C, multiplier);
 7646124        HalfHeight = Multiply(source.HalfHeight, multiplier); Radius = Multiply(source.Radius, multiplier);
 3823125        RawScale = Signed320.NarrowValue(WideArithmetic.MultiplySigned320(source.RawScale, multiplier));
 3823126        EdgeScale = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(source.EdgeScale, multiplier));
 7646127        firstEdge = source.firstEdge; secondEdge = source.secondEdge;
 3823128        Span<Signed576> bounds = stackalloc Signed576[13]
 3823129        {
 3823130            A.X, A.Y, A.Z, B.X, B.Y, B.Z, C.X, C.Y, C.Z,
 3823131            HalfHeight, Radius, coreOffset.X, coreOffset.Z
 3823132        };
 133        // Include the actual endpoint offset: a long core can dominate a
 134        // small centered triangle even when its common denominator is small.
 135        // Two additions cover endpoint/cap translation; the extra margin
 136        // bounds the squared support residual in both regions by 2^ValueShift.
 3823137        ValueShift = Math.Max(valueShift, GetValueShift(bounds, 5));
 3823138    }
 139
 140    internal TriangleCircularGeometry WithCoreChart(in TriangleCircularGeometry chart) =>
 3416141        new(this, chart.EdgeScale, default, chart.ValueShift);
 142
 143    internal TriangleCircularGeometry WithValueShift(int valueShift)
 144    {
 121145        return new TriangleCircularGeometry(this, Signed192.Signed(1), default, valueShift);
 146    }
 147
 148    internal WideDirection3d TransformHullVertex(in WideDirection3d vertex)
 149    {
 356476150        WideDirection3d point = EdgeScale.Equals(Signed192.Signed(1)) ? vertex : Multiply(vertex, EdgeScale);
 356476151        return materialOffset.IsZero ? point : Add(point, materialOffset);
 152    }
 153
 154    internal int GetSupportMask(ReadOnlySpan<ulong> direction, ReadOnlySpan<int> signs)
 155    {
 58928156        Span<ulong> projection = stackalloc ulong[Words];
 117856157        int best = 0, mask = 1;
 353568158        for (int i = 1; i < 3; i++)
 159        {
 117856160            Dot(Subtract(Vertex(i), Vertex(best)), direction, signs, projection, out int sign);
 173418161            if (sign > 0) { best = i; mask = 1 << i; }
 135355162            else if (sign == 0) mask |= 1 << i;
 163        }
 58928164        return mask;
 165    }
 166
 167    private TriangleCircularGeometry(in TriangleCircularGeometry source, in WideDirection3d offset)
 168    {
 3520169        this = source;
 3520170        materialOffset = Add(source.materialOffset, offset);
 10560171        A = Add(source.A, offset); B = Add(source.B, offset); C = Add(source.C, offset);
 3520172    }
 173
 174    // Affine contacts round once in the physical circular frame. Rounding
 175    // authored coordinates first would amplify one raw unit by the source scale.
 176    internal Signed576 WitnessCoordinate(FixedTriangle triangle, int vertex, int component)
 177    {
 4893178        if (IsAffine)
 1212179            return WideArithmetic.SubtractSigned576(Component(Vertex(vertex), component), Component(materialOffset, comp
 3681180        Vector3d point = vertex == 0 ? triangle.A : vertex == 1 ? triangle.B : triangle.C;
 3681181        return Signed576.ExtendValue(Signed192.Raw(component == 0 ? point.X : component == 1 ? point.Y : point.Z));
 182    }
 183
 184    internal Vector3d WitnessVertex(FixedTriangle triangle, int vertex)
 185    {
 33186        if (!IsAffine) return vertex == 0 ? triangle.A : vertex == 1 ? triangle.B : triangle.C;
 3187        Signed320 denominator = RawScale;
 3188        return new Vector3d(
 3189            RoundWitnessRatio(WitnessCoordinate(triangle, vertex, 0), denominator),
 3190            RoundWitnessRatio(WitnessCoordinate(triangle, vertex, 1), denominator),
 3191            RoundWitnessRatio(WitnessCoordinate(triangle, vertex, 2), denominator));
 192    }
 193
 194    private static Fixed64 RoundWitnessRatio(Signed576 numerator, Signed320 denominator)
 195    {
 18196        Span<ulong> n = stackalloc ulong[9], d = stackalloc ulong[5];
 9197        WideArithmetic.GetMagnitude(numerator, n);
 9198        WideArithmetic.GetMagnitude(denominator, out d[4], out d[3], out d[2], out d[1], out d[0]);
 9199        bool represented = Fixed64.TryGetSignedRawRatio(n, d, numerator.Sign < 0, out Fixed64 result);
 200        System.Diagnostics.Debug.Assert(represented);
 9201        return result;
 202    }
 203
 204    internal TriangleCircularGeometry WithCore(Vector2d axis, Fixed64 length, out WideDirection3d coreOffset)
 205    {
 206        // The reduced cylinder scale need not retain its original factor two.
 207        // One common 2Q multiplier represents L*axis/(2Q) at either endpoint,
 208        // including odd raw lengths, without forming a rounded endpoint.
 209        // Affine callers provide admitted finite cores (length <=8192 and
 210        // unit-axis components); the scalar path retains its full-domain input.
 286211        Signed192 multiplier = Signed192.Raw(Fixed64.Two);
 286212        Signed576 lengthScale = WideArithmetic.MultiplySigned320(RawScale, Signed192.Raw(length));
 286213        coreOffset = new WideDirection3d(Multiply(lengthScale, Signed192.Raw(axis.X)), default,
 286214            Multiply(lengthScale, Signed192.Raw(axis.Y)));
 286215        if (IsAffine)
 216        {
 217            // Cancel the same factor from 2Q and both endpoint offsets before
 218            // enlarging coordinates. All points, extents, RawScale and EdgeScale
 219            // then use the reduced multiplier, so no endpoint is rounded.
 220            // Width proof for the admitted affine frame: before either gcd,
 221            // coordinates need at most 306 bits; 2Q adds 33. The largest
 222            // projected squared gap needs at most 2608 bits: edge differences
 223            // need 307, endpoint/cap sums 341, e^2*v-(e.v)*e at most 959,
 224            // support dots at most 1302, then squaring/addition fits 2608.
 225            // If the earlier
 226            // coordinate gcd is G, g=log2(G), that degree-eight expression loses
 227            // 8g bits. Its original RawScale contains 2Q^2, hence the endpoint
 228            // offsets retain at least max(0,65-g) factors of two. This gcd removes
 229            // f>=min(33,max(0,65-g)) more; the degree-four dependence on scaled
 230            // points loses 4f bits. Thus 2608-8g-4f<=2476, below the existing
 231            // 2560-bit analytic bank. No rotation-denominator cancellation is
 232            // assumed: a translated core need not share such a factor.
 95233            Span<Signed576> factors = stackalloc Signed576[3]
 95234            { coreOffset.X, coreOffset.Z, Signed576.ExtendValue(multiplier) };
 95235            WideArithmetic.ReduceCommonScale(factors);
 95236            coreOffset = new(factors[0], default, factors[1]);
 95237            multiplier = Signed192.NarrowProven(Signed320.NarrowValue(factors[2]));
 238        }
 239        // Both endpoint regions retain one actual-bound-based value scale.
 286240        return new TriangleCircularGeometry(this, multiplier, coreOffset);
 241    }
 242
 243    internal TriangleCircularGeometry AtCoreRegion(in WideDirection3d coreOffset, int sign) =>
 3520244        new(this, sign > 0 ? coreOffset : -coreOffset);
 245
 5681246    internal WideDirection3d EdgeFromTo(int from, int to) => (from + 1) % 3 == to ? Edge(from) : -Edge(to);
 247
 248    internal static Signed576 Multiply(Signed576 value, Signed192 multiplier) =>
 250213249        WideArithmetic.MultiplySigned576(value, multiplier);
 250
 80665251    private static WideDirection3d Multiply(in WideDirection3d value, Signed192 multiplier) => new(
 80665252        Multiply(value.X, multiplier), Multiply(value.Y, multiplier), Multiply(value.Z, multiplier));
 253
 66123254    private static WideDirection3d Add(in WideDirection3d first, in WideDirection3d second) => new(
 66123255        WideArithmetic.AddSigned576(first.X, second.X), WideArithmetic.AddSigned576(first.Y, second.Y),
 66123256        WideArithmetic.AddSigned576(first.Z, second.Z));
 257
 258    internal static int GetValueShift(ReadOnlySpan<Signed576> coordinates, int carryBits)
 259    {
 9278260        int bits = 0;
 9278261        Span<ulong> magnitude = stackalloc ulong[9];
 174678262        for (int index = 0; index < coordinates.Length; index++)
 263        {
 78061264            WideArithmetic.GetMagnitude(coordinates[index], magnitude);
 78061265            bits = Math.Max(bits, WideArithmetic.GetMagnitudeBitLength(magnitude));
 266        }
 9278267        return 2 * (bits + carryBits);
 268    }
 269
 270    internal static int GetValueShift(ReadOnlySpan<Signed320> coordinates, int carryBits)
 271    {
 1066272        int bits = 0;
 1066273        Span<ulong> magnitude = stackalloc ulong[5];
 25584274        for (int index = 0; index < coordinates.Length; index++)
 275        {
 11726276            WideArithmetic.GetMagnitude(coordinates[index], out magnitude[4], out magnitude[3],
 11726277                out magnitude[2], out magnitude[1], out magnitude[0]);
 11726278            bits = Math.Max(bits, WideArithmetic.GetMagnitudeBitLength(magnitude));
 279        }
 1066280        return 2 * (bits + carryBits);
 281    }
 282
 283    /// <summary>
 284    /// After nonnegative cap and face support tests, certifies that their
 285    /// retained minimum is global without enumerating the remaining features.
 286    /// </summary>
 287    internal bool HasFaceMinimumCertificate()
 288    {
 449289        if (FaceNormal.X.IsZero && FaceNormal.Z.IsZero)
 290        {
 71291            Signed576 axial = A.Y.Sign < 0 ? WideArithmetic.Negate(A.Y) : A.Y;
 71292            Signed576 depth = WideArithmetic.SubtractSigned576(HalfHeight, axial);
 71293            if (WideArithmetic.SubtractSigned576(depth, Radius).Sign > 0)
 11294                return false;
 295        }
 378296        else if (!FaceNormal.Y.IsZero || WideArithmetic.SubtractSigned576(Radius, HalfHeight).Sign > 0)
 222297            return false;
 298
 299        // Let q be the origin's orthogonal projection onto the triangle plane.
 300        // A horizontal face has depth d=H-|q.Y|<=R; a vertical face has
 301        // d=R-|q.radial|<=R<=H. The caller has proved d>=0. If q is in the
 302        // triangle, the cylinder contains a radius-d ball centered at q.
 303        // Thus triangle-cylinder contains the origin-centered radius-d ball,
 304        // while the tested face support attains d: no other feature is smaller.
 432305        WideDirection3d e = Edge(0), f = -Edge(2);
 648306        Signed832 ee = e.SquaredLength, ff = f.SquaredLength, ef = WideDirection3d.Dot(e, f);
 216307        Signed832 d1 = WideArithmetic.SubtractSigned832(default, WideDirection3d.Dot(A, e));
 216308        Signed832 d2 = WideArithmetic.SubtractSigned832(default, WideDirection3d.Dot(A, f));
 309        // Retained edges are <196 bits; scaled offsets are <232. Thus Gram
 310        // dots are <394, offset dots <430, and mixed minors <825. The actual
 311        // barycentric denominator is EdgeScale*det (not det); alpha stays
 312        // <827 bits, within Signed832 even in a positive-core region.
 216313        if (IsAffine)
 21314            return HasNonnegativeGramWeights(ee, ff, ef, d1, d2);
 195315        Signed832 determinant = ProductDifference(Signed576.NarrowValue(ee), Signed576.NarrowValue(ff),
 195316            Signed576.NarrowValue(ef), Signed576.NarrowValue(ef));
 195317        Signed832 beta = ProductDifference(Signed576.NarrowValue(ff), Signed576.NarrowValue(d1),
 195318            Signed576.NarrowValue(ef), Signed576.NarrowValue(d2));
 195319        Signed832 gamma = ProductDifference(Signed576.NarrowValue(ee), Signed576.NarrowValue(d2),
 195320            Signed576.NarrowValue(ef), Signed576.NarrowValue(d1));
 195321        Signed832 alpha = WideArithmetic.SubtractSigned832(
 195322            WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned832(determinant, EdgeScale), beta), gamma);
 195323        return alpha.Sign >= 0 && beta.Sign >= 0 && gamma.Sign >= 0;
 324    }
 325
 326    private bool HasNonnegativeGramWeights(Signed832 ee, Signed832 ff, Signed832 ef, Signed832 d1, Signed832 d2)
 327    {
 42328        Span<ulong> values = stackalloc ulong[20 * Words]; Span<int> signs = stackalloc int[20];
 42329        ContactQuadratic a = ContactQuadratic.At(values, signs, 0), b = ContactQuadratic.At(values, signs, 1),
 42330            c = ContactQuadratic.At(values, signs, 2), d = ContactQuadratic.At(values, signs, 3),
 42331            e = ContactQuadratic.At(values, signs, 4), determinant = ContactQuadratic.At(values, signs, 5),
 42332            beta = ContactQuadratic.At(values, signs, 6), gamma = ContactQuadratic.At(values, signs, 7),
 42333            alpha = ContactQuadratic.At(values, signs, 8), term = ContactQuadratic.At(values, signs, 9);
 105334        a.Set(ee); b.Set(ff); c.Set(ef); d.Set(d1); e.Set(d2);
 21335        ContactQuadratic.Multiply(a, b, ReadOnlySpan<ulong>.Empty, determinant);
 42336        ContactQuadratic.Multiply(c, c, ReadOnlySpan<ulong>.Empty, term); determinant.Add(term, -1);
 21337        ContactQuadratic.Multiply(b, d, ReadOnlySpan<ulong>.Empty, beta);
 42338        ContactQuadratic.Multiply(c, e, ReadOnlySpan<ulong>.Empty, term); beta.Add(term, -1);
 21339        ContactQuadratic.Multiply(a, e, ReadOnlySpan<ulong>.Empty, gamma);
 42340        ContactQuadratic.Multiply(c, d, ReadOnlySpan<ulong>.Empty, term); gamma.Add(term, -1);
 21341        ContactQuadratic.Scale(determinant, Signed576.ExtendValue(Signed320.ExtendValue(EdgeScale)), alpha);
 42342        alpha.Add(beta, -1); alpha.Add(gamma, -1);
 21343        return alpha.Signs[0] >= 0 && beta.Signs[0] >= 0 && gamma.Signs[0] >= 0;
 344    }
 345
 346    internal bool HasFaceDiskMinimumCertificate(in WideDirection3d coreAxis, ConvexContactCandidate best)
 347    {
 116348        if (!WideDirection3d.Cross(FaceNormal, coreAxis).IsZero)
 65349            return false;
 350        // The caller has tested +/-face with d>=0 and proved that its shape
 351        // contains the normal-axis segment [-s,s], with d<=s-|a| for plane
 352        // u.x=a. A face-plane disk(q,d), q=a*u, minus that segment contains
 353        // the origin ball(d). Its three inward edge clearances therefore
 354        // certify the retained d globally, without changing its earlier tie.
 51355        Span<ulong> values = stackalloc ulong[ConvexContactCandidate.Slots * Words];
 51356        Span<int> signs = stackalloc int[ConvexContactCandidate.Slots];
 51357        Span<ulong> work = stackalloc ulong[6 * Words];
 153358        Span<ulong> zero = Slot(work, 1), scale = Slot(work, 3), squaredScale = Slot(work, 4);
 153359        values.Clear(); signs.Clear(); zero.Clear();
 51360        Import(Signed576.ExtendValue(RawScale), scale);
 51361        WideArithmetic.MultiplyMagnitudes(scale, scale, squaredScale);
 338362        for (int edge = 0; edge < 3; edge++)
 363        {
 364            // Use the parallel raw core axis, not the much wider transformed
 365            // face normal. Parallelism cross<295 bits; tangent<231, offset
 366            // dot<465 and tangent norm²<464 fit the existing fixed carriers.
 367            // Scale²*norm²<792 and numerator²<930 fit forty-word candidates.
 131368            WideDirection3d tangent = WideDirection3d.Cross(coreAxis, Edge(edge));
 131369            int inward = WideDirection3d.Dot(EdgeFromTo(edge, (edge + 2) % 3), tangent).Sign;
 131370            Signed832 projection = WideDirection3d.Dot(Vertex(edge), tangent);
 131371            if (!HasFaceDiskClearance(projection, -inward * projection.Sign, tangent,
 131372                    best, work, values, signs))
 13373                return false;
 374        }
 38375        return true;
 376    }
 377
 378    // Shared by single-triangle disks and complete patch tubes. Work slot 1
 379    // is zero and slot 4 is the squared coordinate scale, prepared once.
 380    internal static bool HasFaceDiskClearance(Signed832 projection, int projectionSign,
 381        in WideDirection3d tangent, ConvexContactCandidate best, Span<ulong> work,
 382        Span<ulong> values, Span<int> signs)
 383    {
 131384        Import(projection, Slot(work, 0));
 131385        Import(tangent.SquaredLength, Slot(work, 2));
 131386        WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 4), Slot(work, 5));
 131387        int gapSign = BuildRadialCandidate(Slot(work, 0), projectionSign,
 131388            Slot(work, 1), Slot(work, 5), values, signs);
 131389        return WideConvexPrismRelations.CompareConvexContactCandidates(
 131390            new ConvexContactCandidate(values, signs, gapSign), best) >= 0;
 391    }
 392
 393    private static Signed832 ProductDifference(Signed576 a, Signed576 b, Signed576 c, Signed576 d) =>
 585394        WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned576ToSigned832(a, b),
 585395            WideArithmetic.MultiplySigned576ToSigned832(c, d));
 396
 397    /// <summary>Rounds a rational local normal's support gap, cancelling principal-axis scale exactly.</summary>
 398    internal void GetAnalyticDepth(ConvexContactCandidate candidate, ReadOnlySpan<int> directionSigns, int mask,
 399        out Fixed64 depth, out bool clamped)
 400    {
 748401        int components = (directionSigns[0] == 0 ? 0 : 1)
 748402            + (directionSigns[1] == 0 ? 0 : 1) + (directionSigns[2] == 0 ? 0 : 1);
 748403        if (components != 1)
 404        {
 338405            WideConvexPrismRelations.GetRoundedConvexContactCandidateDepth(candidate, Fixed64.Zero, out depth, out clamp
 338406            return;
 407        }
 408        // A single local component cancels its magnitude from the normalized
 409        // support gap. Every vertex in the selected mask has this coordinate.
 410410        int axis = directionSigns[0] != 0 ? 0 : directionSigns[1] != 0 ? 1 : 2;
 410411        int vertex = (mask & 1) != 0 ? 0 : (mask & 2) != 0 ? 1 : 2;
 410412        Signed576 coordinate = Component(Vertex(vertex), axis);
 410413        Signed576 numerator = WideArithmetic.AddSigned576(axis == 1 ? HalfHeight : Radius,
 410414            directionSigns[axis] < 0 ? WideArithmetic.Negate(coordinate) : coordinate);
 415        // Shifted numerator and exact clamp threshold fit 233 bits. Test exact
 416        // overflow before nearest-even rounding, including Max+fractions.
 410417        Signed576 maximum = WideArithmetic.MultiplySigned320(RawScale, Signed192.Raw(Fixed64.MaxValue));
 410418        clamped = WideArithmetic.SubtractSigned576(numerator, maximum).Sign > 0;
 410419        if (clamped)
 420        {
 3421            depth = Fixed64.MaxValue;
 3422            return;
 423        }
 407424        bool represented = Fixed64.TryGetSignedRawRatio(numerator, Signed576.ExtendValue(RawScale), out depth);
 425        System.Diagnostics.Debug.Assert(represented);
 407426    }
 427
 613917428    internal WideDirection3d Vertex(int index) => index == 0 ? A : index == 1 ? B : C;
 23803429    internal WideDirection3d Edge(int index) => index == 0 ? firstEdge : index == 1 ? secondEdge : -Add(firstEdge, secon
 32967430    internal WideDirection3d CapOffset(int vertex, int cap) => new(Vertex(vertex).X,
 32967431        WideArithmetic.AddSigned576(Vertex(vertex).Y, cap > 0 ? HalfHeight : WideArithmetic.Negate(HalfHeight)),
 32967432        Vertex(vertex).Z);
 564199433    internal static WideDirection3d Subtract(in WideDirection3d first, in WideDirection3d second) => new(
 564199434        WideArithmetic.SubtractSigned576(first.X, second.X),
 564199435        WideArithmetic.SubtractSigned576(first.Y, second.Y),
 564199436        WideArithmetic.SubtractSigned576(first.Z, second.Z));
 437    internal static FixedPointAnchor GetSupport(Vector3d center, FixedQuaternion rotation,
 438        Signed192 height, Vector3d radialPoint, int cap)
 439    {
 819440        ulong floor = height.Low >> 1;
 819441        Fixed64 halfHeight = Fixed64.FromRaw((long)(floor + ((height.Low & 1UL) != 0 && (floor & 1UL) != 0 ? 1UL : 0UL))
 819442        Vector3d axial = new(Fixed64.Zero, cap >= 0 ? -halfHeight : halfHeight, Fixed64.Zero);
 443        // Public cylinder heights fit one Fixed64 and can retain an odd raw
 444        // half-height. Slab heights may span 64 unsigned bits but are even,
 445        // because their authored half-thickness was representable and exact.
 819446        FixedPointAnchorTerm3d term = height.Low <= long.MaxValue
 819447            ? FixedPointAnchorTerm3d.CreateCenteredAxisSupport(Vector3d.Up,
 819448                Fixed64.FromRaw(cap >= 0 ? -(long)height.Low : (long)height.Low),
 819449                Vector3d.Zero, Fixed64.Zero, axial, Vector3d.Zero)
 819450            : default;
 819451        return new FixedPointAnchor(center, rotation, axial, radialPoint, term);
 452    }
 453}

Methods/Properties

get_ParameterWords()
get_ValueWords()
.ctor(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Geometry.CylinderHullSupport&,System.Int32,System.Int32,System.Int32)
.ctor(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
.ctor(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
.ctor(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Signed192,FixedMathSharp.Geometry.WideDirection3d&,System.Int32)
WithCoreChart(FixedMathSharp.Geometry.TriangleCircularGeometry&)
WithValueShift(System.Int32)
TransformHullVertex(FixedMathSharp.Geometry.WideDirection3d&)
GetSupportMask(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>)
.ctor(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.WideDirection3d&)
WitnessCoordinate(FixedMathSharp.Geometry.FixedTriangle,System.Int32,System.Int32)
WitnessVertex(FixedMathSharp.Geometry.FixedTriangle,System.Int32)
RoundWitnessRatio(FixedMathSharp.Signed576,FixedMathSharp.Signed320)
WithCore(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideDirection3d&)
AtCoreRegion(FixedMathSharp.Geometry.WideDirection3d&,System.Int32)
EdgeFromTo(System.Int32,System.Int32)
Multiply(FixedMathSharp.Signed576,FixedMathSharp.Signed192)
Multiply(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Signed192)
Add(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&)
GetValueShift(System.ReadOnlySpan`1<FixedMathSharp.Signed576>,System.Int32)
GetValueShift(System.ReadOnlySpan`1<FixedMathSharp.Signed320>,System.Int32)
HasFaceMinimumCertificate()
HasNonnegativeGramWeights(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
HasFaceDiskMinimumCertificate(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.ConvexContactCandidate)
HasFaceDiskClearance(FixedMathSharp.Signed832,System.Int32,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.ConvexContactCandidate,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>)
ProductDifference(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
GetAnalyticDepth(FixedMathSharp.Geometry.ConvexContactCandidate,System.ReadOnlySpan`1<System.Int32>,System.Int32,FixedMathSharp.Fixed64&,System.Boolean&)
Vertex(System.Int32)
Edge(System.Int32)
CapOffset(System.Int32,System.Int32)
Subtract(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&)
GetSupport(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Signed192,FixedMathSharp.Vector3d,System.Int32)