< Summary

Information
Class: FixedMathSharp.Geometry.CylinderHullSupport
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/CylinderHullSupport.cs
Line coverage
100%
Covered lines: 109
Uncovered lines: 0
Coverable lines: 109
Total lines: 188
Line coverage: 100%
Branch coverage
100%
Covered branches: 68
Total branches: 68
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_IsEmpty()100%11100%
get_Count()100%11100%
.ctor(...)100%66100%
Vertex(...)100%11100%
Supports(...)100%44100%
Supports(...)100%44100%
GetCoreValueShift(...)100%22100%
HasExtrudedBallMinimumCertificate(...)100%3232100%
HasBallMinimumCertificate(...)100%2020100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// CylinderHullSupport.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>A single exact cylinder chart shared by every feature of a convex hull.</summary>
 14internal readonly ref struct CylinderHullSupport
 15{
 16    private readonly ReadOnlySpan<Vector3d> points;
 17    internal readonly CylinderPolytopeFrame Frame;
 18    private readonly Signed320 divisor;
 19    internal readonly Signed320 RawScale;
 20    internal readonly Signed576 HalfHeight, Radius;
 21    internal readonly WideRationalBasis3d WorldBasis;
 22    internal readonly int ValueShift;
 23    internal readonly bool IsAffine;
 8594824    internal bool IsEmpty => points.IsEmpty;
 40558925    internal int Count => points.Length;
 26
 27    internal CylinderHullSupport(in ScaledRigidFrame3d source, ReadOnlySpan<Vector3d> points,
 28        Vector3d center, FixedQuaternion rotation, Fixed64 height, Fixed64 radius)
 29    {
 24730        this.points = points;
 24731        IsAffine = !source.IsRigid;
 24732        Frame = IsAffine ? new CylinderPolytopeFrame(source, center, rotation, Signed192.Raw(height), radius)
 24733            : new CylinderPolytopeFrame(center, rotation, height, radius, source.RigidOrigin, source.Rotation);
 24734        WorldBasis = new WideRationalBasis3d(rotation);
 24735        Signed320 scale = WideArithmetic.AddSigned320(Frame.CoordinateDenominator, Frame.CoordinateDenominator);
 24736        Signed320 radial = Signed320.NarrowValue(WideArithmetic.MultiplySigned320(Frame.CoordinateDenominator, Frame.Rad
 24737        divisor = WideArithmetic.GetGreatestCommonDivisor(WideArithmetic.GetGreatestCommonDivisor(scale, Frame.Cap), rad
 439838        for (int i = 0; i < points.Length; i++)
 39        {
 195240            WideAxis3 point = Frame.Transform(points[i]);
 195241            divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, point.X);
 195242            divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, point.Y);
 195243            divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, point.Z);
 44        }
 24745        RawScale = WideArithmetic.DivideExactSigned320(scale, divisor);
 24746        HalfHeight = Signed576.ExtendValue(WideArithmetic.DivideExactSigned320(Frame.Cap, divisor));
 24747        Radius = Signed576.ExtendValue(WideArithmetic.DivideExactSigned320(radial, divisor));
 24748        Span<Signed576> bounds = stackalloc Signed576[3] { HalfHeight, Radius, default };
 24749        ValueShift = TriangleCircularGeometry.GetValueShift(bounds, 4);
 439850        for (int i = 0; i < points.Length; i++)
 51        {
 195252            WideDirection3d point = Vertex(i);
 585653            bounds[0] = point.X; bounds[1] = point.Y; bounds[2] = point.Z;
 195254            ValueShift = Math.Max(ValueShift, TriangleCircularGeometry.GetValueShift(bounds, 4));
 55        }
 24756    }
 57
 58    internal WideDirection3d Vertex(int index)
 59    {
 39946960        WideAxis3 point = Frame.Transform(points[index]);
 39946961        return new WideDirection3d(WideArithmetic.DivideExactSigned320(point.X, divisor),
 39946962            WideArithmetic.DivideExactSigned320(point.Y, divisor), WideArithmetic.DivideExactSigned320(point.Z, divisor)
 63    }
 64
 65    internal bool Supports(in WideDirection3d point, ReadOnlySpan<ulong> direction, ReadOnlySpan<int> signs)
 66    {
 192867        Span<ulong> projection = stackalloc ulong[Words];
 3339468        for (int i = 0; i < Count; i++)
 69        {
 1485470            Dot(TriangleCircularGeometry.Subtract(point, Vertex(i)), direction, signs, projection, out int sign);
 1493971            if (sign < 0) return false;
 72        }
 184373        return true;
 74    }
 75
 76    internal bool Supports(in TriangleCircularGeometry geometry, in WideDirection3d point,
 77        ReadOnlySpan<ulong> direction, ReadOnlySpan<int> signs)
 78    {
 5979879        Span<ulong> projection = stackalloc ulong[Words];
 69307480        for (int i = 0; i < Count; i++)
 81        {
 31771482            Dot(TriangleCircularGeometry.Subtract(point, geometry.TransformHullVertex(Vertex(i))),
 31771483                direction, signs, projection, out int sign);
 34868984            if (sign < 0) return false;
 85        }
 2882386        return true;
 87    }
 88
 89    internal int GetCoreValueShift(in TriangleCircularGeometry chart, in WideDirection3d coreOffset)
 90    {
 12191        Span<Signed576> bounds = stackalloc Signed576[4] { chart.HalfHeight, chart.Radius, coreOffset.X, coreOffset.Z };
 12192        int shift = TriangleCircularGeometry.GetValueShift(bounds, 5);
 217893        for (int i = 0; i < Count; i++)
 94        {
 96895            WideDirection3d point = chart.TransformHullVertex(Vertex(i));
 290496            bounds[0] = point.X; bounds[1] = point.Y; bounds[2] = point.Z;
 96897            shift = Math.Max(shift, TriangleCircularGeometry.GetValueShift(bounds, 5));
 98        }
 12199        return shift;
 100    }
 101
 102    internal bool HasExtrudedBallMinimumCertificate(in ScaledRigidFrame3d source, ReadOnlySpan<int> triangles,
 103        in TriangleCircularGeometry chart, in WideDirection3d coreOffset, in WideDirection3d coreAxis, ConvexContactCand
 104    {
 105        // Hull+core has only original-face and edge-cross-core facets. Its
 106        // centered ball plus the cylinder's ball bounds every slab exit below.
 74107        Signed576 ball = WideArithmetic.SubtractSigned576(chart.Radius, chart.HalfHeight).Sign <= 0 ? chart.Radius : cha
 148108        Span<ulong> direction = stackalloc ulong[3 * Words], values = stackalloc ulong[ConvexContactCandidate.Slots * Wo
 148109        Span<int> directionSigns = stackalloc int[3], signs = stackalloc int[ConvexContactCandidate.Slots];
 74110        Span<ulong> work = stackalloc ulong[7 * Words];
 148111        values.Clear(); signs.Clear();
 1392112        for (int index = 0; index < triangles.Length; index += 3)
 113        {
 1950114            int a = triangles[index], b = triangles[index + 1], c = triangles[index + 2];
 650115            var geometry = new TriangleCircularGeometry(new FixedTriangle(points[a], points[b], points[c]), source, this
 650116                .WithCoreChart(chart);
 6384117            for (int feature = 0; feature < 4; feature++)
 118            {
 2570119                WideDirection3d normal = feature == 0 ? geometry.FaceNormal : WideDirection3d.Cross(geometry.Edge(featur
 2716120                if (normal.IsZero) { if (feature == 0) return false; continue; }
 2425121                WideDirection3d point = geometry.Vertex(feature == 0 ? 0 : feature - 1);
 2425122                WriteDirection(normal, direction, directionSigns);
 2425123                Dot(point, direction, directionSigns, Slot(work, 0), out int planeSign);
 2425124                if (planeSign < 0)
 10417125                    for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[comp
 2425126                if (!Supports(geometry, point, direction, directionSigns))
 127                {
 1082128                    if (planeSign != 0) { if (feature == 0) return false; continue; }
 803129                    for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[comp
 139130                    if (!Supports(geometry, point, direction, directionSigns)) { if (feature == 0) return false; continu
 131                }
 1859132                Dot(coreOffset, direction, directionSigns, Slot(work, 6), out _);
 1859133                WideArithmetic.AddMagnitudeInto(Slot(work, 6), Slot(work, 0));
 1859134                SumSquares(direction, Slot(work, 1));
 1859135                Import(ball, Slot(work, 2));
 1859136                WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3));
 1859137                WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 4));
 1859138                Import(Signed576.ExtendValue(chart.RawScale), Slot(work, 2));
 1859139                WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3));
 1859140                WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 5));
 1859141                int gap = BuildRadialCandidate(Slot(work, 0), IsZero(Slot(work, 0)) ? 0 : 1, Slot(work, 4), Slot(work, 5
 1878142                if (WideConvexPrismRelations.CompareConvexContactCandidates(new ConvexContactCandidate(values, signs, ga
 143            }
 144        }
 46145        return true;
 146    }
 147
 148    internal bool HasBallMinimumCertificate(in ScaledRigidFrame3d source, ReadOnlySpan<int> triangles,
 149        ConvexContactCandidate best)
 150    {
 151        // Hull contains B(d) and cylinder contains B(min(R,H)); their sum
 152        // bounds every exit below by d+min(R,H), with no rounded square root.
 401153        Signed576 ball = WideArithmetic.SubtractSigned576(Radius, HalfHeight).Sign <= 0 ? Radius : HalfHeight;
 802154        Span<ulong> direction = stackalloc ulong[3 * Words], values = stackalloc ulong[ConvexContactCandidate.Slots * Wo
 802155        Span<int> directionSigns = stackalloc int[3], signs = stackalloc int[ConvexContactCandidate.Slots];
 401156        Span<ulong> work = stackalloc ulong[6 * Words];
 802157        values.Clear(); signs.Clear();
 3974158        for (int index = 0; index < triangles.Length; index += 3)
 159        {
 5754160            int a = triangles[index], b = triangles[index + 1], c = triangles[index + 2];
 1918161            var triangle = new FixedTriangle(points[a], points[b], points[c]);
 1918162            var geometry = new TriangleCircularGeometry(triangle, source, this, a, b, c);
 1919163            if (geometry.FaceNormal.IsZero) return false;
 1917164            WriteDirection(geometry.FaceNormal, direction, directionSigns);
 1917165            Dot(geometry.A, direction, directionSigns, Slot(work, 0), out int planeSign);
 1917166            if (planeSign < 0)
 2409167                for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[componen
 1917168            if (!Supports(geometry.A, direction, directionSigns))
 169            {
 157170                if (planeSign != 0) return false;
 121171                for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[componen
 12172                if (!Supports(geometry.A, direction, directionSigns)) return false;
 173            }
 1843174            SumSquares(direction, Slot(work, 1));
 1843175            Import(ball, Slot(work, 2));
 1843176            WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3));
 1843177            WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 4));
 1843178            Import(Signed576.ExtendValue(RawScale), Slot(work, 2));
 1843179            WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3));
 1843180            WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 5));
 1843181            int gap = BuildRadialCandidate(Slot(work, 0), planeSign == 0 ? 0 : 1,
 1843182                Slot(work, 4), Slot(work, 5), values, signs);
 1843183            if (WideConvexPrismRelations.CompareConvexContactCandidates(new ConvexContactCandidate(values, signs, gap), 
 257184                return false;
 185        }
 69186        return true;
 187    }
 188}