< Summary

Information
Class: FixedMathSharp.Geometry.CurvedPrismContactGeometry
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/CurvedPrismContactGeometry.cs
Line coverage
100%
Covered lines: 230
Uncovered lines: 0
Coverable lines: 230
Total lines: 427
Line coverage: 100%
Branch coverage
100%
Covered branches: 96
Total branches: 96
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_VertexCount()100%11100%
get_EdgeCount()100%11100%
get_PlaneCount()100%11100%
.ctor(...)100%1212100%
Vertex(...)100%22100%
get_NativeRawScale()100%11100%
GetNativeSourcePoint(...)100%11100%
GetNativeVertex(...)100%22100%
GetNativeSourcePoint(...)100%11100%
FootprintVertex(...)100%11100%
Edge(...)100%44100%
Plane(...)100%66100%
TryGetEdgeNormalCone(...)100%1414100%
IsCoordinateSidePlane(...)100%44100%
GetSidePlane(...)100%22100%
TryOrientSidePlane(...)100%1010100%
ProjectionMinimum(...)100%11100%
ProjectionMinimum(...)100%44100%
GetMinimum(...)100%44100%
TryBuildCandidate(...)100%1616100%
BuildPlaneCandidate(...)100%1414100%
DotNormal(...)100%11100%
ScaleRaw(...)100%11100%
ReadDirectionComponent(...)100%22100%
ReduceVertex(...)100%11100%
Bits(...)100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// CurvedPrismContactGeometry.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 native-yaw prism geometry in the curved primitive's authored quaternion frame.</summary>
 14internal readonly ref struct CurvedPrismContactGeometry
 15{
 16    internal readonly WideRationalBasis3d WorldBasis;
 17    internal readonly WideAxis3 Axis;
 18    internal readonly Signed320 SquaredAxis, Radius;
 19    internal readonly Signed192 HalfFactor, RawScale;
 20    internal readonly WideDirection3d Half;
 21    internal readonly int ValueShift;
 22    internal readonly bool IsCone;
 23    internal readonly Fixed64 PrismHalfThickness, HeightValue, RadiusValue;
 24    private readonly WideConvex2dRelations.RotationFrame2d _yaw;
 25    private readonly ReadOnlySpan<Vector2d> _offsets;
 26    private readonly Signed192 _relativeX, _relativeY, _relativeZ, _thickness;
 27    private readonly Signed192 _coordinateDivisor;
 28    private readonly ReadOnlySpan<WideDirection3d> _vertices;
 29
 21489230    internal int VertexCount => 2 * _offsets.Length;
 758831    internal int EdgeCount => 3 * _offsets.Length;
 355632    internal int PlaneCount => 3 + _offsets.Length;
 33
 34    internal CurvedPrismContactGeometry(Vector3d center, FixedQuaternion rotation,
 35        Vector3d localAxis, Fixed64 length, Fixed64 radius, Vector3d prismOrigin,
 36        Fixed64 prismYaw, ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness, bool isCone,
 37        Span<WideDirection3d> vertexStorage)
 38    {
 103439        WorldBasis = new WideRationalBasis3d(rotation);
 310240        Signed192 axisX = Signed192.Raw(localAxis.X), axisY = Signed192.Raw(localAxis.Y), axisZ = Signed192.Raw(localAxi
 103441        Signed192 axisDivisor = WideArithmetic.GetGreatestCommonDivisor(
 103442            WideArithmetic.GetGreatestCommonDivisor(axisX, axisY), axisZ);
 103443        Axis = new WideAxis3(Signed320.ExtendValue(WideArithmetic.DivideExactSigned192(axisX, axisDivisor)),
 103444            Signed320.ExtendValue(WideArithmetic.DivideExactSigned192(axisY, axisDivisor)),
 103445            Signed320.ExtendValue(WideArithmetic.DivideExactSigned192(axisZ, axisDivisor)));
 103446        SquaredAxis = Signed320.NarrowValue(Axis.SquaredLength);
 103447        HalfFactor = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 103448            WorldBasis.Denominator, Signed192.Raw(length)));
 103449        HalfFactor = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(HalfFactor, axisDivisor));
 103450        Half = new WideDirection3d(WideArithmetic.MultiplySigned320(Axis.X, HalfFactor),
 103451            WideArithmetic.MultiplySigned320(Axis.Y, HalfFactor), WideArithmetic.MultiplySigned320(Axis.Z, HalfFactor));
 103452        RawScale = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 103453            WorldBasis.Denominator, Signed192.Raw(Fixed64.Two)));
 103454        Radius = WideArithmetic.MultiplySigned192(RawScale, Signed192.Raw(radius));
 413655        IsCone = isCone; PrismHalfThickness = halfThickness; HeightValue = length; RadiusValue = radius;
 103456        _yaw = new WideConvex2dRelations.RotationFrame2d(prismYaw);
 103457        _offsets = offsets;
 103458        _relativeX = ScaleRaw(WideArithmetic.Difference(prismOrigin.X, center.X));
 103459        _relativeY = ScaleRaw(WideArithmetic.Difference(prismOrigin.Y, center.Y));
 103460        _relativeZ = ScaleRaw(WideArithmetic.Difference(prismOrigin.Z, center.Z));
 103461        _thickness = ScaleRaw(Signed192.Raw(halfThickness));
 103462        _coordinateDivisor = Signed192.Signed(1);
 103463        _vertices = default;
 103464        ValueShift = 0;
 65
 66        // Native yaw vertices V have denominator Q. The exact quaternion
 67        // transpose gives P=2*B^T*V, S=2*D*Q, H=(D*Lraw)*A, R=S*Rraw.
 68        // Primitive Axis=A/g and T=D*Lraw*g preserve the authored half-axis;
 69        // no alignment quaternion or normalization changes its actual norm.
 70        // D<2^65 and the original |A|<2^33
 71        // give q<2^65, |P|<2^165, |H|,R<2^163 and S<2^99.
 72        // These bounds include full-domain origin differences and native yaw
 73        // offsets. T<2^161 and all coordinate scalars fit Signed192. Divide
 74        // S,T,R and every P by one positive content, retaining all ratios and
 75        // H=T*Axis exactly. T²*q is unchanged by primitive-axis reduction.
 103476        Signed192 divisor = WideArithmetic.GetGreatestCommonDivisor(RawScale, HalfFactor);
 103477        divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, Signed192.NarrowValue(Radius));
 103478        bool cache = VertexCount <= vertexStorage.Length;
 1916879        for (int index = 0; index < VertexCount; index++)
 80        {
 855081            WideDirection3d vertex = GetNativeVertex(index);
 1682882            if (cache) vertexStorage[index] = vertex;
 855083            divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, Signed192.NarrowValue(vertex.X));
 855084            divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, Signed192.NarrowValue(vertex.Y));
 855085            divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, Signed192.NarrowValue(vertex.Z));
 86        }
 103487        _coordinateDivisor = divisor;
 103488        RawScale = WideArithmetic.DivideExactSigned192(RawScale, divisor);
 103489        HalfFactor = WideArithmetic.DivideExactSigned192(HalfFactor, divisor);
 103490        Radius = Signed320.ExtendValue(WideArithmetic.DivideExactSigned192(Signed192.NarrowValue(Radius), divisor));
 103491        Half = new WideDirection3d(WideArithmetic.MultiplySigned320(Axis.X, HalfFactor),
 103492            WideArithmetic.MultiplySigned320(Axis.Y, HalfFactor), WideArithmetic.MultiplySigned320(Axis.Z, HalfFactor));
 103493        Span<ulong> magnitude = stackalloc ulong[9];
 103494        int bits = Math.Max(Bits(Signed576.ExtendValue(Radius), magnitude),
 103495            Math.Max(Bits(Half.X, magnitude), Math.Max(Bits(Half.Y, magnitude), Bits(Half.Z, magnitude))));
 1916896        for (int index = 0; index < VertexCount; index++)
 97        {
 855098            WideDirection3d vertex = ReduceVertex(cache ? vertexStorage[index] : GetNativeVertex(index), divisor);
 1682899            if (cache) vertexStorage[index] = vertex;
 8550100            bits = Math.Max(bits, Math.Max(Bits(vertex.X, magnitude),
 8550101                Math.Max(Bits(vertex.Y, magnitude), Bits(vertex.Z, magnitude))));
 102        }
 103        // Borrow only a complete query-owned cache. Larger footprints stream
 104        // the same reduced vertices and never allocate or change feature order.
 2058105        if (cache) _vertices = vertexStorage[..VertexCount];
 106        // Every projected support residual is shorter than the sum of its
 107        // vertex, half-axis and radial terms. Four bits cover that sum and
 108        // three-coordinate length before squaring onto the retained value axis.
 1034109        ValueShift = 2 * (bits + 4);
 1034110    }
 111
 112    /// <summary>Returns authored vertex 2*i at the bottom and 2*i+1 at the top.</summary>
 230131113    internal WideDirection3d Vertex(int index) => !_vertices.IsEmpty ? _vertices[index]
 230131114        : ReduceVertex(GetNativeVertex(index), _coordinateDivisor);
 115
 73116    internal Signed192 NativeRawScale => Signed192.NarrowProven(WideArithmetic.MultiplySigned192(RawScale, _coordinateDi
 117
 118    // Boundary cuts retain their original source coordinates; do not assume
 119    // the content of the original vertices also divides a new height-band point.
 120    internal WideDirection3d GetNativeSourcePoint(Vector3d point) =>
 146121        GetNativeSourcePoint(new Vector2d(point.X, point.Z), ScaleRaw(Signed192.Raw(point.Y)));
 122
 123    private WideDirection3d GetNativeVertex(int index) =>
 20972124        GetNativeSourcePoint(_offsets[index / 2], (index & 1) == 0 ? WideArithmetic.Negate(_thickness) : _thickness);
 125
 126    private WideDirection3d GetNativeSourcePoint(Vector2d offset, Signed192 y)
 127    {
 21118128        WideConvex2dRelations.GetRotatedOffset(_yaw, offset, out Signed192 x, out Signed192 z);
 21118129        var native = new WideAxis3(Signed320.ExtendValue(WideArithmetic.AddSigned192(_relativeX, x)),
 21118130            Signed320.ExtendValue(WideArithmetic.AddSigned192(_relativeY, y)),
 21118131            Signed320.ExtendValue(WideArithmetic.AddSigned192(_relativeZ, z)));
 21118132        WideRigidProjection.GetLocalAxisProjections(native, WorldBasis, out Signed576 px, out Signed576 py, out Signed57
 21118133        return new WideDirection3d(WideArithmetic.AddSigned576(px, px),
 21118134            WideArithmetic.AddSigned576(py, py), WideArithmetic.AddSigned576(pz, pz));
 135    }
 136
 108137    internal Vector2d FootprintVertex(int index) => _offsets[index];
 138
 139    /// <summary>Returns the bottom, top and vertical real edges of each footprint vertex.</summary>
 140    internal void Edge(int index, out int start, out int end)
 141    {
 34450142        int vertex = index / 3, kind = index % 3;
 17225143        int next = vertex + 1 == _offsets.Length ? 0 : vertex + 1;
 17225144        start = 2 * vertex + (kind == 1 ? 1 : 0);
 17225145        end = kind == 2 ? start + 1 : 2 * next + kind;
 17225146    }
 147
 148    /// <summary>Returns exact native coordinate and footprint-edge plane normals.</summary>
 149    internal WideDirection3d Plane(int index)
 150    {
 64963151        WideDirection3d up = new(Signed576.ExtendValue(WorldBasis.Xy),
 64963152            Signed576.ExtendValue(WorldBasis.Yy), Signed576.ExtendValue(WorldBasis.Zy));
 65711153        if (index == 0) return new WideDirection3d(Signed576.ExtendValue(WorldBasis.Xx),
 748154            Signed576.ExtendValue(WorldBasis.Yx), Signed576.ExtendValue(WorldBasis.Zx));
 121499155        if (index == 1) return up;
 7284156        if (index == 2) return new WideDirection3d(Signed576.ExtendValue(WorldBasis.Xz),
 353157            Signed576.ExtendValue(WorldBasis.Yz), Signed576.ExtendValue(WorldBasis.Zz));
 6578158        Edge(3 * (index - 3), out int start, out int end);
 13156159        WideDirection3d a = Vertex(start), b = Vertex(end);
 6578160        WideDirection3d edge = new(WideArithmetic.SubtractSigned576(b.X, a.X),
 6578161            WideArithmetic.SubtractSigned576(b.Y, a.Y), WideArithmetic.SubtractSigned576(b.Z, a.Z));
 6578162        return WideDirection3d.Cross(edge, up);
 163    }
 164
 165    /// <summary>Returns the two inward incident face normals of a proper real-edge support cone.</summary>
 166    internal bool TryGetEdgeNormalCone(int edge, out WideDirection3d first, out WideDirection3d last)
 167    {
 2372168        int vertex = edge / 3, kind = edge % 3;
 1186169        int previous = vertex == 0 ? _offsets.Length - 1 : vertex - 1;
 1186170        first = kind == 2 ? GetSidePlane(previous) : Plane(1);
 1521171        if (kind == 1) first = -first;
 1186172        last = GetSidePlane(vertex);
 173        // The cap normal already minimizes at its bottom/top edge. Vertical
 174        // edges require both incident wall orientations to be certified.
 1186175        if ((kind == 2 && !TryOrientSidePlane(previous, ref first)) || !TryOrientSidePlane(vertex, ref last)
 1186176            || WideDirection3d.AreParallel(first, last))
 59177            return false;
 178        // Each endpoint supports the complete prism, so its positive span
 179        // does too. Primitive side normals are no larger than native
 180        // B^T*(-dz,0,dx), below 165 bits; their difference stays below 166.
 1127181        Span<Signed576> components = stackalloc Signed576[3];
 3381182        components[0] = first.X; components[1] = first.Y; components[2] = first.Z;
 1127183        WideArithmetic.ReduceCommonScale(components);
 1127184        first = new WideDirection3d(components[0], components[1], components[2]);
 3381185        components[0] = last.X; components[1] = last.Y; components[2] = last.Z;
 1127186        WideArithmetic.ReduceCommonScale(components);
 1127187        last = new WideDirection3d(components[0], components[1], components[2]);
 1127188        return true;
 189    }
 190
 191    /// <summary>Whether a side plane duplicates an earlier native X/Z coordinate plane.</summary>
 192    internal bool IsCoordinateSidePlane(int plane)
 193    {
 1022194        int vertex = plane - 3;
 1022195        int next = vertex + 1 == _offsets.Length ? 0 : vertex + 1;
 196        // Full raw differences need65 bits. Test the exact authored yaw map,
 197        // before quaternion projection or any public Fixed64 narrowing.
 1022198        Signed192 dx = WideArithmetic.Difference(_offsets[next].X, _offsets[vertex].X);
 1022199        Signed192 dz = WideArithmetic.Difference(_offsets[next].Y, _offsets[vertex].Y);
 1022200        WideConvex2dRelations.GetRotatedOffset(_yaw, dx, dz, out Signed192 yawX, out Signed192 yawZ);
 1022201        return yawX.IsZero || yawZ.IsZero;
 202    }
 203
 204    private WideDirection3d GetSidePlane(int vertex)
 205    {
 1710206        int next = vertex + 1 == _offsets.Length ? 0 : vertex + 1;
 207        // Full raw differences need 65 bits; Vector2d subtraction can saturate.
 1710208        Signed192 dx = WideArithmetic.Difference(_offsets[next].X, _offsets[vertex].X);
 1710209        Signed192 dz = WideArithmetic.Difference(_offsets[next].Y, _offsets[vertex].Y);
 1710210        WideConvex2dRelations.GetRotatedOffset(_yaw, dx, dz, out Signed192 yawX, out Signed192 yawZ);
 1710211        var native = new WideAxis3(Signed320.ExtendValue(WideArithmetic.Negate(yawZ)),
 1710212            default, Signed320.ExtendValue(yawX));
 213        // The old Cross(localEdge,localUp) is this vector times the positive
 214        // 2*WorldBasis.Denominator/_coordinateDivisor. The existing endpoint
 215        // reduction returns exactly the same primitive integer vector. Yaw
 216        // differences are below 99 bits and transformed components below 166.
 1710217        WideRigidProjection.GetLocalAxisProjections(native, WorldBasis,
 1710218            out Signed576 x, out Signed576 y, out Signed576 z);
 1710219        return new WideDirection3d(x, y, z);
 220    }
 221
 222    private bool TryOrientSidePlane(int side, ref WideDirection3d normal)
 223    {
 1711224        if (normal.IsZero) return false;
 1633225        int next = side + 1 == _offsets.Length ? 0 : side + 1;
 3266226        Vector2d a = _offsets[side], b = _offsets[next];
 3266227        Signed192 dx = WideArithmetic.Difference(b.X, a.X), dz = WideArithmetic.Difference(b.Y, a.Y);
 3266228        int minimumSign = 0, maximumSign = 0;
 229        // Both transforms multiply side-plane dot differences by a positive
 230        // squared norm. Native determinants preserve every support equality,
 231        // including reversed, repeated and two-sided footprint boundaries.
 232        // Full raw differences are below65 bits; determinants below131 fit320.
 16342233        foreach (Vector2d point in _offsets)
 234        {
 6538235            int sign = WideArithmetic.MultiplySubtract(dx, WideArithmetic.Difference(point.Y, a.Y),
 6538236                dz, WideArithmetic.Difference(point.X, a.X)).Sign;
 6538237            minimumSign = Math.Min(minimumSign, sign);
 6538238            maximumSign = Math.Max(maximumSign, sign);
 239        }
 1633240        int orientation = minimumSign + maximumSign;
 1651241        if (orientation == 0) return false;
 1839242        if (orientation < 0) normal = -normal;
 1615243        return true;
 244    }
 245
 246    /// <summary>Returns the exact lower support-plane constant in common coordinates.</summary>
 15192247    internal Signed832 ProjectionMinimum(in WideDirection3d normal) => ProjectionMinimum(normal, out _);
 248
 249    private Signed832 ProjectionMinimum(in WideDirection3d normal, out int vertex)
 250    {
 251        // Rational fan directions are below 504 bits and vertices below 165,
 252        // so dot products and their differences remain below 672 bits.
 253        // Every top-minus-bottom is the same positive multiple of native Up.
 254        // Choose that supporting level once; bottom preserves first-index ties.
 22062255        vertex = WideDirection3d.Dot(normal, Plane(1)).Sign < 0 ? 1 : 0;
 22062256        Signed832 minimum = WideDirection3d.Dot(normal, Vertex(vertex));
 183518257        for (int index = vertex + 2; index < VertexCount; index += 2)
 258        {
 69697259            Signed832 current = WideDirection3d.Dot(normal, Vertex(index));
 69697260            if (WideArithmetic.SubtractSigned832(current, minimum).Sign >= 0) continue;
 17066261            minimum = current; vertex = index;
 262        }
 22062263        return minimum;
 264    }
 265
 266    /// <summary>
 267    /// Retains the first minimum projection under a seven-slot local normal
 268    /// (rational XYZ, radical XYZ, radicand). Result uses forty-word fields and
 269    /// must be disjoint from normal storage. The finite fan's rational fields
 270    /// are below 768 bits, radical fields below 512 and radicand below 832;
 271    /// vertex dots and their exact quadratic sign products fit forty words.
 272    /// </summary>
 273    internal void GetMinimum(ReadOnlySpan<ulong> normal, ReadOnlySpan<int> normalSigns,
 274        ContactQuadratic result, out int vertexIndex)
 275    {
 2824276        Span<ulong> work = stackalloc ulong[4 * Words];
 2824277        Span<int> signs = stackalloc int[4];
 2824278        ContactQuadratic current = ContactQuadratic.At(work, signs, 0);
 2824279        ContactQuadratic difference = ContactQuadratic.At(work, signs, 1);
 2824280        DotNormal(Plane(1), normal, normalSigns, current);
 2824281        vertexIndex = current.Sign(Slot(normal, 6)) < 0 ? 1 : 0;
 2824282        DotNormal(Vertex(vertexIndex), normal, normalSigns, result);
 25584283        for (int index = vertexIndex + 2; index < VertexCount; index += 2)
 284        {
 9968285            DotNormal(Vertex(index), normal, normalSigns, current);
 19936286            current.CopyTo(difference); difference.Add(result, -1);
 9968287            if (difference.Sign(Slot(normal, 6)) >= 0) continue;
 3774288            current.CopyTo(result); vertexIndex = index;
 289        }
 2824290    }
 291
 292    /// <summary>
 293    /// Encodes the complete support gap for a nonzero rational local direction, retaining
 294    /// that exact direction locally. Input and candidate storage are disjoint.
 295    /// Feature is -1 for bottom/base, +1 for top/apex, zero for a side/generator
 296    /// tie. A required vertex admits only its supporting normal directions.
 297    /// Directions fit Signed576; all support-gap products fit forty words.
 298    /// </summary>
 299    internal bool TryBuildCandidate(in WideDirection3d normal, Span<ulong> direction, Span<int> directionSigns,
 300        Span<ulong> values, Span<int> signs, int requiredVertex, out int gapSign, out int vertex, out int feature)
 301    {
 97548302        gapSign = 0; feature = 0; vertex = requiredVertex;
 303        Signed832 minimum;
 32516304        if (requiredVertex >= 0)
 305        {
 306            // A required edge endpoint cannot support from the wrong extrusion level; zero admits both ties.
 25646307            int upSign = WideDirection3d.Dot(normal, Plane(1)).Sign;
 32715308            if ((requiredVertex & 1) == 0 ? upSign < 0 : upSign > 0) return false;
 18577309            int level = upSign < 0 ? 1 : 0;
 310            // At zero Up projection the levels tie: retain the original even
 311            // level and first footprint index, not the authored required level.
 18577312            int reference = (requiredVertex & ~1) + level;
 18577313            minimum = WideDirection3d.Dot(normal, Vertex(reference));
 18577314            vertex = reference;
 144934315            for (int index = level; index < VertexCount; index += 2)
 316            {
 62095317                Signed832 current = index == reference ? minimum : WideDirection3d.Dot(normal, Vertex(index));
 62095318                int comparison = WideArithmetic.SubtractSigned832(current, minimum).Sign;
 70300319                if (comparison < 0) return false;
 59901320                if (comparison == 0 && index < vertex) vertex = index;
 321            }
 322        }
 6870323        else minimum = ProjectionMinimum(normal, out vertex);
 17242324        WriteDirection(normal, direction, directionSigns);
 17242325        gapSign = BuildPlaneCandidate(direction, directionSigns, normal, minimum, values, signs, out feature);
 17242326        return true;
 327    }
 328
 329
 330    // A retained native face uses its own known plane constant. In particular,
 331    // its opposite gap tests plane intersection, not the prism's opposite face.
 332    internal int BuildPlaneCandidate(ReadOnlySpan<ulong> direction, ReadOnlySpan<int> directionSigns,
 333        in WideDirection3d normal, Signed832 minimum, Span<ulong> values, Span<int> signs, out int feature)
 334    {
 17436335        int words = values.Length / signs.Length;
 34872336        values.Clear(); signs.Clear();
 34872337        direction.CopyTo(values); directionSigns.CopyTo(signs);
 17436338        Span<ulong> work = stackalloc ulong[14 * words];
 52308339        Span<ulong> minimumMagnitude = Slot(work, 0, words), half = Slot(work, 1, words), metric = Slot(work, 2, words);
 69744340        Span<ulong> axial = Slot(work, 3, words), q = Slot(work, 4, words), radial = Slot(work, 5, words), radiusSquared
 69744341        Span<ulong> p = Slot(work, 7, words), root = Slot(work, 8, words), denominator = Slot(work, 9, words), temporary
 52308342        Span<ulong> product = Slot(work, 11, words), scale = Slot(work, 12, words), rational = Slot(work, 13, words);
 17436343        Import(minimum, minimumMagnitude);
 17436344        Signed832 axisProjection = WideDirection3d.Dot(normal, Axis);
 345        // Half=HalfFactor*Axis remains exact after both common-scale reductions.
 17436346        Signed832 halfProjection = WideArithmetic.MultiplySigned832(axisProjection, HalfFactor);
 17436347        Import(halfProjection, half);
 17436348        int halfSign = halfProjection.Sign;
 17436349        SumSquares(direction, metric);
 17436350        Import(axisProjection, axial);
 17436351        Import(SquaredAxis, q);
 17436352        WideArithmetic.MultiplyMagnitudes(q, metric, temporary);
 17436353        WideArithmetic.MultiplyMagnitudes(axial, axial, product);
 17436354        WideArithmetic.SubtractEqualMagnitudes(temporary, product, radial);
 17436355        Import(Radius, temporary);
 17436356        WideArithmetic.MultiplyMagnitudes(temporary, temporary, radiusSquared);
 17436357        bool apex = false;
 17436358        feature = halfSign;
 17436359        if (IsCone)
 360        {
 6301361            int comparison = -1;
 6301362            if (halfSign >= 0)
 363            {
 4119364                WideArithmetic.MultiplyMagnitudes(half, half, temporary);
 4119365                WideArithmetic.MultiplyMagnitudes(q, temporary, product);
 8238366                WideArithmetic.AddMagnitudeInto(product, product); WideArithmetic.AddMagnitudeInto(product, product);
 4119367                WideArithmetic.MultiplyMagnitudes(radiusSquared, radial, temporary);
 4119368                comparison = WideArithmetic.CompareMagnitudeEqualLength(product, temporary);
 369            }
 6301370            apex = comparison >= 0;
 6301371            feature = comparison == 0 ? 0 : apex ? 1 : -1;
 372        }
 17436373        half.CopyTo(rational);
 17436374        int rationalSign = IsCone ? (apex ? halfSign : -halfSign) : IsZero(half) ? 0 : 1;
 17436375        Add(minimumMagnitude, -minimum.Sign, rational, ref rationalSign);
 17436376        Import(Signed576.ExtendValue(RawScale), scale);
 17436377        WideArithmetic.MultiplyMagnitudes(scale, scale, temporary);
 17436378        WideArithmetic.MultiplyMagnitudes(temporary, metric, denominator);
 17436379        if (IsCone && apex)
 380        {
 3378381            rational.CopyTo(p); root.Clear();
 382        }
 383        else
 384        {
 385            // g=(q*T+sqrt(q*R²*(q*n²-(Axis.n)²)))/(q*S*|n|).
 386            // Retain the positive q scale exactly; no axis norm is rounded.
 15747387            WideArithmetic.MultiplyMagnitudes(q, rational, p);
 15747388            WideArithmetic.MultiplyMagnitudes(radiusSquared, radial, temporary);
 15747389            WideArithmetic.MultiplyMagnitudes(q, temporary, root);
 15747390            WideArithmetic.MultiplyMagnitudes(q, q, temporary);
 31494391            WideArithmetic.MultiplyMagnitudes(denominator, temporary, product); product.CopyTo(denominator);
 392        }
 17436393        int gapSign = BuildRadialCandidate(p, rationalSign, root, denominator, values, signs);
 17436394        return gapSign;
 395    }
 396
 397    private static void DotNormal(in WideDirection3d point, ReadOnlySpan<ulong> normal,
 398        ReadOnlySpan<int> normalSigns, ContactQuadratic result)
 399    {
 15616400        Dot(point, normal[..(3 * Words)], normalSigns[..3], result.Rational, out int rationalSign);
 15616401        Dot(point, normal.Slice(3 * Words, 3 * Words), normalSigns.Slice(3, 3), result.Radical, out int radicalSign);
 31232402        result.Signs[0] = rationalSign; result.Signs[1] = radicalSign;
 15616403    }
 404
 405    private static Signed192 ScaleRaw(Signed192 value) =>
 4282406        Signed192.NarrowProven(WideArithmetic.MultiplySigned192(value, Signed192.One));
 407
 408    internal static Signed576 ReadDirectionComponent(ReadOnlySpan<ulong> magnitude, int sign)
 409    {
 410        // Rational fan components fit 504 bits; the remaining padded words
 411        // contain no information. Quadratic generator normals use GetMinimum.
 28108412        var value = new Signed576(magnitude[8], magnitude[7], magnitude[6], magnitude[5], magnitude[4],
 28108413            magnitude[3], magnitude[2], magnitude[1], magnitude[0]);
 28108414        return sign < 0 ? WideArithmetic.Negate(value) : value;
 415    }
 416
 20700417    private static WideDirection3d ReduceVertex(in WideDirection3d vertex, Signed192 divisor) => new(
 20700418        Signed576.ExtendValue(WideArithmetic.DivideExactSigned192(Signed192.NarrowValue(vertex.X), divisor)),
 20700419        Signed576.ExtendValue(WideArithmetic.DivideExactSigned192(Signed192.NarrowValue(vertex.Y), divisor)),
 20700420        Signed576.ExtendValue(WideArithmetic.DivideExactSigned192(Signed192.NarrowValue(vertex.Z), divisor)));
 421
 422    private static int Bits(Signed576 value, Span<ulong> magnitude)
 423    {
 29786424        WideArithmetic.GetMagnitude(value, magnitude);
 29786425        return WideArithmetic.GetMagnitudeBitLength(magnitude);
 426    }
 427}

Methods/Properties

get_VertexCount()
get_EdgeCount()
get_PlaneCount()
.ctor(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,System.Boolean,System.Span`1<FixedMathSharp.Geometry.WideDirection3d>)
Vertex(System.Int32)
get_NativeRawScale()
GetNativeSourcePoint(FixedMathSharp.Vector3d)
GetNativeVertex(System.Int32)
GetNativeSourcePoint(FixedMathSharp.Vector2d,FixedMathSharp.Signed192)
FootprintVertex(System.Int32)
Edge(System.Int32,System.Int32&,System.Int32&)
Plane(System.Int32)
TryGetEdgeNormalCone(System.Int32,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&)
IsCoordinateSidePlane(System.Int32)
GetSidePlane(System.Int32)
TryOrientSidePlane(System.Int32,FixedMathSharp.Geometry.WideDirection3d&)
ProjectionMinimum(FixedMathSharp.Geometry.WideDirection3d&)
ProjectionMinimum(FixedMathSharp.Geometry.WideDirection3d&,System.Int32&)
GetMinimum(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,FixedMathSharp.Geometry.ContactQuadratic,System.Int32&)
TryBuildCandidate(FixedMathSharp.Geometry.WideDirection3d&,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32,System.Int32&,System.Int32&,System.Int32&)
BuildPlaneCandidate(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Signed832,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&)
DotNormal(FixedMathSharp.Geometry.WideDirection3d&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,FixedMathSharp.Geometry.ContactQuadratic)
ScaleRaw(FixedMathSharp.Signed192)
ReadDirectionComponent(System.ReadOnlySpan`1<System.UInt64>,System.Int32)
ReduceVertex(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Signed192)
Bits(FixedMathSharp.Signed576,System.Span`1<System.UInt64>)