< Summary

Information
Class: FixedMathSharp.Geometry.TriangleCylinderAnalyticFeatures
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Triangles/TriangleCylinderAnalyticFeatures.cs
Line coverage
100%
Covered lines: 171
Uncovered lines: 0
Coverable lines: 171
Total lines: 299
Line coverage: 100%
Branch coverage
100%
Covered branches: 126
Total branches: 126
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_Up()100%11100%
TryGetSeed(...)100%1414100%
TryGetBest(...)100%2020100%
KeepSeam(...)100%22100%
KeepRegion(...)100%3030100%
KeepAxis(...)100%3434100%
BuildAxis(...)100%88100%
HasVertexRimSeparation(...)100%1818100%
ResidualProjectionSign(...)100%11100%
.ctor(...)100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// TriangleCylinderAnalyticFeatures.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>Face, cap-pole, vertex/side and edge/side boundaries of the support fan.</summary>
 14internal static class TriangleCylinderAnalyticFeatures
 15{
 749616    private static WideDirection3d Up => new(default, Signed576.One, default);
 17
 18    internal static bool TryGetSeed(in TriangleCircularGeometry geometry, in WideDirection3d coreOffset, in WideDirectio
 19        Span<ulong> values, Span<int> signs, Span<ulong> direction, Span<int> directionSigns,
 20        out int gapSign, out int supportMask, out int coreSign, in CylinderHullSupport hull, bool includeSeam)
 21    {
 90322        var selection = new Selection(values, signs, direction, directionSigns);
 102223        if (includeSeam) KeepSeam(geometry, coreAxis, ref selection, hull);
 180624        Span<ulong> normal = stackalloc ulong[3 * Words]; Span<int> normalSigns = stackalloc int[3];
 90325        WideDirection3d face = geometry.FaceNormal;
 90326        if (!face.IsZero && WideDirection3d.Dot(face, coreAxis).Sign == 0)
 27        {
 28            // Match the complete fan's compact seam normal, retaining its
 29            // witness widths even when the hull face carries unreduced scale.
 37030            face = WideDirection3d.Cross(geometry.Edge(0), coreAxis);
 44631            if (face.IsZero) face = WideDirection3d.Cross(geometry.Edge(1), coreAxis);
 32        }
 90333        WriteDirection(face, normal, normalSigns);
 722434        for (int region = -1; region <= 1; region++)
 35        {
 270936            var endpoint = region == 0 ? geometry : geometry.AtCoreRegion(coreOffset, region);
 270937            KeepAxis(endpoint, coreAxis, region, normal, normalSigns, ref selection, hull);
 38        }
 270939        gapSign = selection.GapSign; supportMask = selection.SupportMask; coreSign = selection.CoreSign;
 90440        if (!selection.HasValue) return false;
 90241        WriteWorldDirection(geometry.WorldBasis, direction, directionSigns, values, signs);
 90242        return gapSign >= 0;
 43    }
 44
 45    internal static bool TryGetBest(in TriangleCircularGeometry geometry, in WideDirection3d coreOffset, in WideDirectio
 46        Span<ulong> bestValues, Span<int> bestSigns, Span<ulong> bestDirection,
 47        Span<int> bestDirectionSigns, out int gapSign, out int supportMask, out int coreSign, out bool faceMinimumCertif
 48        in CylinderHullSupport hull = default)
 49    {
 230850        var selection = new Selection(bestValues, bestSigns, bestDirection, bestDirectionSigns);
 230851        faceMinimumCertified = false;
 230852        if (coreAxis.IsZero)
 185053            KeepRegion(geometry, coreAxis, 0, ref selection, out faceMinimumCertified, hull);
 54        else
 55        {
 45856            KeepSeam(geometry, coreAxis, ref selection, hull);
 57            // The centered cylinder is contained in the stadium. Its existing
 58            // inscribed ball therefore remains a lower bound. An exactly
 59            // core-perpendicular face has the same support in both shapes;
 60            // Up or Up x core already attained it in the explicit seam fan.
 45861            faceMinimumCertified = hull.IsEmpty && selection.GapSign >= 0
 45862                && WideDirection3d.Dot(geometry.FaceNormal, coreAxis).Sign == 0
 45863                && geometry.HasFaceMinimumCertificate();
 243064            for (int region = -1; region <= 1 && selection.GapSign >= 0 && !faceMinimumCertified; region += 2)
 65            {
 75766                TriangleCircularGeometry endpoint = geometry.AtCoreRegion(coreOffset, region);
 75767                KeepRegion(endpoint, coreAxis, region, ref selection, out _, hull);
 68            }
 45869            if (hull.IsEmpty && !faceMinimumCertified && selection.GapSign >= 0)
 7570                faceMinimumCertified = geometry.HasFaceDiskMinimumCertificate(coreAxis,
 7571                    new ConvexContactCandidate(selection.Values, selection.Signs, selection.GapSign));
 72        }
 73        // Selection and its face certificates read gap fields only. Transform
 74        // the retained local direction once, after the exact winner is known.
 230875        WriteWorldDirection(geometry.WorldBasis, selection.Direction, selection.DirectionSigns, selection.Values, select
 692476        gapSign = selection.GapSign; supportMask = selection.SupportMask; coreSign = selection.CoreSign;
 230877        return gapSign >= 0;
 78    }
 79
 80    private static void KeepSeam(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, ref Selection select
 81    {
 57782        Span<ulong> direction = stackalloc ulong[3 * Words];
 57783        Span<int> signs = stackalloc int[3];
 57784        WriteDirection(Up, direction, signs);
 57785        KeepAxis(geometry, coreAxis, 0, direction, signs, ref selection, hull);
 57786        WriteDirection(new WideDirection3d(WideArithmetic.Negate(coreAxis.Z), default, coreAxis.X), direction, signs);
 57787        KeepAxis(geometry, coreAxis, 0, direction, signs, ref selection, hull);
 461688        for (int edge = 0; edge < 3; edge++)
 89        {
 173190            WriteDirection(WideDirection3d.Cross(geometry.Edge(edge), coreAxis), direction, signs);
 173191            KeepAxis(geometry, coreAxis, 0, direction, signs, ref selection, hull);
 92        }
 57793    }
 94
 95    private static void KeepRegion(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, int region,
 96        ref Selection selection, out bool faceMinimumCertified, in CylinderHullSupport hull)
 97    {
 260798        Span<ulong> direction = stackalloc ulong[3 * Words];
 260799        Span<int> signs = stackalloc int[3];
 2607100        WriteDirection(Up, direction, signs);
 2607101        KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 102        // Horizontal face directions duplicate the already tested +/-Up,
 103        // including their support masks and first-winner tie ordering.
 2607104        if (!geometry.FaceNormal.X.IsZero || !geometry.FaceNormal.Z.IsZero)
 105        {
 2284106            WriteDirection(geometry.FaceNormal, direction, signs);
 2284107            KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 108        }
 2607109        faceMinimumCertified = hull.IsEmpty && coreAxis.IsZero && selection.GapSign >= 0 && geometry.HasFaceMinimumCerti
 17846110        for (int vertex = 0; vertex < 3 && selection.GapSign >= 0 && !faceMinimumCertified; vertex++)
 111        {
 6318112            WideDirection3d point = geometry.Vertex(vertex);
 6318113            WriteDirection(new WideDirection3d(point.X, default, point.Z), direction, signs);
 6318114            KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 6318115            WideDirection3d edge = geometry.Edge(vertex);
 6318116            bool oblique = !edge.Y.IsZero && (!edge.X.IsZero || !edge.Z.IsZero);
 6318117            if (oblique)
 118            {
 4312119                CircularRimContactAlgebra.GetBasis(edge, out WideDirection3d first, out WideDirection3d second);
 4312120                WriteDirection(first, direction, signs);
 4312121                KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 4312122                WriteDirection(second, direction, signs);
 4312123                KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 124            }
 6318125            WriteDirection(new WideDirection3d(WideArithmetic.Negate(edge.Z), default, edge.X), direction, signs);
 6318126            KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 127            // Principal axes and K=0 directions are rational and must be
 128            // included separately from the stationary quartic's division by K.
 129            // Parallel/perpendicular edges project a circle/rectangle. Their
 130            // positive minima are the vertex-radial, pole, side or face
 131            // boundaries above; an admitted negative interior also has a
 132            // negative admitted boundary. Only oblique ellipses need extras.
 6318133            if (oblique)
 134            {
 4312135                ProjectPerpendicular(edge, Up, direction, signs);
 4312136                KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 137            }
 37900138            for (int cap = -1; cap <= 1; cap += 2)
 139            {
 12634140                if (oblique)
 141                {
 8622142                    ProjectPerpendicular(edge, geometry.CapOffset(vertex, cap), direction, signs);
 8622143                    KeepAxis(geometry, coreAxis, region, direction, signs, ref selection, hull);
 144                }
 12634145                if (HasVertexRimSeparation(geometry, vertex, cap, coreAxis, region, hull))
 146                {
 2147                    selection.GapSign = -1;
 2148                    return;
 149                }
 150            }
 151        }
 2605152    }
 153
 154    private static void KeepAxis(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, int region, scoped S
 155        scoped Span<int> directionSigns, ref Selection selection, in CylinderHullSupport hull)
 156    {
 44679157        if (selection.HasValue && selection.GapSign < 0)
 295158            return;
 44384159        if (directionSigns[0] == 0 && directionSigns[1] == 0 && directionSigns[2] == 0)
 596160            return;
 43788161        Span<ulong> values = stackalloc ulong[ConvexContactCandidate.Slots * Words];
 43788162        Span<int> signs = stackalloc int[ConvexContactCandidate.Slots];
 43788163        Span<ulong> projection = stackalloc ulong[Words];
 43788164        int alignment = 0;
 43788165        if (!coreAxis.IsZero)
 16235166            Dot(coreAxis, direction, directionSigns, projection, out alignment);
 43788167        bool invariantsReady = false;
 262728168        for (int orientation = 0; orientation < 2; orientation++)
 169        {
 87576170            if (coreAxis.IsZero || (region == 0 ? alignment == 0 : alignment * region > 0))
 171            {
 71397172                if (!hull.IsEmpty)
 173                {
 56846174                    int mask = geometry.GetSupportMask(direction, directionSigns);
 56846175                    int vertex = (mask & 1) != 0 ? 0 : (mask & 2) != 0 ? 1 : 2;
 56846176                    if (!hull.Supports(geometry, geometry.Vertex(vertex), direction, directionSigns)) goto Reverse;
 177                }
 41271178                int gapSign = BuildAxis(geometry, direction, directionSigns, values, signs, invariantsReady, out int sup
 41271179                invariantsReady = true;
 41271180                var candidate = new ConvexContactCandidate(values, signs, gapSign);
 41271181                if (!selection.HasValue || WideConvexPrismRelations.CompareConvexContactCandidates(candidate,
 41271182                        new ConvexContactCandidate(selection.Values, selection.Signs, selection.GapSign)) < 0)
 183                {
 12624184                    values.CopyTo(selection.Values); signs.CopyTo(selection.Signs);
 12624185                    direction.CopyTo(selection.Direction); directionSigns.CopyTo(selection.DirectionSigns);
 18936186                    selection.HasValue = true; selection.GapSign = gapSign; selection.SupportMask = supportMask;
 6312187                    selection.CoreSign = region;
 188                }
 189            }
 190        Reverse:
 700608191            for (int component = 0; component < 3; component++)
 262728192                directionSigns[component] = -directionSigns[component];
 87576193            alignment = -alignment;
 194        }
 43788195    }
 196
 197    private static int BuildAxis(in TriangleCircularGeometry geometry,
 198        ReadOnlySpan<ulong> direction, ReadOnlySpan<int> directionSigns,
 199        Span<ulong> values, Span<int> signs, bool invariantsReady, out int mask)
 200    {
 82542201        values[..(9 * Words)].Clear(); signs.Clear();
 41271202        Span<ulong> work = stackalloc ulong[8 * Words];
 123813203        Span<ulong> rational = Slot(work, 0), current = Slot(work, 1), difference = Slot(work, 2);
 123813204        Span<ulong> temporary = Slot(work, 3), radial = Slot(work, 4), scale = Slot(work, 5);
 82542205        Span<ulong> metric = Slot(work, 6), denominator = Slot(work, 7);
 41271206        Dot(geometry.A, direction, directionSigns, rational, out int rationalSign);
 41271207        mask = 1;
 247626208        for (int vertex = 1; vertex < 3; vertex++)
 209        {
 82542210            Dot(geometry.Vertex(vertex), direction, directionSigns, current, out int currentSign);
 82542211            current.CopyTo(difference);
 82542212            int comparison = currentSign;
 82542213            Add(rational, -rationalSign, difference, ref comparison);
 82542214            if (comparison > 0)
 215            {
 64938216                current.CopyTo(rational); rationalSign = currentSign; mask = 1 << vertex;
 217            }
 60896218            else if (comparison == 0)
 31870219                mask |= 1 << vertex;
 220        }
 41271221        Import(geometry.HalfHeight, current);
 41271222        WideArithmetic.MultiplyMagnitudes(current, Slot(direction, 1), temporary);
 41271223        Add(temporary, 1, rational, ref rationalSign);
 224        // +/-N have identical R²|N_h|² and RawScale²|N|². Borrow the
 225        // working candidate's disjoint slots only after an admitted build;
 226        // a core region may admit the second orientation alone.
 41271227        if (invariantsReady)
 7585228            return BuildRadialCandidate(rational, rationalSign, Slot(values, 9), Slot(values, 10), values, signs);
 33686229        WideArithmetic.MultiplyMagnitudes(Slot(direction, 0), Slot(direction, 0), radial);
 33686230        WideArithmetic.MultiplyMagnitudes(Slot(direction, 2), Slot(direction, 2), temporary);
 33686231        WideArithmetic.AddMagnitudeInto(temporary, radial);
 33686232        Import(geometry.Radius, current);
 33686233        WideArithmetic.MultiplyMagnitudes(current, current, temporary);
 33686234        WideArithmetic.MultiplyMagnitudes(radial, temporary, current);
 33686235        SumSquares(direction, metric);
 33686236        Import(Signed576.ExtendValue(geometry.RawScale), scale);
 33686237        WideArithmetic.MultiplyMagnitudes(scale, scale, temporary);
 33686238        WideArithmetic.MultiplyMagnitudes(metric, temporary, denominator);
 33686239        return BuildRadialCandidate(rational, rationalSign, current, denominator, values, signs);
 240    }
 241
 242    private static bool HasVertexRimSeparation(in TriangleCircularGeometry geometry, int vertex, int cap,
 243        in WideDirection3d coreAxis, int region, in CylinderHullSupport hull)
 244    {
 12634245        WideDirection3d c = geometry.CapOffset(vertex, cap);
 12634246        if (c.Y.Sign * cap > 0)
 7715247            return false;
 4919248        Signed832 radialSquared = WideArithmetic.AddSigned832(
 4919249            WideArithmetic.MultiplySigned576(c.X, c.X), WideArithmetic.MultiplySigned576(c.Z, c.Z));
 4919250        if (WideArithmetic.SubtractSigned832(radialSquared,
 4919251                WideArithmetic.MultiplySigned576(geometry.Radius, geometry.Radius)).Sign <= 0)
 802252            return false;
 4117253        Span<ulong> work = stackalloc ulong[4 * Words];
 16468254        Span<ulong> rational = Slot(work, 0), radical = Slot(work, 1), root = Slot(work, 2), radius = Slot(work, 3);
 8234255        Import(radialSquared, root); Import(geometry.Radius, radius);
 256        // The endpoint's closest-rim residual is a separator of the whole
 257        // stadium only in this endpoint's open core-normal hemisphere.
 4117258        if (!coreAxis.IsZero && ResidualProjectionSign(c, coreAxis, rational, radical, root, radius) * region <= 0)
 1513259            return false;
 7178260        for (int other = 0; other < 3; other++)
 261        {
 3576262            if (other == vertex)
 263                continue;
 2690264            if (ResidualProjectionSign(c, geometry.EdgeFromTo(other, vertex), rational, radical, root, radius) < 0)
 2591265                return false;
 266        }
 60267        for (int other = 0; other < hull.Count; other++)
 28268            if (ResidualProjectionSign(c, TriangleCircularGeometry.Subtract(geometry.Vertex(vertex), geometry.TransformH
 28269                    rational, radical, root, radius) < 0)
 11270                return false;
 2271        return true;
 272    }
 273
 274    private static int ResidualProjectionSign(in WideDirection3d c, in WideDirection3d axis, Span<ulong> rational,
 275        Span<ulong> radical, ReadOnlySpan<ulong> root, ReadOnlySpan<ulong> radius)
 276    {
 4360277        Signed832 planar = WideArithmetic.AddSigned832(
 4360278            WideArithmetic.MultiplySigned576(c.X, axis.X), WideArithmetic.MultiplySigned576(c.Z, axis.Z));
 4360279        Import(planar, radical);
 4360280        WideArithmetic.MultiplyMagnitudes(radical, radius, rational);
 4360281        Signed832 full = WideDirection3d.Dot(c, axis);
 4360282        Import(full, radical);
 4360283        return WideConvexPrismRelations.GetConvexContactCandidateQuadraticSign(
 4360284            rational, planar.Sign, radical, -full.Sign, root);
 285    }
 286
 287    private ref struct Selection
 288    {
 289        internal Span<ulong> Values, Direction;
 290        internal Span<int> Signs, DirectionSigns;
 291        internal int GapSign, SupportMask, CoreSign;
 292        internal bool HasValue;
 293        internal Selection(Span<ulong> values, Span<int> signs, Span<ulong> direction, Span<int> directionSigns)
 294        {
 12844295            Values = values; Signs = signs; Direction = direction; DirectionSigns = directionSigns;
 6422296            GapSign = SupportMask = CoreSign = 0; HasValue = false;
 3211297        }
 298    }
 299}

Methods/Properties

get_Up()
TryGetSeed(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.WideDirection3d&,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&,FixedMathSharp.Geometry.CylinderHullSupport&,System.Boolean)
TryGetBest(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.WideDirection3d&,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.Boolean&,FixedMathSharp.Geometry.CylinderHullSupport&)
KeepSeam(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.TriangleCylinderAnalyticFeatures/Selection&,FixedMathSharp.Geometry.CylinderHullSupport&)
KeepRegion(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.WideDirection3d&,System.Int32,FixedMathSharp.Geometry.TriangleCylinderAnalyticFeatures/Selection&,System.Boolean&,FixedMathSharp.Geometry.CylinderHullSupport&)
KeepAxis(FixedMathSharp.Geometry.TriangleCircularGeometry&,FixedMathSharp.Geometry.WideDirection3d&,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,FixedMathSharp.Geometry.TriangleCylinderAnalyticFeatures/Selection&,FixedMathSharp.Geometry.CylinderHullSupport&)
BuildAxis(FixedMathSharp.Geometry.TriangleCircularGeometry&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Boolean,System.Int32&)
HasVertexRimSeparation(FixedMathSharp.Geometry.TriangleCircularGeometry&,System.Int32,System.Int32,FixedMathSharp.Geometry.WideDirection3d&,System.Int32,FixedMathSharp.Geometry.CylinderHullSupport&)
ResidualProjectionSign(FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideDirection3d&,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
.ctor(System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>)