< Summary

Information
Class: FixedMathSharp.Geometry.WideConvexPrismRelations
Assembly: FixedMathSharp
File(s): File 1: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.Axis3.cs
File 2: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleDomain.cs
File 3: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleEdges.cs
File 4: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleSlab.cs
File 5: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleSlabRim.cs
File 6: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.ContactCandidate.cs
File 7: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.cs
File 8: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CurvedAdmission.cs
File 9: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderCapsuleEllipse.cs
File 10: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderCapsuleFeatures.cs
File 11: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderCylinderPenetration.cs
File 12: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderPairContact.cs
File 13: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderPairDepth.cs
File 14: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderPairSideFeatures.cs
File 15: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.Projection.cs
File 16: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.ProjectionArithmetic.cs
File 17: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.ProjectionDepth.cs
File 18: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.RigidCylinderPairs.cs
File 19: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.RigidCylinderPairs.Rounding.cs
File 20: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.SphereBoundary.cs
File 21: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.SphereDomain.cs
File 22: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.Support.cs
File 23: /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.SweptUprightCylinder.cs
Line coverage
100%
Covered lines: 4056
Uncovered lines: 0
Coverable lines: 4056
Total lines: 6960
Line coverage: 100%
Branch coverage
100%
Covered branches: 1354
Total branches: 1354
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%11100%
File 1: get_IsZero()100%44100%
File 2: TryClipProjectedPrismFace(...)100%4040100%
File 2: GetPrismInteriorParameter(...)100%22100%
File 2: IsProjectedPrismPointWithinCapsule(...)100%2020100%
File 3: TryGetPrismEdgeContact(...)100%3838100%
File 3: .cctor()100%11100%
File 3: KeepPrismEdgeCandidates(...)100%3434100%
File 3: NarrowPrismEdgeParameter(...)100%11100%
File 3: GetClippedEdgeParameter(...)100%66100%
File 3: KeepPrismEdgeEndpoint(...)100%44100%
File 3: .ctor(...)100%44100%
File 3: SetEdge(...)100%11100%
File 3: Reset()100%11100%
File 3: Keep(...)100%3232100%
File 3: SameTarget(...)100%44100%
File 3: CopyTarget(...)100%11100%
File 3: TryGetContact(...)100%44100%
File 3: WithinRadius(...)100%11100%
File 3: Supports(...)100%1414100%
File 3: SupportsVertex(...)100%88100%
File 3: TryGetPrismEdgeDirection(...)100%1010100%
File 3: GetPrismEdgeVertex(...)100%11100%
File 3: PrismEdgeDot(...)100%11100%
File 3: ClampPrismEdgeParameter(...)100%44100%
File 3: MaterializePrismEdgeContact(...)100%22100%
File 3: MaterializePrismEdgeAnchor(...)100%1010100%
File 4: .ctor(...)100%22100%
File 4: CapOffset(...)100%44100%
File 4: EndpointOffset(...)100%11100%
File 4: Keep(...)100%11100%
File 4: KeepDirection()100%22100%
File 4: get_Best()100%11100%
File 4: TryGetCenteredCapsuleSlabCapsulePenetration(...)100%5252100%
File 4: BuildCapsuleSlabDirection(...)100%44100%
File 4: HasCapsuleSlabStraightRimCertificate(...)100%1010100%
File 4: KeepCapsuleSlabHorizontalCorners(...)100%44100%
File 4: BuildCapsuleSlabEndpointRim(...)100%1616100%
File 5: KeepParameter(...)100%11100%
File 5: TryImproveCapsuleSlabRim(...)100%2424100%
File 5: KeepCapsuleSlabRimCharts(...)100%88100%
File 5: KeepCapsuleSlabRimChart(...)100%3434100%
File 5: GetCapsuleSlabRimNormal(...)100%11100%
File 6: CompareConvexContactCandidates(...)100%44100%
File 6: CompareConvexContactCandidateDepthToTwiceRaw(...)100%44100%
File 6: GetRoundedConvexContactCandidateDepth(...)100%1414100%
File 6: GetConvexContactCandidateNormal(...)100%11100%
File 6: GetConvexContactCandidateScaledNormal(...)100%1212100%
File 6: BuildConvexContactCandidateNormalSquare(...)100%22100%
File 6: GetRoundedConvexContactCandidateNormalComponent(...)100%88100%
File 6: GetConvexContactCandidateMagnitudeFloorBounds(...)100%22100%
File 6: GetConvexContactCandidateNormalFloorBounds(...)100%22100%
File 6: GetConvexContactCandidateQuadraticBounds(...)100%22100%
File 6: AddConvexContactCandidateRadicalBounds(...)100%88100%
File 6: CompareConvexContactCandidateNormalComponentToTwiceRaw(...)100%11100%
File 6: GetConvexContactCandidateQuadraticSign(...)100%1212100%
File 6: GetConvexContactCandidateThreeTermSign(...)100%2424100%
File 6: GetConvexContactCandidateRankRootBounds(...)100%22100%
File 6: CopyConvexContactCandidateSignedRadicand(...)100%44100%
File 6: ConvexContactCandidateLength(...)100%11100%
File 6: ConvexContactCandidateProductWords(...)100%11100%
File 7: TryGetSphereContact(...)100%44100%
File 7: VisitCapsuleContacts(...)100%5252100%
File 7: TryGetCenteredCapsuleContact(...)100%11100%
File 7: TryGetCenteredCylinderContact(...)100%11100%
File 7: TryGetCenteredConeContact(...)100%11100%
File 7: TryGetContact(...)100%11100%
File 7: TryGetContact(...)100%3636100%
File 7: TryKeepAxis(...)100%2020100%
File 7: GetPrismProjection(...)100%1010100%
File 7: GetEndpointToPrismVertexAxis(...)100%11100%
File 7: GetScaledEndpointDifference(...)100%11100%
File 7: GetPlanarEdge(...)100%11100%
File 7: GetPlanarVertexAxis(...)100%11100%
File 7: Cross(...)100%11100%
File 7: SubtractProducts(...)100%11100%
File 7: GetDifferenceProjection(...)100%11100%
File 7: GetRotatedPlanarProjection(...)100%11100%
File 7: ScaleProjection(...)100%11100%
File 8: IntersectsCurvedPrism(...)100%22100%
File 8: IntersectsCurvedPrism(...)100%3030100%
File 8: PrismContainsPoint(...)100%44100%
File 8: .ctor(...)100%11100%
File 8: Vertex(...)100%11100%
File 8: ScaleCoordinate(...)100%11100%
File 8: Contains(...)100%3030100%
File 8: IntersectsFace(...)100%2222100%
File 8: IntersectsEdge(...)100%44100%
File 8: CurvedDot(...)100%11100%
File 8: Relative(...)100%11100%
File 8: CurvedDifferenceProduct(...)100%11100%
File 8: FootCoordinate(...)100%11100%
File 8: SetConeCoefficient(...)100%11100%
File 9: .ctor(...)100%11100%
File 9: TryPrepareCylinderCapsuleEllipse(...)100%22100%
File 9: ReduceCylinderCapsuleEllipseAxis(...)100%11100%
File 9: OrientCylinderCapsuleEllipseAxis(...)100%22100%
File 9: GetCylinderCapsuleEllipseProjection(...)100%11100%
File 9: TryImproveCylinderCapsuleEllipse(...)100%1212100%
File 9: CompareCylinderCapsuleEllipseSquaredGap(...)100%1212100%
File 9: CompareCylinderCapsuleEllipseDepthToTwiceRaw(...)100%66100%
File 9: CompareCylinderCapsuleEllipseMagnitudeToTwiceRaw(...)100%22100%
File 9: GetRoundedCylinderCapsuleEllipseDepth(...)100%2020100%
File 9: GetRoundedCylinderCapsuleEllipseNormalComponent(...)100%88100%
File 9: BuildCylinderCapsuleEllipsePolynomials(...)100%11100%
File 9: BuildCylinderCapsuleEllipseNormalPolynomials(...)100%1010100%
File 9: CopyCylinderCapsuleEllipseMagnitude(...)100%11100%
File 9: SquareCylinderCapsuleEllipseMagnitude(...)100%11100%
File 9: ScaleCylinderCapsuleEllipsePolynomial(...)100%22100%
File 9: AddCylinderCapsuleEllipseCoefficient(...)100%11100%
File 10: .ctor(...)100%11100%
File 10: .ctor(...)100%11100%
File 10: get_IsZero()100%44100%
File 10: .ctor(...)100%11100%
File 10: EndpointOffset(...)100%11100%
File 10: GetCylinderCapsuleScaledHalf(...)100%11100%
File 10: GetCylinderCapsuleEndpointCoordinate(...)100%88100%
File 10: DotCylinderCapsuleDirection(...)100%11100%
File 10: DotCylinderCapsuleDirection(...)100%11100%
File 10: ProjectCylinderCapsulePerpendicular(...)100%11100%
File 10: GetCylinderCapsulePerpendicular(...)100%44100%
File 10: ImportCylinderCapsuleFeature(...)100%11100%
File 10: ImportCylinderCapsuleFeature(...)100%11100%
File 10: ImportCylinderCapsuleFeature(...)100%11100%
File 10: ImportCylinderCapsuleFeature(...)100%11100%
File 10: CylinderCapsuleFeatureSlot(...)100%11100%
File 10: BuildCylinderCapsuleAxisCandidate(...)100%1414100%
File 10: BuildCylinderCapsuleEndpointRimCandidate(...)100%1010100%
File 10: BuildCylinderCapsuleProjectedCornerCandidate(...)100%66100%
File 10: KeepCylinderCapsuleCandidate(...)100%44100%
File 10: KeepCylinderCapsuleDirection(...)100%22100%
File 10: TryGetCenteredFiniteCylinderCapsuleContact(...)100%22100%
File 10: TryGetCenteredFiniteCylinderCapsulePenetration(...)100%4040100%
File 10: MaterializeCylinderCapsulePenetration(...)100%44100%
File 10: CreateCylinderCapsuleFeatureContact(...)100%11100%
File 11: .ctor(...)100%11100%
File 12: TryGetNonparallelCylinderPairPenetration(...)100%1010100%
File 12: TrySelectCylinderPairFeature(...)100%3030100%
File 12: TryKeepCylinderPairCapValues(...)100%1414100%
File 12: ShouldKeepCylinderPairValue(...)100%44100%
File 12: TryHandleRepeatedCylinderPairValue(...)100%1414100%
File 12: .ctor(...)100%11100%
File 12: KeepTouch(...)100%22100%
File 12: KeepRoot(...)100%11100%
File 12: KeepSimpleRoot(...)100%11100%
File 13: .ctor(...)100%11100%
File 13: get_RadialCoefficient()100%11100%
File 14: TryGetCylinderPairSideFeature(...)100%5252100%
File 14: GetKnownCylinderPairSideUnshiftedRoot(...)100%88100%
File 14: CompareCylinderPairSideUnshiftedEndpoint(...)100%44100%
File 15: GetCapsuleProjectionDepths(...)100%11100%
File 15: IsProjectionNonNegative(...)100%22100%
File 15: IsSphereProjectionWithinRadius(...)100%11100%
File 15: CompareProjectionDepths(...)100%66100%
File 15: GetRoundedDepth(...)100%1010100%
File 15: GetDepthApproximation(...)100%44100%
File 15: GetCeilingSquareRoot(...)100%22100%
File 15: GetNearestEvenIncrement(...)100%11100%
File 15: CompareProjectionDepthToTwiceRaw(...)100%66100%
File 15: GetAxisProjection(...)100%11100%
File 15: GetAxisSquared(...)100%11100%
File 15: GetAxisSquared(...)100%11100%
File 15: GetMagnitude(...)100%22100%
File 15: BuildLocalCapsuleRadicand(...)100%11100%
File 15: BuildThresholdCapsuleRadicand(...)100%11100%
File 15: BuildRationalSquare(...)100%11100%
File 15: BuildThresholdRadicand(...)100%11100%
File 15: CompareRadicalPairs(...)100%1010100%
File 16: GetMagnitude576(...)100%22100%
File 16: BuildProduct(...)100%11100%
File 16: BuildProduct(...)100%11100%
File 16: BuildProduct(...)100%11100%
File 16: BuildProduct(...)100%11100%
File 16: MultiplyBy(...)100%11100%
File 16: MultiplyBy(...)100%11100%
File 16: MultiplyBy(...)100%11100%
File 16: GetMagnitude(...)100%11100%
File 16: CopyPositiveRadicand(...)100%22100%
File 16: CombineWideSignedMagnitudes(...)100%1616100%
File 16: ShiftLeft(...)100%44100%
File 16: IsZero(...)100%11100%
File 17: .ctor(...)100%11100%
File 18: TryGetCenteredFiniteCylindersContact(...)100%88100%
File 18: TryGetPositiveRadiusCylinderPairPenetration(...)100%1010100%
File 18: TryKeepCylinderCylinderAxis(...)100%1010100%
File 18: GetCylinderPairDepthSign(...)100%11100%
File 18: CompareCylinderPairDepths(...)100%11100%
File 19: GetRoundedCylinderCylinderDepth(...)100%44100%
File 20: TryGetSphereBoundaryFaceContact(...)100%3434100%
File 21: KeepSphereProjection(...)100%1010100%
File 21: AdmitPrismSpherePoint(...)100%88100%
File 21: KeepSphereEdgeAxes(...)100%2222100%
File 22: .cctor()100%11100%
File 22: .ctor(...)100%11100%
File 22: ToWide()100%11100%
File 22: .ctor(...)100%11100%
File 22: TryMaterializeContact(...)100%44100%
File 22: TryGetMatchedPrismAnchor(...)100%3232100%
File 22: TryGetPrismDomainParameter(...)100%88100%
File 22: GetPrismCoreAnchor(...)100%44100%
File 22: TryGetPrismSupportInterval(...)100%2626100%
File 22: GetPrismSupportProjection(...)100%1616100%
File 22: GetProjectedCapsuleSupport(...)100%11100%
File 22: GetPrismCapsuleRadialOffset(...)100%11100%
File 22: DotPrismSupport(...)100%11100%
File 22: GetPrismEdgeConstraint(...)100%11100%
File 22: MaterializePrismProjection(...)100%66100%
File 22: FromDirection(...)100%11100%
File 22: Compare(...)100%11100%
File 22: Negate(...)100%11100%
File 23: .ctor(...)100%11100%
File 23: get_Unit()100%11100%
File 23: ClipStrictLinear(...)100%44100%
File 23: KeepLower(...)100%22100%
File 23: KeepUpper(...)100%22100%
File 23: ValidateOrder()100%11100%
File 23: IntersectsSweptUprightCylinderStrict(...)100%44100%
File 23: ClipStrictVerticalOverlap(...)100%22100%
File 23: IntersectsStrictPlanarSweep(...)100%2222100%
File 23: IntersectsOpenCircle(...)100%11100%
File 23: IntersectsOpenEdgeStrip(...)100%44100%
File 23: HasStrictNegativeQuadratic(...)100%1212100%
File 23: EvaluateQuadratic(...)100%11100%
File 23: CompareRatioToBound(...)100%11100%
File 23: CompareRatios(...)100%11100%
File 23: GetSweepFootprintWinding(...)100%88100%
File 23: GetSweepEdge(...)100%11100%
File 23: Cross(...)100%11100%
File 23: Dot(...)100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.Axis3.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.Axis3.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.ContactQuadratic;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>Axis3 declaration for WideConvexPrismRelations.</content>
 14internal static partial class WideConvexPrismRelations
 15{
 16    internal readonly struct Axis3
 17    {
 18        internal readonly Signed320 X;
 19        internal readonly Signed320 Y;
 20        internal readonly Signed320 Z;
 21
 22        internal Axis3(Signed320 x, Signed320 y, Signed320 z)
 23        {
 11345224            X = x;
 11345225            Y = y;
 11345226            Z = z;
 11345227        }
 28
 2877229        internal bool IsZero => X.IsZero && Y.IsZero && Z.IsZero;
 30    }
 31}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleDomain.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CapsuleDomain.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Retains exact capsule support intervals in bounded native-prism face domains.</content>
 13internal static partial class WideConvexPrismRelations
 14{
 15    /// <summary>Clips an already projected support segment to a retained native side rectangle or cap.</summary>
 16    /// <remarks>
 17    /// Endpoints are raw world-coordinate offsets from the prism origin over pointD. Admitted geometry
 18    /// has reach at most 4096; native tangent-cut bounds use fewer than 162 bits. A cap's complete
 19    /// footprint has already been admitted by coreClip, so cap clipping needs only the supplied Y plane.
 20    /// </remarks>
 21    internal static bool TryClipProjectedPrismFace(ReadOnlySpan<Signed832> projected, Signed832 pointD,
 22        Fixed64 yaw, Vector2d start, Vector2d end, in RationalSegmentInterval tangentClip,
 23        Fixed64 lowerY, Fixed64 upperY, in RationalSegmentInterval coreClip,
 24        out RationalSegmentInterval retained, bool cap = false)
 25    {
 1426        retained = default;
 1427        var rotation = new WideConvex2dRelations.RotationFrame2d(yaw);
 1428        Signed320 metric = WideArithmetic.AddSigned320(
 1429            WideArithmetic.MultiplySigned192(rotation.Cosine, rotation.Cosine),
 1430            WideArithmetic.MultiplySigned192(rotation.Sine, rotation.Sine));
 1431        Signed832 localD = WideArithmetic.MultiplySigned832(pointD, metric);
 1432        Span<Signed832> local = stackalloc Signed832[6];
 8433        for (int endpoint = 0; endpoint < 2; endpoint++)
 34        {
 2835            int index = 3 * endpoint;
 2836            local[index] = WideArithmetic.MultiplySigned832(WideArithmetic.AddSigned832(
 2837                WideArithmetic.MultiplySigned832(projected[index], rotation.Cosine),
 2838                WideArithmetic.MultiplySigned832(projected[index + 2], rotation.Sine)), Scale);
 2839            local[index + 1] = WideArithmetic.MultiplySigned832(projected[index + 1], metric);
 2840            local[index + 2] = WideArithmetic.MultiplySigned832(WideArithmetic.SubtractSigned832(
 2841                WideArithmetic.MultiplySigned832(projected[index + 2], rotation.Cosine),
 2842                WideArithmetic.MultiplySigned832(projected[index], rotation.Sine)), Scale);
 43        }
 5644        Signed832 lowerN = default, lowerD = Signed832.ExtendValue(Signed192.Signed(1)), upperN = lowerD, upperD = lower
 1445        if (coreClip.IsClipped)
 46        {
 647            lowerN = coreClip.WideLowerNumerator; lowerD = coreClip.WideLowerDenominator;
 648            upperN = coreClip.WideUpperNumerator; upperD = coreClip.WideUpperDenominator;
 49        }
 50        // Native inversion divides by the actual c²+s². Projected numerators <551
 51        // bits become <617; retained tangent constraints stay below 780 bits.
 1452        if (!cap)
 53        {
 2454            Signed192 dx = WideArithmetic.Difference(end.X, start.X), dz = WideArithmetic.Difference(end.Y, start.Y);
 1355            if (dx.IsZero && dz.IsZero) return false;
 3356            Span<Signed832> x = stackalloc Signed832[2], z = stackalloc Signed832[2], side = stackalloc Signed832[2];
 6657            for (int endpoint = 0; endpoint < 2; endpoint++)
 58            {
 2259                x[endpoint] = WideArithmetic.SubtractSigned832(local[3 * endpoint],
 2260                    WideArithmetic.MultiplySigned832(localD, Signed192.Raw(start.X)));
 2261                z[endpoint] = WideArithmetic.SubtractSigned832(local[3 * endpoint + 2],
 2262                    WideArithmetic.MultiplySigned832(localD, Signed192.Raw(start.Y)));
 2263                side[endpoint] = WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned832(x[endpoint], dz),
 2264                    WideArithmetic.MultiplySigned832(z[endpoint], dx));
 65            }
 1166            if (!WideTriangleRelations.ClipScaledHalfPlane(side[0], side[1], ref lowerN, ref lowerD, ref upperN, ref upp
 1167                || !WideTriangleRelations.ClipScaledHalfPlane(WideArithmetic.SubtractSigned832(default, side[0]),
 1368                    WideArithmetic.SubtractSigned832(default, side[1]), ref lowerN, ref lowerD, ref upperN, ref upperD))
 69
 970            Signed192 delta = dx.IsZero ? dz : dx;
 971            ReadOnlySpan<Signed832> coordinate = dx.IsZero ? z : x;
 972            Signed320 tangentLowerN = tangentClip.IsClipped ? tangentClip.LowerNumerator : default;
 973            Signed320 tangentLowerD = tangentClip.IsClipped ? tangentClip.LowerDenominator : Signed320.One;
 974            Signed320 tangentUpperN = tangentClip.IsClipped ? tangentClip.UpperNumerator : Signed320.One;
 975            Signed320 tangentUpperD = tangentClip.IsClipped ? tangentClip.UpperDenominator : Signed320.One;
 976            Signed832 edgeD = WideArithmetic.MultiplySigned832(localD, delta);
 1877            Span<Signed832> low = stackalloc Signed832[2], high = stackalloc Signed832[2];
 5478            for (int endpoint = 0; endpoint < 2; endpoint++)
 79            {
 1880                low[endpoint] = WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned832(coordinate[endpoint], 
 1881                    WideArithmetic.MultiplySigned832(edgeD, tangentLowerN));
 1882                high[endpoint] = WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned832(edgeD, tangentUpperN)
 1883                    WideArithmetic.MultiplySigned832(coordinate[endpoint], tangentUpperD));
 1884                if (delta.Sign < 0)
 85                {
 286                    low[endpoint] = WideArithmetic.SubtractSigned832(default, low[endpoint]);
 287                    high[endpoint] = WideArithmetic.SubtractSigned832(default, high[endpoint]);
 88                }
 89            }
 990            if (!WideTriangleRelations.ClipScaledHalfPlane(low[0], low[1], ref lowerN, ref lowerD, ref upperN, ref upper
 1191                || !WideTriangleRelations.ClipScaledHalfPlane(high[0], high[1], ref lowerN, ref lowerD, ref upperN, ref 
 92        }
 993        Signed832 bottom = WideArithmetic.MultiplySigned832(localD, Signed192.Raw(lowerY));
 994        Signed832 top = WideArithmetic.MultiplySigned832(localD, Signed192.Raw(upperY));
 995        if (!WideTriangleRelations.ClipScaledHalfPlane(WideArithmetic.SubtractSigned832(local[1], bottom),
 996                WideArithmetic.SubtractSigned832(local[4], bottom), ref lowerN, ref lowerD, ref upperN, ref upperD)
 997            || !WideTriangleRelations.ClipScaledHalfPlane(WideArithmetic.SubtractSigned832(top, local[1]),
 1398                WideArithmetic.SubtractSigned832(top, local[4]), ref lowerN, ref lowerD, ref upperN, ref upperD)) return
 599        retained = new RationalSegmentInterval(lowerN, lowerD, upperN, upperD);
 5100        return true;
 101    }
 102
 103    private static void GetPrismInteriorParameter(in RationalSegmentInterval interval, out Signed832 n, out Signed832 d)
 104    {
 851105        if (!interval.IsClipped) { n = Signed832.ExtendValue(Signed192.Signed(1)); d = Signed832.ExtendValue(Signed192.S
 106        // Reduced-bound mediants are exact interior points independent of the
 107        // producer's coefficient scale. Native face bounds need <780 bits, so the sum fits.
 23108        Span<ulong> values = stackalloc ulong[52];
 23109        WideArithmetic.GetMagnitude(interval.WideLowerNumerator, values[..13]);
 23110        WideArithmetic.GetMagnitude(interval.WideLowerDenominator, values.Slice(13, 13));
 23111        WideArithmetic.GetMagnitude(interval.WideUpperNumerator, values.Slice(26, 13));
 23112        WideArithmetic.GetMagnitude(interval.WideUpperDenominator, values.Slice(39, 13));
 23113        RationalSegmentInterval.PackReducedParameter(values[..13], values.Slice(13, 13), out var ln, out var ld);
 23114        RationalSegmentInterval.PackReducedParameter(values.Slice(26, 13), values.Slice(39, 13), out var un, out var ud)
 46115        n = WideArithmetic.AddSigned832(ln, un); d = WideArithmetic.AddSigned832(ld, ud);
 23116    }
 117
 118    /// <summary>Tests a projected support point against the complete rotated capsule before physical/recovery classific
 119    /// <remarks>relativeCenter is the capsule center minus the projected coordinates' prism origin.</remarks>
 120    internal static bool IsProjectedPrismPointWithinCapsule(ReadOnlySpan<Signed832> projected, Signed832 pointD,
 121        Signed832 tN, Signed832 tD, Vector3d relativeCenter, FixedQuaternion rotation, Fixed64 length, Fixed64 radius)
 122    {
 123        // Projected coefficients <551/503 bits and an 832-bit unit parameter
 124        // leave transformed doubled coordinates <1466 bits and their norm <2934.
 125        const int words = 48;
 92126        Span<ulong> values = stackalloc ulong[28 * words]; Span<int> signs = stackalloc int[28];
 46127        var dx = ContactQuadratic.At(values, signs, 0, words);
 46128        var dy = ContactQuadratic.At(values, signs, 1, words);
 46129        var dz = ContactQuadratic.At(values, signs, 2, words);
 92130        var parameter = ContactQuadratic.At(values, signs, 3, words); parameter.Set(tN);
 92131        var remaining = ContactQuadratic.At(values, signs, 4, words); remaining.Set(WideArithmetic.SubtractSigned832(tD,
 92132        var sourceD = ContactQuadratic.At(values, signs, 5, words); sourceD.Set(pointD);
 46133        var denominator = ContactQuadratic.At(values, signs, 6, words);
 46134        var localD = ContactQuadratic.At(values, signs, 7, words);
 46135        var value = ContactQuadratic.At(values, signs, 8, words);
 46136        var work = ContactQuadratic.At(values, signs, 9, words);
 46137        var local = ContactQuadratic.At(values, signs, 10, words);
 92138        var squared = ContactQuadratic.At(values, signs, 11, words); squared.Clear();
 46139        var bound = ContactQuadratic.At(values, signs, 12, words);
 46140        var term = ContactQuadratic.At(values, signs, 13, words);
 92141        value.Set(tD); ContactQuadratic.Multiply(sourceD, value, ReadOnlySpan<ulong>.Empty, denominator);
 368142        for (int axis = 0; axis < 3; axis++)
 143        {
 138144            ContactQuadratic coordinate = axis == 0 ? dx : axis == 1 ? dy : dz;
 276145            value.Set(projected[axis]); ContactQuadratic.Multiply(value, remaining, ReadOnlySpan<ulong>.Empty, coordinat
 414146            value.Set(projected[axis + 3]); ContactQuadratic.Multiply(value, parameter, ReadOnlySpan<ulong>.Empty, work)
 276147            ContactQuadratic.Scale(denominator, Signed576.ExtendValue(Signed192.Raw(relativeCenter[axis])), work); coord
 148        }
 46149        var basis = new WideRationalBasis3d(rotation);
 46150        ContactQuadratic.Scale(denominator, Signed576.ExtendValue(basis.Denominator), localD);
 46151        ReadOnlySpan<Signed192> coefficients = stackalloc Signed192[9]
 46152            { basis.Xx, basis.Xy, basis.Xz, basis.Yx, basis.Yy, basis.Yz, basis.Zx, basis.Zy, basis.Zz };
 46153        Signed576 two = Signed576.ExtendValue(Signed192.Signed(2));
 368154        for (int axis = 0; axis < 3; axis++)
 155        {
 138156            local.Clear();
 1104157            for (int component = 0; component < 3; component++)
 158            {
 414159                ContactQuadratic coordinate = component == 0 ? dx : component == 1 ? dy : dz;
 828160                ContactQuadratic.Scale(coordinate, Signed576.ExtendValue(coefficients[3 * axis + component]), work); loc
 161            }
 276162            ContactQuadratic.Scale(local, two, work); work.CopyTo(local);
 138163            if (axis == 1)
 164            {
 50165                if (local.Signs[0] < 0) local.MultiplySign(-1);
 92166                ContactQuadratic.Scale(localD, Signed576.ExtendValue(Signed192.Raw(length)), work); local.Add(work, -1);
 85167                if (local.Signs[0] < 0) local.Clear();
 168            }
 276169            ContactQuadratic.Multiply(local, local, ReadOnlySpan<ulong>.Empty, term); squared.Add(term);
 170        }
 46171        ContactQuadratic.Scale(localD, Signed576.ExtendValue(Signed192.Raw(radius)), work);
 46172        ContactQuadratic.Scale(work, two, bound);
 92173        ContactQuadratic.Multiply(bound, bound, ReadOnlySpan<ulong>.Empty, term); squared.Add(term, -1);
 46174        return squared.Signs[0] <= 0;
 175    }
 176}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleEdges.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CapsuleEdges.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Finite capsule-core/prism-edge matching when a canonical support projection is empty.</content>
 13internal static partial class WideConvexPrismRelations
 14{
 15    private const int PrismEdgeWords = 32;
 16
 17    private static bool TryGetPrismEdgeContact(RigidAxis3 shapeAxis, Vector3d center,
 18        FixedQuaternion shapeRotation, Fixed64 length, Fixed64 radius,
 19        Vector3d prismOrigin, Fixed64 prismYaw, FixedQuaternion prismOrientation,
 20        ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness, out FixedContactAnchors contact,
 21        IPrismCapsuleContactDomain? domain = null)
 22    {
 19623        contact = default;
 19624        Signed192 twiceRotation = WideArithmetic.AddSigned192(shapeAxis.RotationDenominator, shapeAxis.RotationDenominat
 19625        Signed192 coordinateD = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(twiceRotation, Scale));
 19626        ReadOnlySpan<Signed192> axis = stackalloc Signed192[3] { shapeAxis.X, shapeAxis.Y, shapeAxis.Z };
 39227        Span<Signed192> core = stackalloc Signed192[3], u = stackalloc Signed192[3];
 156828        for (int component = 0; component < 3; component++)
 29        {
 58830            Signed320 midpoint = WideArithmetic.MultiplySigned192(WideArithmetic.Difference(center[component], prismOrig
 58831            Signed320 half = WideArithmetic.MultiplySigned192(axis[component], Signed192.Raw(length));
 58832            core[component] = Signed192.NarrowProven(WideArithmetic.SubtractSigned320(midpoint, half));
 58833            u[component] = Signed192.NarrowProven(WideArithmetic.AddSigned320(half, half));
 34        }
 35        // Ordinary full-domain endpoints/core use <165 bits, Gram terms <330
 36        // and determinant <656. Compact native clipping adds at most 320 bits
 37        // to one Gram term; retained point numerators still fit Signed832.
 38        // Filtered candidates retain their gap while the incident-fan checks borrow scratch.
 19639        int banks = domain == null ? 12 : 16;
 39240        Span<ulong> values = stackalloc ulong[banks * PrismEdgeWords]; Span<int> signs = stackalloc int[banks];
 19641        Span<Signed832> best = stackalloc Signed832[7];
 19642        var yaw = new WideConvex2dRelations.RotationFrame2d(prismYaw);
 19643        var selection = new PrismEdgeSelection(core, u, coordinateD, values, signs, best,
 19644            domain, offsets, halfThickness, yaw, twiceRotation, radius);
 39245        Span<Signed192> first = stackalloc Signed192[3], second = stackalloc Signed192[3];
 39246        Span<Signed192> v = stackalloc Signed192[3], w = stackalloc Signed192[3];
 19647        Span<Signed832> previousTarget = stackalloc Signed832[4];
 19648        Signed576 aa = PrismEdgeDot(u, u);
 19649        bool any = false;
 197250        for (int index = 0; index < offsets.Length; index++)
 51        {
 79052            int next = index + 1 == offsets.Length ? 0 : index + 1;
 632053            for (int kind = 0; kind < 3; kind++)
 54            {
 237055                GetPrismEdgeVertex(yaw, offsets[index], kind == 1 ? halfThickness : -halfThickness, twiceRotation, first
 237056                GetPrismEdgeVertex(yaw, kind == 2 ? offsets[index] : offsets[next],
 237057                    kind == 0 ? -halfThickness : halfThickness, twiceRotation, second);
 1896058                for (int component = 0; component < 3; component++)
 59                {
 711060                    v[component] = WideArithmetic.SubtractSigned192(second[component], first[component]);
 711061                    w[component] = WideArithmetic.SubtractSigned192(core[component], first[component]);
 62                }
 237063                selection.SetEdge(first, v, index, kind);
 237064                bool hasPreviousTarget = false;
 474065                Signed576 bb = PrismEdgeDot(v, v), ab = PrismEdgeDot(u, v);
 474066                Signed576 ao = PrismEdgeDot(u, w), bo = PrismEdgeDot(v, w);
 237067                ReadOnlySpan<RationalSegmentInterval> intervals = domain == null ? FullPrismEdgeInterval : domain.GetEdg
 928468                for (int interval = 0; interval < intervals.Length; interval++)
 69                {
 407070                    if (domain != null) selection.Reset();
 227271                    RationalSegmentInterval clip = intervals[interval];
 227272                    if (bb.IsZero)
 73                    {
 1274                        Signed576 d = aa.IsZero ? Signed576.ExtendValue(Signed192.Signed(1)) : aa;
 1275                        selection.Keep(Signed832.ExtendValue(ClampPrismEdgeParameter(WideArithmetic.Negate(ao), d)), def
 76                    }
 77                    else
 78                    {
 226079                        KeepPrismEdgeCandidates(ref selection, aa, bb, ab, ao, bo, clip, domain != null);
 80                    }
 227281                    if (domain != null && selection.TryGetContact(radius, center, shapeRotation, prismOrigin, prismOrien
 82                    {
 83                        // Closed adjacent height bands can share one unique
 84                        // closest point; it is one physical constraint.
 21785                        if (hasPreviousTarget && selection.SameTarget(previousTarget)) continue;
 21686                        var native = new CapsulePrismContactDepth(selection.BestQ, selection.BestD, radius);
 21687                        domain.AddEdgeContact(physical, native, selection.BestNormal, index, kind, interval);
 43288                        selection.CopyTarget(previousTarget); hasPreviousTarget = true;
 43289                        contact = physical; any = true;
 90                    }
 91                }
 92            }
 93        }
 35494        if (domain != null) return any;
 95        System.Diagnostics.Debug.Assert(selection.Found && selection.BestQ.Signs[0] > 0);
 3896        return selection.TryGetContact(radius, center, shapeRotation, prismOrigin, prismOrientation, out contact);
 97    }
 98
 199    private static readonly RationalSegmentInterval[] FullPrismEdgeInterval = { default };
 100
 101    private static void KeepPrismEdgeCandidates(ref PrismEdgeSelection selection,
 102        Signed576 aa, Signed576 bb, Signed576 ab, Signed576 ao, Signed576 bo,
 103        in RationalSegmentInterval clip, bool filtered)
 104    {
 9040105        Signed832 ln = default, ld = Signed832.ExtendValue(Signed192.Signed(1)), un = ld, ud = ld;
 2260106        if (clip.IsClipped)
 40107        { ln = clip.WideLowerNumerator; ld = clip.WideLowerDenominator; un = clip.WideUpperNumerator; ud = clip.WideUppe
 108        Signed832 tn, td;
 2260109        if (!clip.IsClipped)
 4500110        { tn = Signed832.ExtendValue(ClampPrismEdgeParameter(bo, bb)); td = Signed832.ExtendValue(bb); }
 10111        else GetClippedEdgeParameter(Signed832.ExtendValue(bo), Signed832.ExtendValue(bb), ln, ld, un, ud, out tn, out t
 2260112        selection.Keep(default, tn, td);
 2395113        if (aa.IsZero) return;
 2125114        if (!clip.IsClipped)
 4230115        { tn = Signed832.ExtendValue(ClampPrismEdgeParameter(WideArithmetic.AddSigned576(bo, ab), bb)); td = Signed832.E
 10116        else GetClippedEdgeParameter(Signed832.ExtendValue(WideArithmetic.AddSigned576(bo, ab)), Signed832.ExtendValue(b
 10117            ln, ld, un, ud, out tn, out td);
 2125118        selection.Keep(td, tn, td);
 2125119        if (!clip.IsClipped)
 120        {
 2115121            selection.Keep(Signed832.ExtendValue(ClampPrismEdgeParameter(WideArithmetic.Negate(ao), aa)), default, Signe
 2115122            selection.Keep(Signed832.ExtendValue(ClampPrismEdgeParameter(WideArithmetic.SubtractSigned576(ab, ao), aa)),
 2115123                Signed832.ExtendValue(aa), Signed832.ExtendValue(aa));
 124        }
 125        else
 126        {
 10127            KeepPrismEdgeEndpoint(ref selection, aa, ab, ao, clip.LowerNumerator, clip.LowerDenominator);
 10128            KeepPrismEdgeEndpoint(ref selection, aa, ab, ao, clip.UpperNumerator, clip.UpperDenominator);
 129        }
 2125130        Signed576 twiceBb = WideArithmetic.AddSigned576(bb, bb);
 2125131        if (!clip.IsClipped)
 2115132            selection.Keep(Signed832.ExtendValue(bb),
 2115133                Signed832.ExtendValue(ClampPrismEdgeParameter(WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(bo
 2115134                Signed832.ExtendValue(twiceBb));
 135        else
 136        {
 10137            GetClippedEdgeParameter(Signed832.ExtendValue(WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(bo, bo
 10138                Signed832.ExtendValue(twiceBb), ln, ld, un, ud, out tn, out td);
 10139            selection.Keep(td, WideArithmetic.AddSigned832(tn, tn), WideArithmetic.AddSigned832(td, td));
 140        }
 2125141        Signed832 determinant = WideArithmetic.SubtractSigned832(
 2125142            WideArithmetic.MultiplySigned576ToSigned832(aa, bb), WideArithmetic.MultiplySigned576ToSigned832(ab, ab));
 2125143        if (determinant.Sign > 0)
 144        {
 1496145            Signed832 s = WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned576ToSigned832(ab, bo),
 1496146                WideArithmetic.MultiplySigned576ToSigned832(bb, ao));
 1496147            Signed832 t = WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned576ToSigned832(aa, bo),
 1496148                WideArithmetic.MultiplySigned576ToSigned832(ab, ao));
 1496149            if (s.Sign < 0 || t.Sign < 0 || WideArithmetic.SubtractSigned832(determinant, s).Sign < 0
 1496150                || WideArithmetic.SubtractSigned832(determinant, t).Sign < 0
 1496151                || clip.IsClipped && (RationalSegmentInterval.CompareParameters(t, determinant, ln, ld) < 0
 2981152                    || RationalSegmentInterval.CompareParameters(t, determinant, un, ud) > 0)) return;
 11153            selection.Keep(s, t, determinant);
 154        }
 629155        else if (filtered
 629156            && WideTriangleRelations.ClipScaledHalfPlane(Signed832.ExtendValue(WideArithmetic.Negate(ao)),
 629157                Signed832.ExtendValue(WideArithmetic.SubtractSigned576(ab, ao)), ref ln, ref ld, ref un, ref ud)
 629158            && WideTriangleRelations.ClipScaledHalfPlane(Signed832.ExtendValue(WideArithmetic.AddSigned576(aa, ao)),
 629159                Signed832.ExtendValue(WideArithmetic.SubtractSigned576(WideArithmetic.AddSigned576(aa, ao), ab)),
 629160                ref ln, ref ld, ref un, ref ud))
 161        {
 162            // An endpoint fan can reject the old midpoint although an open
 163            // parallel closest family remains exposed. Retain its true interior.
 207164            GetPrismInteriorParameter(new RationalSegmentInterval(ln, ld, un, ud), out tn, out td);
 207165            KeepPrismEdgeEndpoint(ref selection, aa, ab, ao, NarrowPrismEdgeParameter(tn), NarrowPrismEdgeParameter(td))
 166        }
 629167    }
 168
 169    private static Signed320 NarrowPrismEdgeParameter(Signed832 value) =>
 414170        new(value.Word4, value.Word3, value.Word2, value.Word1, value.Word0);
 171
 172    private static void GetClippedEdgeParameter(Signed832 n, Signed832 d,
 173        Signed832 ln, Signed832 ld, Signed832 un, Signed832 ud, out Signed832 resultN, out Signed832 resultD)
 174    {
 60175        resultN = n; resultD = d;
 30176        if (n.Sign < 0 || RationalSegmentInterval.CompareParameters(n, d, ln, ld) < 0)
 20177        { resultN = ln; resultD = ld; }
 20178        else if (RationalSegmentInterval.CompareParameters(n, d, un, ud) > 0)
 20179        { resultN = un; resultD = ud; }
 20180    }
 181
 182    private static void KeepPrismEdgeEndpoint(ref PrismEdgeSelection selection, Signed576 aa, Signed576 ab, Signed576 ao
 183        Signed320 tn, Signed320 td)
 184    {
 227185        Signed832 d = WideArithmetic.MultiplySigned576ToSigned832(aa, td);
 227186        Signed832 s = WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned576ToSigned832(ab, tn),
 227187            WideArithmetic.MultiplySigned576ToSigned832(ao, td));
 229188        if (s.Sign < 0) s = default;
 231189        else if (WideArithmetic.SubtractSigned832(s, d).Sign > 0) s = d;
 227190        selection.Keep(s, WideArithmetic.MultiplySigned576ToSigned832(aa, tn), d);
 227191    }
 192
 193    private ref struct PrismEdgeSelection
 194    {
 195        private readonly ReadOnlySpan<Signed192> _core, _u;
 196        private readonly Signed192 _coordinateD, _twiceRotation, _radiusSquared;
 197        private readonly ContactQuadratic _currentQ, _currentD, _term, _product;
 198        private readonly ContactQuadratic _gapQ, _gapD;
 199        internal readonly ContactQuadratic BestQ, BestD;
 200        private readonly Span<Signed832> _best;
 201        private readonly IPrismCapsuleContactDomain? _domain;
 202        private readonly ReadOnlySpan<Vector2d> _offsets;
 203        private readonly Fixed64 _half;
 204        private readonly WideConvex2dRelations.RotationFrame2d _yaw;
 205        private ReadOnlySpan<Signed192> _edge, _v;
 206        private int _vertex, _kind;
 207        private Signed832 _middleN, _middleD;
 208        internal bool Found;
 209        internal WideDirection3d BestNormal;
 210
 211        internal PrismEdgeSelection(ReadOnlySpan<Signed192> core, ReadOnlySpan<Signed192> u, Signed192 coordinateD,
 212            Span<ulong> values, Span<int> signs, Span<Signed832> best, IPrismCapsuleContactDomain? domain,
 213            ReadOnlySpan<Vector2d> offsets, Fixed64 half, WideConvex2dRelations.RotationFrame2d yaw, Signed192 twiceRota
 214        {
 1176215            this = default; _core = core; _u = u; _coordinateD = coordinateD; _best = best; _domain = domain;
 784216            _offsets = offsets; _half = half; _yaw = yaw; _twiceRotation = twiceRotation;
 217            // A nonnegative scalar radius has at most 63 magnitude bits; its exact
 218            // raw square fits Signed192 and is shared by all retained candidates.
 196219            _radiusSquared = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(Signed192.Raw(radius), Signed192.Ra
 392220            BestQ = ContactQuadratic.At(values, signs, 0, PrismEdgeWords); BestD = ContactQuadratic.At(values, signs, 1,
 392221            _currentQ = ContactQuadratic.At(values, signs, 2, PrismEdgeWords); _currentD = ContactQuadratic.At(values, s
 392222            _term = ContactQuadratic.At(values, signs, 4, PrismEdgeWords); _product = ContactQuadratic.At(values, signs,
 196223            _gapQ = domain == null ? _currentQ : ContactQuadratic.At(values, signs, 6, PrismEdgeWords);
 196224            _gapD = domain == null ? _currentD : ContactQuadratic.At(values, signs, 7, PrismEdgeWords);
 196225        }
 226
 227        internal void SetEdge(ReadOnlySpan<Signed192> edge, ReadOnlySpan<Signed192> v, int vertex, int kind)
 11850228        { _edge = edge; _v = v; _vertex = vertex; _kind = kind; }
 229
 5394230        internal void Reset() { Found = false; _middleN = _middleD = default; }
 231
 232        internal void Keep(Signed832 s, Signed832 t, Signed832 d)
 233        {
 234            // Each prism cap vertex belongs to its original vertical edge.
 235            // Horizontal edge endpoints must not emit duplicate corner rows.
 16794236            if (_domain != null && _kind != 2 && (t.IsZero || WideArithmetic.SubtractSigned832(t, d).IsZero)) return;
 5186237            Span<Signed832> points = stackalloc Signed832[7];
 41488238            for (int component = 0; component < 3; component++)
 239            {
 15558240                points[component] = WideArithmetic.AddSigned832(WideArithmetic.MultiplySigned832(d, _core[component]),
 15558241                    WideArithmetic.MultiplySigned832(s, _u[component]));
 15558242                points[component + 3] = WideArithmetic.AddSigned832(WideArithmetic.MultiplySigned832(d, _edge[component]
 15558243                    WideArithmetic.MultiplySigned832(t, _v[component]));
 244            }
 5186245            points[6] = WideArithmetic.MultiplySigned832(d, _coordinateD);
 5186246            _gapQ.Clear();
 41488247            for (int component = 0; component < 3; component++)
 248            {
 15558249                _term.Set(WideArithmetic.SubtractSigned832(points[component + 3], points[component]));
 31116250                ContactQuadratic.Multiply(_term, _term, ReadOnlySpan<ulong>.Empty, _product); _gapQ.Add(_product);
 251            }
 10372252            _term.Set(points[6]); ContactQuadratic.Multiply(_term, _term, ReadOnlySpan<ulong>.Empty, _gapD);
 253            // Most unrelated retained edges are outside the capsule radius.
 254            // Reject those before GCD reduction, whole-solid support and fan tests.
 5186255            WideDirection3d normal = default;
 5186256            if (_domain != null && (!WithinRadius(_gapQ, _gapD) || !TryGetPrismEdgeDirection(points, out normal)
 8089257                || !Supports(points, d, normal) || !_domain.AdmitEdgePoint(_vertex, _kind, t, d, normal))) return;
 2283258            int comparison = Found ? ContactQuadratic.CompareRatios(_gapQ, _gapD, ReadOnlySpan<ulong>.Empty,
 2283259                BestQ, BestD, ReadOnlySpan<ulong>.Empty) : -1;
 2283260            Signed832 middle = WideArithmetic.SubtractSigned832(WideArithmetic.AddSigned832(s, s), d);
 3267261            if (middle.Sign < 0) middle = WideArithmetic.SubtractSigned832(default, middle);
 4199262            if (comparison > 0 || comparison == 0 && WideArithmetic.CompareNonNegativeProducts(middle, _middleD, _middle
 1101263            _gapQ.CopyTo(BestQ); _gapD.CopyTo(BestD); points.CopyTo(_best);
 1468264            _middleN = middle; _middleD = d; Found = true; BestNormal = normal;
 367265        }
 266
 267        internal bool SameTarget(scoped ReadOnlySpan<Signed832> other)
 268        {
 4269            _currentD.Set(_best[6]); _product.Set(other[3]);
 12270            for (int axis = 0; axis < 3; axis++)
 271            {
 10272                _currentQ.Set(_best[axis + 3]); _term.Set(other[axis]);
 5273                if (ContactQuadratic.CompareRatios(_currentQ, _currentD, ReadOnlySpan<ulong>.Empty,
 6274                    _term, _product, ReadOnlySpan<ulong>.Empty) != 0) return false;
 275            }
 1276            return true;
 277        }
 278
 279        internal void CopyTarget(scoped Span<Signed832> target)
 280        {
 432281            _best.Slice(3, 3).CopyTo(target); target[3] = _best[6];
 216282        }
 283
 284        internal bool TryGetContact(Fixed64 radius, Vector3d center, FixedQuaternion rotation,
 285            Vector3d origin, FixedQuaternion prismRotation, out FixedContactAnchors contact)
 286        {
 1836287            contact = default;
 3417288            if (!Found) return false;
 273289            if (!WithinRadius(BestQ, BestD)) return false;
 237290            contact = MaterializePrismEdgeContact(_best, BestQ.Rational, center, rotation, origin, prismRotation, radius
 237291            return true;
 292        }
 293
 294        private bool WithinRadius(scoped ContactQuadratic squared, scoped ContactQuadratic denominator)
 295        {
 3657296            _term.Set(Signed576.ExtendValue(_radiusSquared));
 3657297            ContactQuadratic.Multiply(_term, denominator, ReadOnlySpan<ulong>.Empty, _product);
 3657298            _product.Add(squared, -1);
 3657299            return _product.Signs[0] >= 0;
 300        }
 301
 302        private bool Supports(scoped ReadOnlySpan<Signed832> points, Signed832 d, in WideDirection3d normal)
 303        {
 1713304            Span<Signed192> vertex = stackalloc Signed192[3];
 1915305            if (!SupportsVertex(_core, points[..3], d, normal, true)) return false;
 16621306            for (int axis = 0; axis < 3; axis++) vertex[axis] = WideArithmetic.AddSigned192(_core[axis], _u[axis]);
 1713307            if (!SupportsVertex(vertex, points[..3], d, normal, true)) return false;
 8112308            foreach (Vector2d offset in _offsets)
 15874309                for (int sign = -1; sign <= 1; sign += 2)
 310                {
 5593311                    GetPrismEdgeVertex(_yaw, offset, sign < 0 ? -_half : _half, _twiceRotation, vertex);
 6399312                    if (!SupportsVertex(vertex, points[3..6], d, normal, false)) return false;
 313                }
 503314            return true;
 315        }
 316
 317        private bool SupportsVertex(scoped ReadOnlySpan<Signed192> vertex, scoped ReadOnlySpan<Signed832> selected,
 318            Signed832 d, in WideDirection3d normal, bool maximum)
 319        {
 8817320            _currentQ.Clear();
 8817321            Signed832 projection = default;
 70536322            for (int axis = 0; axis < 3; axis++)
 323            {
 26451324                Signed576 component = axis == 0 ? normal.X : axis == 1 ? normal.Y : normal.Z;
 26451325                projection = WideArithmetic.AddSigned832(projection, WideArithmetic.MultiplySigned576ToSigned832(compone
 79353326                _term.Set(selected[axis]); ContactQuadratic.Scale(_term, component, _product); _currentQ.Add(_product);
 327            }
 26451328            _term.Set(d); ContactQuadratic.Scale(_term, projection, _currentD); _currentD.Add(_currentQ, -1);
 8817329            return maximum ? _currentD.Signs[0] <= 0 : _currentD.Signs[0] >= 0;
 330        }
 331    }
 332
 333    private static bool TryGetPrismEdgeDirection(ReadOnlySpan<Signed832> points, out WideDirection3d direction)
 334    {
 1714335        direction = default;
 1714336        Span<Signed832> delta = stackalloc Signed832[3];
 5142337        Span<ulong> content = stackalloc ulong[13], magnitude = stackalloc ulong[13], quotient = stackalloc ulong[13],
 3428338            remainder = stackalloc ulong[13], scratch = stackalloc ulong[27];
 1714339        content.Clear();
 13712340        for (int axis = 0; axis < 3; axis++)
 341        {
 5142342            delta[axis] = WideArithmetic.SubtractSigned832(points[axis + 3], points[axis]);
 5142343            WideArithmetic.GetMagnitude(delta[axis], magnitude);
 7070344            if (WideArithmetic.GetActiveMagnitudeLength(content) == 0) magnitude.CopyTo(content);
 3214345            else WideArithmetic.GetMagnitudeGreatestCommonDivisor(content, magnitude, scratch);
 346        }
 1715347        if (WideArithmetic.GetActiveMagnitudeLength(content) == 0) return false;
 1713348        Span<Signed576> normal = stackalloc Signed576[3];
 13704349        for (int axis = 0; axis < 3; axis++)
 350        {
 5139351            WideArithmetic.GetMagnitude(delta[axis], magnitude);
 5139352            WideArithmetic.DivideMagnitudes(magnitude, content, quotient, remainder, scratch);
 353            // For bounded rigid core/edge pairs, a primitive residual is either
 354            // the line cross (<300 bits) or endpoint projection (<450 bits).
 5139355            var value = new Signed576(quotient[8], quotient[7], quotient[6], quotient[5], quotient[4], quotient[3], quot
 5139356            normal[axis] = delta[axis].Sign < 0 ? WideArithmetic.Negate(value) : value;
 357        }
 3426358        direction = new WideDirection3d(normal[0], normal[1], normal[2]); return true;
 359    }
 360
 361    private static void GetPrismEdgeVertex(WideConvex2dRelations.RotationFrame2d yaw, Vector2d offset,
 362        Fixed64 y, Signed192 twiceRotation, Span<Signed192> point)
 363    {
 10647364        WideConvex2dRelations.GetRotatedOffset(yaw, offset, out Signed192 x, out Signed192 z);
 10647365        point[0] = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(x, twiceRotation));
 10647366        point[1] = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 10647367            Signed192.NarrowProven(WideArithmetic.MultiplySigned192(Signed192.Raw(y), Scale)), twiceRotation));
 10647368        point[2] = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(z, twiceRotation));
 10647369    }
 370
 371    private static Signed576 PrismEdgeDot(ReadOnlySpan<Signed192> first, ReadOnlySpan<Signed192> second) =>
 9978372        WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(
 9978373            WideArithmetic.MultiplySigned320(Signed320.ExtendValue(first[0]), Signed320.ExtendValue(second[0])),
 9978374            WideArithmetic.MultiplySigned320(Signed320.ExtendValue(first[1]), Signed320.ExtendValue(second[1]))),
 9978375            WideArithmetic.MultiplySigned320(Signed320.ExtendValue(first[2]), Signed320.ExtendValue(second[2])));
 376
 377    private static Signed576 ClampPrismEdgeParameter(Signed576 value, Signed576 denominator) =>
 10722378        value.Sign < 0 ? default : WideArithmetic.SubtractSigned576(value, denominator).Sign > 0 ? denominator : value;
 379
 380    private static FixedContactAnchors MaterializePrismEdgeContact(ReadOnlySpan<Signed832> points,
 381        ReadOnlySpan<ulong> root, Vector3d center, FixedQuaternion rotation,
 382        Vector3d prismOrigin, FixedQuaternion prismRotation, Fixed64 radius)
 383    {
 474384        Span<ulong> normals = stackalloc ulong[7 * PrismEdgeWords]; Span<int> normalSigns = stackalloc int[7];
 474385        normals.Clear(); normalSigns.Clear();
 1896386        for (int axis = 0; axis < 3; axis++)
 387        {
 711388            Signed832 delta = WideArithmetic.SubtractSigned832(points[axis + 3], points[axis]);
 1422389            WideArithmetic.GetMagnitude(delta, normals.Slice(axis * PrismEdgeWords, PrismEdgeWords)); normalSigns[axis] 
 390        }
 237391        Vector3d normal = GetConvexContactCandidateNormal(new ConvexContactCandidate(normals, normalSigns, 1, PrismEdgeW
 474392        Span<ulong> values = stackalloc ulong[6 * PrismEdgeWords]; Span<int> signs = stackalloc int[6];
 237393        ContactQuadratic denominator = ContactQuadratic.At(values, signs, 0, PrismEdgeWords);
 237394        ContactQuadratic numerator = ContactQuadratic.At(values, signs, 1, PrismEdgeWords);
 237395        ContactQuadratic term = ContactQuadratic.At(values, signs, 2, PrismEdgeWords);
 474396        denominator.Set(points[6]); numerator.Clear();
 237397        term.Set(Signed576.ExtendValue(Signed192.Raw(radius)));
 237398        ContactQuadratic.Multiply(denominator, term, ReadOnlySpan<ulong>.Empty, numerator);
 474399        numerator.Radical[0] = 1; numerator.Signs[1] = -1;
 237400        Fixed64 depth = ContactQuadratic.RoundRatio(numerator, denominator, root, 0, long.MaxValue);
 237401        FixedPointAnchor shape = MaterializePrismEdgeAnchor(points, root, center, rotation, prismOrigin, radius, true);
 237402        FixedPointAnchor prism = MaterializePrismEdgeAnchor(points, root, prismOrigin, prismRotation, prismOrigin, radiu
 403        // The core is outside the prism, so 0<=radius-distance<=radius fits.
 237404        return new FixedContactAnchors(shape, prism, normal, depth, false);
 405    }
 406
 407    private static FixedPointAnchor MaterializePrismEdgeAnchor(ReadOnlySpan<Signed832> points,
 408        ReadOnlySpan<ulong> root, Vector3d origin, FixedQuaternion rotation, Vector3d prismOrigin, Fixed64 radius, bool 
 409    {
 948410        Span<ulong> values = stackalloc ulong[18 * PrismEdgeWords]; Span<int> signs = stackalloc int[18];
 474411        ContactQuadratic denominator = ContactQuadratic.At(values, signs, 3, PrismEdgeWords);
 474412        ContactQuadratic localD = ContactQuadratic.At(values, signs, 4, PrismEdgeWords);
 474413        ContactQuadratic first = ContactQuadratic.At(values, signs, 5, PrismEdgeWords);
 474414        ContactQuadratic second = ContactQuadratic.At(values, signs, 6, PrismEdgeWords);
 474415        ContactQuadratic numerator = ContactQuadratic.At(values, signs, 7, PrismEdgeWords);
 474416        ContactQuadratic term = ContactQuadratic.At(values, signs, 8, PrismEdgeWords);
 474417        denominator.Set(points[6]);
 3792418        for (int axis = 0; axis < 3; axis++)
 419        {
 1422420            ContactQuadratic coordinate = ContactQuadratic.At(values, signs, axis, PrismEdgeWords);
 2844421            if (!capsule) { coordinate.Set(points[axis + 3]); continue; }
 711422            Signed832 relative = WideArithmetic.SubtractSigned832(points[axis],
 711423                WideArithmetic.MultiplySigned832(points[6], WideArithmetic.Difference(origin[axis], prismOrigin[axis])))
 711424            first.Set(WideArithmetic.SubtractSigned832(points[axis + 3], points[axis]));
 711425            ContactQuadratic.Scale(first, Signed576.ExtendValue(Signed192.Raw(radius)), second);
 711426            ContactQuadratic.Multiply(second, denominator, ReadOnlySpan<ulong>.Empty, coordinate);
 1422427            WideArithmetic.GetMagnitude(relative, coordinate.Radical); coordinate.Signs[1] = relative.Sign;
 428        }
 474429        if (capsule)
 430        {
 711431            denominator.Clear(); WideArithmetic.GetMagnitude(points[6], denominator.Radical); denominator.Signs[1] = 1;
 432        }
 474433        WideRationalBasis3d basis = new(rotation);
 474434        ContactQuadratic.Scale(denominator, Signed576.ExtendValue(basis.Denominator), localD);
 474435        ReadOnlySpan<Signed192> coefficients = stackalloc Signed192[9]
 474436            { basis.Xx, basis.Xy, basis.Xz, basis.Yx, basis.Yy, basis.Yz, basis.Zx, basis.Zy, basis.Zz };
 948437        Vector3d point = default, displacement = default;
 3792438        for (int axis = 0; axis < 3; axis++)
 439        {
 1422440            numerator.Clear();
 11376441            for (int component = 0; component < 3; component++)
 442            {
 4266443                ContactQuadratic.Scale(ContactQuadratic.At(values, signs, component, PrismEdgeWords),
 8532444                    Signed576.ExtendValue(coefficients[3 * axis + component]), term); numerator.Add(term);
 445            }
 1422446            ContactQuadratic.SplitRound(numerator, localD, root, out Fixed64 a, out Fixed64 b);
 2844447            point[axis] = a; displacement[axis] = b;
 448        }
 474449        return new FixedPointAnchor(origin, rotation, point, displacement);
 450    }
 451}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleSlab.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CapsuleSlab.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 System.Runtime.CompilerServices;
 10using static FixedMathSharp.Geometry.CylinderContactAlgebra;
 11
 12namespace FixedMathSharp.Geometry;
 13
 14/// <content>Complete signed support minima for a capsule and a flat-capped stadium slab.</content>
 15internal static partial class WideConvexPrismRelations
 16{
 17    private readonly struct CapsuleSlabGeometry
 18    {
 19        internal readonly WideRationalBasis3d WorldBasis;
 20        internal readonly WideAxis3 Center, CapsuleHalf, CapsuleAxis;
 21        internal readonly Signed320 HalfHeight, HalfCore, Radius;
 22        internal readonly Signed192 RawScale, AxisDenominator;
 23        internal readonly int ValueShift;
 24
 25        internal CapsuleSlabGeometry(Vector3d center, Fixed64 rotation, Fixed64 core, Fixed64 radius,
 26            Fixed64 halfHeight, Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Fixed64 capsuleCore)
 27        {
 9428            FixedQuaternion slabRotation = FixedQuaternion.FromAxisAngle(Vector3d.Up, -rotation);
 9429            WorldBasis = new WideRationalBasis3d(slabRotation);
 9430            var frame = new CylinderPolytopeFrame(center, slabRotation,
 9431                WideArithmetic.AddSigned192(Signed192.Raw(halfHeight), Signed192.Raw(halfHeight)),
 9432                radius, capsuleCenter, capsuleRotation);
 9433            AxisDenominator = frame.Denominator;
 9434            CapsuleAxis = new WideAxis3(Signed320.ExtendValue(frame.Basis.Yx),
 9435                Signed320.ExtendValue(frame.Basis.Yy), Signed320.ExtendValue(frame.Basis.Yz));
 9436            WideAxis3 offset = frame.Transform(Vector3d.Zero);
 9437            Span<Signed320> coordinates = stackalloc Signed320[10]
 9438            {
 9439                Signed320.ExtendValue(WideArithmetic.AddSigned192(frame.Denominator, frame.Denominator)),
 9440                offset.X, offset.Y, offset.Z,
 9441                WideArithmetic.MultiplySigned192(frame.Basis.Yx, Signed192.Raw(capsuleCore)),
 9442                WideArithmetic.MultiplySigned192(frame.Basis.Yy, Signed192.Raw(capsuleCore)),
 9443                WideArithmetic.MultiplySigned192(frame.Basis.Yz, Signed192.Raw(capsuleCore)),
 9444                frame.Cap, WideArithmetic.MultiplySigned192(frame.Denominator, Signed192.Raw(core)),
 9445                WideArithmetic.MultiplySigned192(frame.Denominator, frame.Radius)
 9446            };
 9447            WideArithmetic.ReduceCommonScale(coordinates);
 9448            RawScale = Signed192.NarrowProven(coordinates[0]);
 9449            Center = new WideAxis3(coordinates[1], coordinates[2], coordinates[3]);
 9450            CapsuleHalf = new WideAxis3(coordinates[4], coordinates[5], coordinates[6]);
 28251            HalfHeight = coordinates[7]; HalfCore = coordinates[8]; Radius = coordinates[9];
 9452            int bits = 0;
 9453            Span<ulong> magnitude = stackalloc ulong[5];
 188054            for (int index = 1; index < coordinates.Length; index++)
 55            {
 84656                WideArithmetic.GetMagnitude(coordinates[index], out magnitude[4], out magnitude[3],
 84657                    out magnitude[2], out magnitude[1], out magnitude[0]);
 84658                bits = Math.Max(bits, WideArithmetic.GetMagnitudeBitLength(magnitude));
 59            }
 60            // Extents/offsets <2^200; the margin covers both endpoint additions
 61            // and the complete support value. All end regions use this scale.
 9462            ValueShift = 2 * (bits + 5);
 9463        }
 64
 180165        internal WideAxis3 CapOffset(int cap, int end) => new(WideArithmetic.Negate(Center.X),
 180166            WideArithmetic.SubtractSigned320(cap > 0 ? HalfHeight : WideArithmetic.Negate(HalfHeight), Center.Y),
 180167            WideArithmetic.SubtractSigned320(end > 0 ? HalfCore : WideArithmetic.Negate(HalfCore), Center.Z));
 68
 69        internal WideAxis3 EndpointOffset(int cap, int end, int capsule)
 70        {
 108171            WideAxis3 c = CapOffset(cap, end);
 108172            return new WideAxis3(
 108173                GetCylinderCapsuleEndpointCoordinate(WideArithmetic.Negate(c.X), default, 0, CapsuleHalf.X, capsule),
 108174                GetCylinderCapsuleEndpointCoordinate(WideArithmetic.Negate(c.Y), default, 0, CapsuleHalf.Y, capsule),
 108175                GetCylinderCapsuleEndpointCoordinate(WideArithmetic.Negate(c.Z), default, 0, CapsuleHalf.Z, capsule));
 76        }
 77    }
 78
 79    // Borrowed buffers keep enumeration in one place without allocating or
 80    // retaining a candidate per feature. Exact ties keep the earlier feature.
 81    private ref struct CapsuleSlabCandidates
 82    {
 83        internal CapsuleSlabGeometry Geometry;
 84        internal Span<ulong> Values, BestValues, Direction;
 85        internal Span<int> Signs, BestSigns, DirectionSigns;
 86        internal int GapSign;
 87        internal bool HasBest;
 88
 89        internal void Keep(WideAxis3 direction)
 90        {
 163291            WriteDirection(direction, Direction, DirectionSigns);
 163292            KeepDirection();
 163293        }
 94
 95        internal void KeepDirection()
 96        {
 191297            if (IsZero(Direction)) return;
 189698            int gapSign = BuildCapsuleSlabDirection(Geometry, Direction, DirectionSigns, Values, Signs);
 189699            KeepCylinderCapsuleCandidate(Values, Signs, gapSign,
 1896100                BestValues, BestSigns, ref GapSign, ref HasBest);
 1896101        }
 102
 99103        internal ConvexContactCandidate Best => new(BestValues, BestSigns, GapSign);
 104    }
 105
 106    /// <summary>Returns complete slab-to-capsule penetration from authoritative authored frames.</summary>
 107    internal static bool TryGetCenteredCapsuleSlabCapsulePenetration(
 108        Vector3d slabCenter, Fixed64 slabRotation, Fixed64 slabCoreLength, Fixed64 slabRadius, Fixed64 slabHalfThickness
 109        Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Fixed64 capsuleCoreLength, Fixed64 capsuleRadius,
 110        out Vector3d normal, out Fixed64 depth, out bool depthIsClamped)
 111    {
 96112        if (slabCoreLength == Fixed64.Zero)
 2113            return TryGetCenteredFiniteCylinderCapsulePenetration(slabCenter, FixedQuaternion.Identity, Vector3d.Up,
 2114                WideArithmetic.AddSigned192(Signed192.Raw(slabHalfThickness), Signed192.Raw(slabHalfThickness)), slabRad
 2115                capsuleCenter, capsuleRotation, Vector3d.Up, capsuleCoreLength, capsuleRadius,
 2116                out normal, out depth, out depthIsClamped);
 117
 94118        var geometry = new CapsuleSlabGeometry(slabCenter, slabRotation, slabCoreLength, slabRadius,
 94119            slabHalfThickness, capsuleCenter, capsuleRotation, capsuleCoreLength);
 94120        Span<ulong> storage = stackalloc ulong[(2 * ConvexContactCandidate.Slots + 3) * Words];
 94121        Span<int> signs = stackalloc int[2 * ConvexContactCandidate.Slots + 3];
 94122        var candidates = new CapsuleSlabCandidates
 94123        {
 94124            Geometry = geometry, Values = storage[..(11 * Words)], BestValues = storage.Slice(11 * Words, 11 * Words),
 94125            Direction = storage[(22 * Words)..], Signs = signs[..11], BestSigns = signs.Slice(11, 11),
 94126            DirectionSigns = signs[22..]
 94127        };
 94128        WideAxis3 x = new(Signed320.ExtendValue(Signed192.Signed(1)), default, default);
 188129        WideAxis3 y = new(default, x.X, default), z = new(default, default, x.X);
 130        // Poles and plane intersections own ties before interior stationary roots.
 94131        candidates.Keep(y);
 94132        if (geometry.CapsuleHalf.X.IsZero && geometry.CapsuleHalf.Z.IsZero)
 133        {
 134            // Parallel cores form a Cartesian product: a planar stadium and
 135            // one vertical interval. If either factor contains the center,
 136            // min support is the smaller of the planar and vertical gaps.
 137            // Both-outside corners still require the complete rim enumeration.
 28138            int capGapSign = candidates.GapSign;
 28139            Signed320 radialZ = GetCylinderCapsuleEndpointCoordinate(geometry.Center.Z,
 28140                geometry.HalfCore, geometry.Center.Z.Sign, default, 0);
 30141            if (radialZ.Sign != geometry.Center.Z.Sign) radialZ = default;
 28142            WideAxis3 radial = new(geometry.Center.X, default, radialZ);
 28143            candidates.Keep(radial.IsZero ? x : radial);
 28144            if (capGapSign >= 0 || WideArithmetic.SubtractSigned576(
 28145                    WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius), radial.SquaredLength).Sign >= 0)
 21146                return MaterializeCylinderCapsulePenetration(candidates.Best, capsuleRadius,
 21147                    out normal, out depth, out depthIsClamped);
 148        }
 146149        candidates.Keep(x); candidates.Keep(z);
 73150        WideAxis3 e = geometry.CapsuleAxis;
 146151        candidates.Keep(WideAxis3.Cross(y, e)); candidates.Keep(WideAxis3.Cross(z, e));
 152        // The straight-side normal plane has n.Z=0, so the slab endpoint
 153        // does not affect its fan. Enumerate it once, not once per end.
 428154        for (int cap = -1; cap <= 1; cap += 2)
 864155            for (int capsule = -1; capsule <= 1; capsule += 2)
 156            {
 291157                WideAxis3 q = geometry.EndpointOffset(cap, 1, capsule);
 1736158                for (int radial = -1; radial <= 1; radial += 2)
 159                {
 582160                    WideAxis3 direction = new(WideArithmetic.SubtractSigned320(q.X,
 582161                        radial > 0 ? geometry.Radius : WideArithmetic.Negate(geometry.Radius)), q.Y, default);
 582162                    candidates.Keep(direction);
 582163                    if (HasCapsuleSlabStraightRimCertificate(geometry, direction,
 582164                            WideArithmetic.AddSigned320(q.Z, geometry.HalfCore), cap, radial, capsule))
 5165                        return MaterializeCylinderCapsulePenetration(candidates.Best, capsuleRadius,
 5166                            out normal, out depth, out depthIsClamped);
 167                }
 168            }
 392169        for (int end = -1; end <= 1; end += 2)
 784170            for (int capsule = -1; capsule <= 1; capsule += 2)
 171            {
 264172                WideAxis3 q = geometry.EndpointOffset(1, end, capsule);
 264173                candidates.Keep(new WideAxis3(q.X, default, q.Z));
 1568174                for (int cap = -1; cap <= 1; cap += 2)
 175                {
 526176                    q = geometry.EndpointOffset(cap, end, capsule);
 526177                    if (BuildCapsuleSlabEndpointRim(geometry, q, cap, end, capsule, candidates.Values, candidates.Signs)
 6178                        return MaterializeCylinderCapsulePenetration(new ConvexContactCandidate(candidates.Values, candi
 6179                            capsuleRadius, out normal, out depth, out depthIsClamped);
 180                }
 181            }
 182
 62183        if (!geometry.CapsuleHalf.IsZero && !(e.X.IsZero && e.Z.IsZero))
 184        {
 62185            WideAxis3 principal = WideAxis3.Cross(e, y);
 124186            candidates.Keep(principal); candidates.Keep(WideAxis3.Cross(e, principal));
 62187            CircularRimContactAlgebra.GetBasis(e, out WideAxis3 first, out WideAxis3 second);
 124188            candidates.Keep(first); candidates.Keep(second);
 62189            candidates.Keep(new WideAxis3(WideArithmetic.AddSigned320(first.X, second.X),
 62190                WideArithmetic.AddSigned320(first.Y, second.Y), WideArithmetic.AddSigned320(first.Z, second.Z)));
 62191            candidates.Keep(new WideAxis3(WideArithmetic.SubtractSigned320(first.X, second.X),
 62192                WideArithmetic.SubtractSigned320(first.Y, second.Y), WideArithmetic.SubtractSigned320(first.Z, second.Z)
 372193            for (int cap = -1; cap <= 1; cap += 2)
 744194                for (int end = -1; end <= 1; end += 2)
 195                {
 248196                    WideAxis3 c = geometry.CapOffset(cap, end);
 248197                    ProjectPerpendicular(e, c, candidates.Direction, candidates.DirectionSigns);
 248198                    candidates.KeepDirection();
 248199                    if (e.Y.IsZero && !geometry.Radius.IsZero)
 12200                        KeepCapsuleSlabHorizontalCorners(ref candidates, c, principal);
 201                }
 62202            if (!e.Y.IsZero && !geometry.Radius.IsZero
 62203                && TryImproveCapsuleSlabRim(geometry, candidates.Best, capsuleRadius,
 62204                    out bool intersects, out normal, out depth, out depthIsClamped))
 47205                return intersects;
 206        }
 15207        return MaterializeCylinderCapsulePenetration(candidates.Best, capsuleRadius, out normal, out depth, out depthIsC
 208    }
 209
 210    [MethodImpl(MethodImplOptions.NoInlining)]
 211    private static int BuildCapsuleSlabDirection(in CapsuleSlabGeometry geometry,
 212        ReadOnlySpan<ulong> direction, ReadOnlySpan<int> directionSigns, Span<ulong> values, Span<int> signs)
 213    {
 3792214        values.Clear(); signs.Clear();
 1896215        Span<ulong> work = stackalloc ulong[7 * Words];
 7584216        Span<ulong> p = Slot(work, 0), q = Slot(work, 1), metric = Slot(work, 2), scale = Slot(work, 3),
 5688217            factor = Slot(work, 4), product = Slot(work, 5), denominator = Slot(work, 6);
 1896218        CylinderContactAlgebra.Dot(geometry.Center, direction, directionSigns, p, out int centerSign);
 1896219        int pSign = IsZero(p) ? 0 : -1;
 1896220        Import(geometry.HalfHeight, factor);
 3792221        WideArithmetic.MultiplyMagnitudes(factor, Slot(direction, 1), product); Add(product, 1, p, ref pSign);
 1896222        Import(geometry.HalfCore, factor);
 3792223        WideArithmetic.MultiplyMagnitudes(factor, Slot(direction, 2), product); Add(product, 1, p, ref pSign);
 3792224        CylinderContactAlgebra.Dot(geometry.CapsuleHalf, direction, directionSigns, product, out _); Add(product, 1, p, 
 1896225        WideArithmetic.MultiplyMagnitudes(Slot(direction, 0), Slot(direction, 0), metric);
 1896226        WideArithmetic.MultiplyMagnitudes(Slot(direction, 2), Slot(direction, 2), product);
 1896227        WideArithmetic.AddMagnitudeInto(product, metric);
 1896228        Import(WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius), factor);
 1896229        WideArithmetic.MultiplyMagnitudes(factor, metric, q);
 1896230        SumSquares(direction, metric);
 1896231        Import(WideArithmetic.MultiplySigned192(geometry.RawScale, geometry.RawScale), scale);
 1896232        WideArithmetic.MultiplyMagnitudes(scale, metric, denominator);
 233        // The core Minkowski sum is centrally symmetric. Of ±n, the one
 234        // facing the offset always has the smaller whole-shape support gap.
 1896235        WriteWorldDirection(geometry.WorldBasis, direction, directionSigns, values, signs, centerSign < 0 ? -1 : 1);
 1896236        return BuildRadialCandidate(p, pSign, q, denominator, values, signs);
 237    }
 238
 239    private static bool HasCapsuleSlabStraightRimCertificate(in CapsuleSlabGeometry geometry,
 240        WideAxis3 residual, Signed320 planarZ, int cap, int radial, int capsule)
 241    {
 242        // w=(±R,±H,planarZ)+±B is feasible and supports the entire core
 243        // along residual=D-w. These exact support signs make w its closest
 244        // point, including the end seam and a tied capsule endpoint.
 582245        return residual.X.Sign * radial > 0 && residual.Y.Sign * cap > 0
 582246            && WideArithmetic.SubtractSigned320(planarZ, geometry.HalfCore).Sign <= 0
 582247            && WideArithmetic.AddSigned320(planarZ, geometry.HalfCore).Sign >= 0
 582248            && (geometry.CapsuleHalf.IsZero || WideAxis3.Dot(residual, geometry.CapsuleAxis).Sign * capsule >= 0);
 249    }
 250
 251    [MethodImpl(MethodImplOptions.NoInlining)]
 252    private static void KeepCapsuleSlabHorizontalCorners(ref CapsuleSlabCandidates candidates, WideAxis3 offset, WideAxi
 253    {
 12254        Span<ulong> work = stackalloc ulong[3 * Words];
 36255        Span<ulong> factor = Slot(work, 0), component = Slot(work, 1), product = Slot(work, 2);
 12256        Import(WideArithmetic.MultiplySigned320(candidates.Geometry.Radius, candidates.Geometry.AxisDenominator), factor
 72257        for (int side = -1; side <= 1; side += 2)
 258        {
 24259            ProjectPerpendicular(candidates.Geometry.CapsuleAxis, offset, candidates.Direction, candidates.DirectionSign
 192260            for (int axis = 0; axis < 3; axis++)
 261            {
 72262                Signed320 value = Component(radial, axis);
 144263                Import(value, component); WideArithmetic.MultiplyMagnitudes(factor, component, product);
 72264                Add(product, side * value.Sign, Slot(candidates.Direction, axis), ref candidates.DirectionSigns[axis]);
 265            }
 24266            candidates.KeepDirection();
 267        }
 12268    }
 269
 270    [MethodImpl(MethodImplOptions.NoInlining)]
 271    private static bool BuildCapsuleSlabEndpointRim(in CapsuleSlabGeometry geometry, WideAxis3 q,
 272        int cap, int end, int capsule, Span<ulong> values, Span<int> signs)
 273    {
 948274        if (q.Y.Sign * cap <= 0 || q.Z.Sign * end <= 0) return false;
 104275        Signed576 radialSquared = CircularRimContactAlgebra.RadialDot(q, q);
 104276        Signed576 radiusSquared = WideArithmetic.MultiplySigned320(geometry.Radius, geometry.Radius);
 108277        if (WideArithmetic.SubtractSigned576(radialSquared, radiusSquared).Sign <= 0) return false;
 100278        Span<ulong> work = stackalloc ulong[6 * Words];
 100279        Span<int> directionSigns = stackalloc int[3];
 400280        Span<ulong> direction = work[..(3 * Words)], root = Slot(work, 3), a = Slot(work, 4), b = Slot(work, 5);
 100281        Import(radialSquared, root);
 100282        if (!geometry.CapsuleHalf.IsZero)
 283        {
 98284            Signed576 projection = WideAxis3.Dot(q, geometry.CapsuleAxis);
 98285            Signed576 radialProjection = CircularRimContactAlgebra.RadialDot(q, geometry.CapsuleAxis);
 98286            ImportCylinderCapsuleFeature(WideArithmetic.MultiplySigned576ToSigned832(radialProjection, geometry.Radius),
 98287            Import(projection, b);
 98288            if (GetConvexContactCandidateQuadraticSign(a, -radialProjection.Sign, b, projection.Sign, root) * capsule <=
 94289                return false;
 290        }
 12291        values.Clear(); signs.Clear();
 6292        WriteDirection(new WideAxis3(q.X, default, q.Z), direction, directionSigns);
 6293        Import(geometry.Radius, a);
 48294        for (int axis = 0; axis < 3; axis++)
 295        {
 18296            WideArithmetic.MultiplyMagnitudes(Slot(direction, axis), a, b);
 36297            b.CopyTo(Slot(direction, axis)); directionSigns[axis] = IsZero(b) ? 0 : -directionSigns[axis];
 298        }
 6299        WriteWorldDirection(geometry.WorldBasis, direction, directionSigns, values, signs);
 6300        WriteDirection(q, direction, directionSigns);
 6301        WriteWorldDirection(geometry.WorldBasis, direction, directionSigns, values[(3 * Words)..], signs[3..]);
 12302        root.CopyTo(Slot(values, 6)); root.CopyTo(Slot(values, 9));
 6303        Import(WideArithmetic.AddSigned576(q.SquaredLength, radiusSquared), Slot(values, 7));
 6304        Import(WideArithmetic.AddSigned320(geometry.Radius, geometry.Radius), Slot(values, 8));
 6305        Import(WideArithmetic.MultiplySigned192(geometry.RawScale, geometry.RawScale), Slot(values, 10));
 12306        signs[7] = 1; signs[8] = geometry.Radius.IsZero ? 0 : -1;
 6307        return true;
 308    }
 309}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CapsuleSlabRim.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CapsuleSlabRim.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 System.Runtime.CompilerServices;
 10using static FixedMathSharp.Geometry.CylinderContactAlgebra;
 11using static FixedMathSharp.Geometry.CircularRimContactAlgebra;
 12
 13namespace FixedMathSharp.Geometry;
 14
 15/// <content>
 16/// Exact constrained circular-rim selection for capsule/stadium-slab contacts.
 17/// </content>
 18internal static partial class WideConvexPrismRelations
 19{
 20    private const int CapsuleSlabParameterSlots = 26;
 21    // The reviewed value-polynomial height is <2800 bits, including the
 22    // common variable shift. Do not size retained cells from the field width.
 23    private const int CapsuleSlabValueCellWords = (16 * (2800 + 11) + 255) / 64;
 24    // The unshifted stationary quartic W has coefficient height <2520 bits.
 25    private const int CapsuleSlabParameterCellWords = (16 * (2520 + 11) + 255) / 64;
 26
 27    private ref struct CapsuleSlabRimSelection
 28    {
 29        internal bool HasValue;
 30        internal WideAxis3 First, Second;
 31        internal int GapSign;
 32        internal ulong AnalyticUpperTwiceRaw;
 33        internal FiniteAxisValueRoot Value, Parameter;
 34        internal Span<ulong> Values, Cell, ParameterValues, ParameterCell;
 35        internal Span<sbyte> Signs, ParameterSigns;
 36
 37        internal void KeepParameter(scoped FiniteAxisValueRoot root, WideAxis3 first, WideAxis3 second, int gapSign)
 38        {
 4739            Parameter = FiniteAxisValueRoot.CopyTo(root, ParameterValues, ParameterSigns, ParameterCell);
 18840            First = first; Second = second; GapSign = gapSign; HasValue = true;
 4741        }
 42    }
 43
 44    [MethodImpl(MethodImplOptions.NoInlining)]
 45    private static bool TryImproveCapsuleSlabRim(in CapsuleSlabGeometry geometry, ConvexContactCandidate analytic,
 46        Fixed64 capsuleRadius, out bool intersects, out Vector3d normal, out Fixed64 depth, out bool clamped)
 47    {
 23248        intersects = false; normal = default; depth = default; clamped = false;
 5849        var best = new CapsuleSlabRimSelection
 5850        {
 5851            Values = stackalloc ulong[5 * ValueWords], Signs = stackalloc sbyte[5],
 5852            Cell = stackalloc ulong[CapsuleSlabValueCellWords],
 5853            ParameterValues = stackalloc ulong[5 * Words], ParameterSigns = stackalloc sbyte[5],
 5854            ParameterCell = stackalloc ulong[CapsuleSlabParameterCellWords]
 5855        };
 5856        GetBasis(geometry.CapsuleAxis, out WideAxis3 u, out WideAxis3 v);
 5857        if (analytic.GapSign > 0)
 58        {
 1959            GetRoundedConvexContactCandidateDepth(analytic, Fixed64.Zero, out Fixed64 magnitude, out bool magnitudeClamp
 1960            if (!magnitudeClamped)
 1861                best.AnalyticUpperTwiceRaw = ((ulong)magnitude.m_rawValue << 1) | 1UL;
 62        }
 63        // Every quadrant and reciprocal chart is retained. Reflection and
 64        // largest-positive-root selection are not valid inside an end region.
 27465        for (int cap = -1; cap <= 1; cap += 2)
 57866            for (int end = -1; end <= 1; end += 2)
 115667                for (int firstSign = -1; firstSign <= 1; firstSign += 2)
 228268                    for (int secondSign = -1; secondSign <= 1; secondSign += 2)
 69                        {
 154670                            WideAxis3 first = firstSign > 0 ? u : -u, second = secondSign > 0 ? v : -v;
 77371                            if (KeepCapsuleSlabRimCharts(geometry, cap, end, first, second, analytic, ref best))
 72                                goto RimSelected;
 73                        }
 74        RimSelected:
 6975        if (!best.HasValue) return false;
 4776        if (best.GapSign == 0)
 77        {
 278            intersects = true; depth = capsuleRadius;
 79        }
 80        else
 81        {
 4682            intersects = ConvexContactValueRoot.GetRoundedDepth(geometry.RawScale, geometry.ValueShift,
 4683                ref best.Value, out depth, out clamped, best.GapSign, capsuleRadius);
 84        }
 8385        if (intersects) normal = GetCapsuleSlabRimNormal(geometry, ref best);
 4786        return true;
 87    }
 88
 89    [MethodImpl(MethodImplOptions.NoInlining)]
 90    private static bool KeepCapsuleSlabRimCharts(in CapsuleSlabGeometry geometry, int cap, int end,
 91        WideAxis3 first, WideAxis3 second, scoped ConvexContactCandidate analytic,
 92        ref CapsuleSlabRimSelection best)
 93    {
 94        // GetBasis puts each Y/Z coordinate in exactly one of the two
 95        // directions. Its sign is therefore constant on the open chart
 96        // (0,1], even after swapping/sign changes. Admit the whole chart here;
 97        // no per-root cap/end polynomials or sign queries are needed.
 98        // This paired owner receives the original GetBasis order: second.Y is zero.
 77399        if (first.Y.Sign * cap < 0
 773100            || first.Z.Sign * end < 0 || second.Z.Sign * end < 0)
 563101            return false;
 210102        Span<ulong> data = stackalloc ulong[CapsuleSlabParameterSlots * Words];
 210103        Span<sbyte> signs = stackalloc sbyte[CapsuleSlabParameterSlots];
 210104        BuildParameter(geometry.CapOffset(cap, end), geometry.Radius, geometry.RawScale, first, second, data, signs);
 210105        Span<ulong> batchCells = stackalloc ulong[64];
 210106        Span<int> batchShifts = stackalloc int[8];
 210107        Span<ulong> reciprocal = stackalloc ulong[5 * Words];
 210108        Span<sbyte> reciprocalSigns = stackalloc sbyte[5];
 210109        Span<ulong> reciprocalCells = stackalloc ulong[64];
 210110        Span<int> reciprocalShifts = stackalloc int[8];
 210111        WideFiniteAxisIntersection.GetFiniteValueReciprocalRoots(data[..(5 * Words)], signs[..5],
 210112            reciprocal, reciprocalSigns, batchCells, batchShifts, reciprocalCells, reciprocalShifts,
 210113            out FiniteAxisValueRoots roots, out FiniteAxisValueRoots reciprocalRoots);
 210114        if (KeepCapsuleSlabRimChart(geometry, cap, end, first, second, analytic, data, signs, roots, ref best))
 37115            return true;
 116        // Swapping the basis reverses the homogeneous stationary quartic;
 117        // both original charts and their order remain. Rebuild admission/value
 118        // data in the same buffer after chart zero's independent winner copies.
 173119        BuildParameter(geometry.CapOffset(cap, end), geometry.Radius, geometry.RawScale, second, first, data, signs);
 120#if DEBUG
 121        System.Diagnostics.Debug.Assert(signs[..5].SequenceEqual(reciprocalSigns)
 122            && data[..(5 * Words)].SequenceEqual(reciprocal));
 123#endif
 173124        return KeepCapsuleSlabRimChart(geometry, cap, end, second, first, analytic, data, signs, reciprocalRoots, ref be
 125    }
 126
 127    [MethodImpl(MethodImplOptions.NoInlining)]
 128    private static bool KeepCapsuleSlabRimChart(in CapsuleSlabGeometry geometry, int cap, int end,
 129        WideAxis3 first, WideAxis3 second, scoped ConvexContactCandidate analytic,
 130        scoped Span<ulong> data, scoped Span<sbyte> signs, scoped FiniteAxisValueRoots roots,
 131        ref CapsuleSlabRimSelection best)
 132    {
 383133        Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords(data[..(5 * Words)], 
 383134        Span<ulong> values = stackalloc ulong[5 * ValueWords];
 383135        Span<sbyte> valueSigns = stackalloc sbyte[5];
 383136        Span<ulong> valueCell = stackalloc ulong[CapsuleSlabValueCellWords];
 383137        bool valuesReady = false;
 1146138        for (int ordinal = 0; ordinal < roots.Count; ordinal++)
 139        {
 227140            scoped FiniteAxisValueRoot root = FiniteAxisValueRoots.GetRoot(roots, ordinal, data[..(5 * Words)], signs[..
 141            // Squaring the stationary equation introduces opposite-sign roots.
 142            // Simultaneous K=L=0 belongs to the earlier principal-axis fan.
 227143            int kSign = Sign(ref root, data, signs, 8, 2);
 227144            if (kSign == 0 || Sign(ref root, data, signs, 10, 3) != -kSign) continue;
 138145            int gapSign = Sign(ref root, data, signs, 13, 3) * kSign;
 138146            int incumbentSign = best.HasValue ? best.GapSign : analytic.GapSign;
 138147            if (gapSign > incumbentSign || gapSign == 0 && incumbentSign == 0) continue;
 91148            if (gapSign == 0)
 149            {
 1150                best.KeepParameter(root, first, second, gapSign);
 1151                continue;
 152            }
 153            // A positive analytic gap rounded to d is at most d+1/2 raw.
 154            // A root at or above that exact bound cannot win (ties stay earlier).
 155            // Reuse the triangle/box certificate before value mapping; negative
 156            // gaps retain exact signed ranking, and clamped gaps supply no bound.
 90157            if (best.AnalyticUpperTwiceRaw != 0 && gapSign > 0
 90158                && ConvexContactValueRoot.CompareSquaredGapToTwiceRaw(ref root,
 90159                    data.Slice(16 * Words, 5 * Words), signs.Slice(16, 5),
 90160                    data.Slice(21 * Words, 5 * Words), signs.Slice(21, 5), best.AnalyticUpperTwiceRaw) >= 0)
 161                continue;
 53162            if (!valuesReady)
 163            {
 52164                BuildValues(geometry.CapsuleAxis, geometry.CapOffset(cap, end), geometry.Radius,
 52165                    geometry.ValueShift, values, valueSigns);
 52166                valuesReady = true;
 167            }
 53168            scoped FiniteAxisValueRoot value = ConvexContactValueRoot.MapSquaredValue(geometry.RawScale, geometry.ValueS
 53169                ref root, data.Slice(16 * Words, 5 * Words), signs.Slice(16, 5),
 53170                data.Slice(21 * Words, 5 * Words), signs.Slice(21, 5), values, valueSigns, valueCell);
 53171            if (gapSign == incumbentSign)
 172            {
 50173                int squaredComparison = best.HasValue
 50174                    ? WideFiniteAxisIntersection.CompareFiniteValueRoots(value, best.Value)
 50175                    : ConvexContactValueRoot.CompareRootSquared(geometry.RawScale, geometry.ValueShift, ref value, analy
 50176                if (gapSign * squaredComparison >= 0) continue;
 177            }
 46178            best.Value = FiniteAxisValueRoot.CopyTo(value, best.Values, best.Signs, best.Cell);
 179            // For a negative admitted stationary gap g, a feasible finite-core
 180            // projection gives q=RawScale*g*n and q.(x-q)>=0 throughout the
 181            // convex Minkowski difference. Thus q is its unique closest point,
 182            // g is the global minimum, and no later chart can change its normal.
 183            // Strict ranking above still keeps earlier ties. Positive/zero gaps
 184            // retain full traversal. The mapped cell is dead after the value copy;
 185            // borrow it for the certificate, then retain any parameter refinement.
 46186            bool complete = gapSign < 0 && HasSegmentSupport(geometry.CapsuleAxis, geometry.CapsuleHalf,
 46187                geometry.CapOffset(cap, end), geometry.Radius, first, second, ref root,
 46188                data.Slice(5 * Words, 3 * Words), signs.Slice(5, 3), valueCell);
 46189            best.KeepParameter(root, first, second, gapSign);
 83190            if (complete) return true;
 191        }
 346192        return false;
 193    }
 194
 195    [MethodImpl(MethodImplOptions.NoInlining)]
 196    private static Vector3d GetCapsuleSlabRimNormal(in CapsuleSlabGeometry geometry, scoped ref CapsuleSlabRimSelection 
 197    {
 198        // Admission and value mapping already refined this parameter. Retain
 199        // that exact cell and its metadata rather than rebuilding its quartic
 200        // and isolating the same root again for final normal rounding.
 36201        Span<ulong> gradient = stackalloc ulong[30];
 36202        Span<sbyte> gradientSigns = stackalloc sbyte[6];
 36203        WriteGradient(Transform(geometry.WorldBasis, best.First), Transform(geometry.WorldBasis, best.Second), gradient,
 36204        return ConvexContactValueRoot.GetNormalizedDirection(ref best.Parameter, gradient, gradientSigns, 1);
 205    }
 206}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.ContactCandidate.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.ContactCandidate.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact comparison and final rounding of analytic convex contact
 14/// candidates. Geometry and candidate-buffer ownership remain with the caller.
 15/// </content>
 16internal static partial class WideConvexPrismRelations
 17{
 18    // No fixed-width operation below truncates an input to fit a scratch span.
 19    // Multiplication reserves the sum of active operand lengths; a signed sum
 20    // reserves an additional word. For forty-word candidate fields, cross-
 21    // denominated A/B have at most 81/80 words. Their radical radicands have
 22    // at most 200 words, plus one guard word for CompareRadicalPairs' additions
 23    // and shifts. Its three-term reduction fits twice that scratch width.
 24    // Normal-square coefficients use at most 122 words (three 40-word factors
 25    // and two carry words). Integer midpoint factors add at most two words.
 26    // Actual active lengths, not those maxima, size every arithmetic scratch.
 27    internal static int CompareConvexContactCandidates(
 28        ConvexContactCandidate left,
 29        ConvexContactCandidate right)
 30    {
 11641331        if (left.GapSign != right.GapSign)
 465032            return left.GapSign.CompareTo(right.GapSign);
 11176333        if (left.GapSign == 0)
 29834            return 0;
 35
 11146536        int words = Math.Max(
 11146537            ConvexContactCandidateProductWords(
 11146538                left.GapRational, right.GapDenominator),
 11146539            ConvexContactCandidateProductWords(
 11146540                right.GapRational, left.GapDenominator)) + 1;
 11146541        Span<ulong> first = stackalloc ulong[words];
 11146542        Span<ulong> second = stackalloc ulong[words];
 11146543        Span<ulong> rational = stackalloc ulong[words];
 11146544        WideArithmetic.MultiplyMagnitudes(
 11146545            left.GapRational, right.GapDenominator, first);
 11146546        WideArithmetic.MultiplyMagnitudes(
 11146547            right.GapRational, left.GapDenominator, second);
 11146548        CombineWideSignedMagnitudes(
 11146549            first, left.GapRationalSign,
 11146550            second, -right.GapRationalSign,
 11146551            rational, out int rationalSign);
 52
 11146553        Span<ulong> leftRadical = stackalloc ulong[
 11146554            ConvexContactCandidateProductWords(
 11146555                left.GapRadical, right.GapDenominator)];
 11146556        Span<ulong> rightRadical = stackalloc ulong[
 11146557            ConvexContactCandidateProductWords(
 11146558                right.GapRadical, left.GapDenominator)];
 11146559        WideArithmetic.MultiplyMagnitudes(
 11146560            left.GapRadical, right.GapDenominator, leftRadical);
 11146561        WideArithmetic.MultiplyMagnitudes(
 11146562            right.GapRadical, left.GapDenominator, rightRadical);
 11146563        return left.GapSign * GetConvexContactCandidateThreeTermSign(
 11146564            rational, rationalSign,
 11146565            leftRadical, left.GapRadicalSign, left.GapRadicand,
 11146566            rightRadical, -right.GapRadicalSign, right.GapRadicand);
 67    }
 68
 69    internal static int CompareConvexContactCandidateDepthToTwiceRaw(
 70        ConvexContactCandidate candidate,
 71        Fixed64 radiusOffset,
 72        Signed192 twiceRaw)
 73    {
 701074        Signed192 target = WideArithmetic.SubtractSigned192(
 701075            twiceRaw,
 701076            new Signed192(0UL, 0UL,
 701077                unchecked((ulong)radiusOffset.m_rawValue << 1)));
 701078        if (candidate.GapSign != target.Sign)
 51179            return candidate.GapSign.CompareTo(target.Sign);
 649980        if (candidate.GapSign == 0)
 881            return 0;
 82
 649183        Span<ulong> targetMagnitude = stackalloc ulong[3];
 649184        WideArithmetic.GetMagnitude(target,
 649185            out targetMagnitude[2], out targetMagnitude[1],
 649186            out targetMagnitude[0]);
 649187        Span<ulong> targetSquare = stackalloc ulong[
 649188            ConvexContactCandidateProductWords(
 649189                targetMagnitude, targetMagnitude)];
 649190        WideArithmetic.MultiplyMagnitudes(
 649191            targetMagnitude, targetMagnitude, targetSquare);
 649192        int words = Math.Max(
 649193            ConvexContactCandidateLength(candidate.GapRational) + 1,
 649194            ConvexContactCandidateProductWords(
 649195                targetSquare, candidate.GapDenominator)) + 1;
 649196        Span<ulong> rational = stackalloc ulong[words];
 649197        Span<ulong> threshold = stackalloc ulong[words];
 649198        Span<ulong> difference = stackalloc ulong[words];
 649199        rational.Clear();
 6491100        candidate.GapRational.Slice(0,
 6491101            ConvexContactCandidateLength(candidate.GapRational))
 6491102            .CopyTo(rational);
 6491103        ShiftLeft(rational, 2);
 6491104        WideArithmetic.MultiplyMagnitudes(
 6491105            targetSquare, candidate.GapDenominator, threshold);
 6491106        CombineWideSignedMagnitudes(
 6491107            rational, candidate.GapRationalSign,
 6491108            threshold, -1, difference, out int differenceSign);
 6491109        Span<ulong> radical = stackalloc ulong[
 6491110            ConvexContactCandidateLength(candidate.GapRadical) + 1];
 6491111        radical.Clear();
 6491112        candidate.GapRadical.Slice(0,
 6491113            ConvexContactCandidateLength(candidate.GapRadical))
 6491114            .CopyTo(radical);
 6491115        ShiftLeft(radical, 2);
 6491116        return candidate.GapSign * GetConvexContactCandidateQuadraticSign(
 6491117            difference, differenceSign,
 6491118            radical, candidate.GapRadicalSign, candidate.GapRadicand);
 119    }
 120
 121    /// <summary>
 122    /// Rounds an admitted nonnegative depth, including the supplied radius offset,
 123    /// once to nearest-even; clamping describes the exact value, not rounding.
 124    /// </summary>
 125    internal static void GetRoundedConvexContactCandidateDepth(
 126        ConvexContactCandidate candidate,
 127        Fixed64 radiusOffset,
 128        out Fixed64 rounded,
 129        out bool isClamped)
 130    {
 2758131        if (candidate.GapSign == 0)
 132        {
 156133            rounded = radiusOffset;
 156134            isClamped = false;
 156135            return;
 136        }
 137
 2602138        ulong radiusRaw = unchecked((ulong)radiusOffset.m_rawValue);
 2602139        GetConvexContactCandidateMagnitudeFloorBounds(candidate,
 2602140            candidate.GapSign < 0 ? radiusRaw : (ulong)long.MaxValue,
 2602141            out ulong lowerMagnitude, out ulong upperMagnitude);
 142        ulong low;
 143        ulong high;
 2602144        if (candidate.GapSign < 0)
 145        {
 146            // Magnitude bounds describe floors. Subtraction can cross one
 147            // additional raw unit when the magnitude is nonintegral; contact
 148            // admission proves its exact value is at most the radius.
 211149            low = upperMagnitude >= radiusRaw ? 0 : radiusRaw - upperMagnitude - 1;
 211150            high = radiusRaw - lowerMagnitude + 1;
 151        }
 152        else
 153        {
 154            // Clipped magnitude floors also cover conceptual overflow. Search
 155            // includes MaxValue itself; its exact comparison below still owns
 156            // clamping, even when both enclosure floors already equal the cap.
 2391157            low = Math.Min((ulong)long.MaxValue, radiusRaw + lowerMagnitude);
 2391158            high = Math.Min(1UL << 63, radiusRaw + upperMagnitude + 1);
 159        }
 5784160        while (low < high)
 161        {
 3182162            ulong midpoint = low + ((high - low) >> 1);
 3182163            int comparison = CompareConvexContactCandidateDepthToTwiceRaw(
 3182164                candidate, radiusOffset,
 3182165                new Signed192(0UL, 0UL, midpoint << 1));
 3182166            if (comparison >= 0)
 2711167                low = midpoint + 1UL;
 168            else
 471169                high = midpoint;
 170        }
 171
 2602172        ulong floor = low - 1UL;
 2602173        if (floor == unchecked((ulong)long.MaxValue))
 174        {
 28175            rounded = Fixed64.MaxValue;
 28176            isClamped = CompareConvexContactCandidateDepthToTwiceRaw(
 28177                candidate, radiusOffset,
 28178                new Signed192(0UL, 0UL, floor << 1)) > 0;
 28179            return;
 180        }
 181
 2574182        int midpointComparison =
 2574183            CompareConvexContactCandidateDepthToTwiceRaw(
 2574184                candidate, radiusOffset,
 2574185                new Signed192(0UL, 0UL, (floor << 1) | 1UL));
 2574186        rounded = Fixed64.FromRaw((long)(floor + GetNearestEvenIncrement(
 2574187            midpointComparison, floor)));
 2574188        isClamped = false;
 2574189    }
 190
 191    internal static Vector3d GetConvexContactCandidateNormal(
 3908192        ConvexContactCandidate candidate) => GetConvexContactCandidateScaledNormal(candidate, Fixed64.One);
 193
 194    internal static Vector3d GetConvexContactCandidateScaledNormal(
 195        ConvexContactCandidate candidate, Fixed64 scale)
 196    {
 4821197        if (scale == Fixed64.Zero)
 16198            return Vector3d.Zero;
 4805199        int xSign = GetConvexContactCandidateQuadraticSign(candidate.NormalRational(0), candidate.Signs[0],
 4805200            candidate.NormalRadical(0), candidate.Signs[3], candidate.NormalRadicand);
 4805201        int ySign = GetConvexContactCandidateQuadraticSign(candidate.NormalRational(1), candidate.Signs[1],
 4805202            candidate.NormalRadical(1), candidate.Signs[4], candidate.NormalRadicand);
 4805203        int zSign = GetConvexContactCandidateQuadraticSign(candidate.NormalRational(2), candidate.Signs[2],
 4805204            candidate.NormalRadical(2), candidate.Signs[5], candidate.NormalRadicand);
 4805205        if ((xSign == 0 ? 0 : 1) + (ySign == 0 ? 0 : 1) + (zSign == 0 ? 0 : 1) == 1)
 2586206            return new Vector3d(xSign * scale, ySign * scale, zSign * scale);
 207
 2219208        int rationalWords = 0;
 2219209        int radicalWords = 0;
 17752210        for (int component = 0; component < 3; component++)
 211        {
 6657212            rationalWords = Math.Max(rationalWords,
 6657213                ConvexContactCandidateLength(
 6657214                    candidate.NormalRational(component)));
 6657215            radicalWords = Math.Max(radicalWords,
 6657216                ConvexContactCandidateLength(
 6657217                    candidate.NormalRadical(component)));
 218        }
 219
 2219220        int words = Math.Max(
 2219221            Math.Max(rationalWords * 2,
 2219222                radicalWords * 2 + ConvexContactCandidateLength(
 2219223                    candidate.NormalRadicand)),
 2219224            rationalWords + radicalWords + 1) + 2;
 225        // Slots 0..2 and 3..5 hold each component square's rational/radical
 226        // coefficient; slots 6 and 7 hold their sums (the normal norm square).
 2219227        Span<ulong> squares = stackalloc ulong[8 * words];
 2219228        Span<int> signs = stackalloc int[4];
 2219229        Span<ulong> sum = stackalloc ulong[words];
 2219230        squares.Clear();
 2219231        signs.Clear();
 2219232        Span<ulong> normRational = squares.Slice(6 * words, words);
 2219233        Span<ulong> normRadical = squares.Slice(7 * words, words);
 17752234        for (int component = 0; component < 3; component++)
 235        {
 6657236            Span<ulong> rational = squares.Slice(component * words, words);
 6657237            Span<ulong> radical = squares.Slice((component + 3) * words, words);
 6657238            BuildConvexContactCandidateNormalSquare(
 6657239                candidate, component, rational, radical,
 6657240                out signs[component]);
 6657241            WideArithmetic.AddMagnitudeInto(rational, normRational);
 6657242            CombineWideSignedMagnitudes(
 6657243                normRadical, signs[3], radical, signs[component],
 6657244                sum, out signs[3]);
 6657245            sum.CopyTo(normRadical);
 246        }
 247
 2219248        WideArithmetic.GetMagnitudeSquareRootBounds(candidate.NormalRadicand,
 2219249            out Signed192 lowerRoot, out Signed192 upperRoot, out int rootShift);
 2219250        Span<ulong> roots = stackalloc ulong[4]
 2219251            { lowerRoot.Low, lowerRoot.Middle, upperRoot.Low, upperRoot.Middle };
 252        // A coefficient times the <=97-bit prefix root, shifted by k, needs
 253        // at most coefficientWords+2+ceil(k/64)+1 words. Use the scaled
 254        // component width too, so numerator and denominator bounds share one
 255        // padded division width. Forty-word fields give at most 147 words.
 2219256        int boundWords = words + 6 + ((rootShift + 63) >> 6);
 2219257        Span<ulong> denominatorBounds = stackalloc ulong[2 * boundWords];
 2219258        GetConvexContactCandidateQuadraticBounds(normRational, 1,
 2219259            normRadical, signs[3], roots, rootShift,
 2219260            denominatorBounds[..boundWords], denominatorBounds[boundWords..],
 2219261            out int denominatorSign, out _);
 2219262        if (denominatorSign > 0)
 263        {
 264            // scaledSquares=(2S)^2*n_i^2, so its matching denominator is
 265            // 4*|n|^2. A nonpositive lower enclosure is uncertainty, not a
 266            // zero norm: retain the existing full search in that case.
 2210267            ShiftLeft(denominatorBounds[..boundWords], 2);
 2210268            ShiftLeft(denominatorBounds[boundWords..], 2);
 269        }
 2219270        ReadOnlySpan<ulong> positiveDenominatorBounds = denominatorSign > 0
 2219271            ? denominatorBounds : ReadOnlySpan<ulong>.Empty;
 2219272        return new Vector3d(
 2219273            GetRoundedConvexContactCandidateNormalComponent(
 2219274                candidate, 0, squares, signs, words, (ulong)scale.m_rawValue,
 2219275                roots, rootShift, positiveDenominatorBounds),
 2219276            GetRoundedConvexContactCandidateNormalComponent(
 2219277                candidate, 1, squares, signs, words, (ulong)scale.m_rawValue,
 2219278                roots, rootShift, positiveDenominatorBounds),
 2219279            GetRoundedConvexContactCandidateNormalComponent(
 2219280                candidate, 2, squares, signs, words, (ulong)scale.m_rawValue,
 2219281                roots, rootShift, positiveDenominatorBounds));
 282    }
 283
 284    private static void BuildConvexContactCandidateNormalSquare(
 285        ConvexContactCandidate candidate,
 286        int component,
 287        Span<ulong> rational,
 288        Span<ulong> radical,
 289        out int radicalSign)
 290    {
 6657291        ReadOnlySpan<ulong> a = candidate.NormalRational(component);
 6657292        ReadOnlySpan<ulong> b = candidate.NormalRadical(component);
 6657293        Span<ulong> aSquared = stackalloc ulong[rational.Length];
 6657294        Span<ulong> bSquared = stackalloc ulong[rational.Length];
 6657295        Span<ulong> bSquaredK = stackalloc ulong[rational.Length];
 6657296        WideArithmetic.MultiplyMagnitudes(a, a, aSquared);
 6657297        WideArithmetic.MultiplyMagnitudes(b, b, bSquared);
 6657298        WideArithmetic.MultiplyMagnitudes(
 6657299            bSquared, candidate.NormalRadicand, bSquaredK);
 6657300        WideArithmetic.AddEqualMagnitudes(aSquared, bSquaredK, rational);
 6657301        WideArithmetic.MultiplyMagnitudes(a, b, radical);
 6657302        ShiftLeft(radical, 1);
 6657303        radicalSign = IsZero(radical) ? 0
 6657304            : candidate.Signs[component] * candidate.Signs[component + 3];
 6657305    }
 306
 307    private static Fixed64 GetRoundedConvexContactCandidateNormalComponent(
 308        ConvexContactCandidate candidate,
 309        int component,
 310        ReadOnlySpan<ulong> squares,
 311        ReadOnlySpan<int> signs,
 312        int words,
 313        ulong scale,
 314        ReadOnlySpan<ulong> roots,
 315        int rootShift,
 316        ReadOnlySpan<ulong> denominatorBounds)
 317    {
 6657318        int sign = GetConvexContactCandidateQuadraticSign(
 6657319            candidate.NormalRational(component), candidate.Signs[component],
 6657320            candidate.NormalRadical(component), candidate.Signs[component + 3],
 6657321            candidate.NormalRadicand);
 6657322        if (sign == 0)
 1415323            return Fixed64.Zero;
 324
 325        // These two products do not depend on the searched threshold. Keep
 326        // them in this returning component frame, including the final tie
 327        // comparison; do not enlarge or mutate the borrowed square slots.
 5242328        int productWords = words + 3;
 5242329        Span<ulong> scaledSquares = stackalloc ulong[2 * productWords];
 5242330        Span<ulong> scaleSquared = stackalloc ulong[2];
 5242331        Fixed64.Multiply64To128(scale << 1, scale << 1,
 5242332            out scaleSquared[1], out scaleSquared[0]);
 5242333        WideArithmetic.MultiplyMagnitudes(
 5242334            squares.Slice(component * words, words), scaleSquared, scaledSquares[..productWords]);
 5242335        WideArithmetic.MultiplyMagnitudes(
 5242336            squares.Slice((component + 3) * words, words), scaleSquared, scaledSquares[productWords..]);
 337
 5242338        ulong low = 0UL;
 339        // Exact one-component normals already returned above. Every remaining
 340        // nonzero component is strictly below the norm, so its raw floor is
 341        // below scale; nearest-even rounding may still produce scale.
 5242342        ulong high = scale;
 5242343        if (!denominatorBounds.IsEmpty)
 344        {
 5228345            GetConvexContactCandidateNormalFloorBounds(scaledSquares,
 5228346                signs[component], roots, rootShift, denominatorBounds, scale,
 5228347                out low, out ulong upperFloor);
 5228348            high = Math.Min(high, upperFloor + 1);
 349        }
 11110350        while (low < high)
 351        {
 5868352            ulong midpoint = low + ((high - low) >> 1);
 5868353            int comparison =
 5868354                CompareConvexContactCandidateNormalComponentToTwiceRaw(
 5868355                    squares, signs, words, component,
 5868356                    candidate.NormalRadicand, midpoint << 1, scaledSquares);
 5868357            if (comparison >= 0)
 5525358                low = midpoint + 1UL;
 359            else
 343360                high = midpoint;
 361        }
 362
 5242363        ulong floor = low - 1UL;
 5242364        int midpointComparison =
 5242365            CompareConvexContactCandidateNormalComponentToTwiceRaw(
 5242366                squares, signs, words, component,
 5242367                candidate.NormalRadicand, (floor << 1) | 1UL, scaledSquares);
 5242368        floor += GetNearestEvenIncrement(midpointComparison, floor);
 5242369        return Fixed64.FromRaw(sign * (long)floor);
 370    }
 371
 372    private static void GetConvexContactCandidateMagnitudeFloorBounds(
 373        ConvexContactCandidate candidate, ulong cap,
 374        out ulong lower, out ulong upper)
 375    {
 2602376        WideArithmetic.GetMagnitudeSquareRootBounds(candidate.GapRadicand,
 2602377            out Signed192 lowerRoot, out Signed192 upperRoot, out int shift);
 2602378        Span<ulong> roots = stackalloc ulong[4]
 2602379            { lowerRoot.Low, lowerRoot.Middle, upperRoot.Low, upperRoot.Middle };
 2602380        int words = Math.Max(2, Math.Max(
 2602381            Math.Max(ConvexContactCandidateLength(candidate.GapRational),
 2602382                ConvexContactCandidateLength(candidate.GapDenominator)),
 2602383            ConvexContactCandidateLength(candidate.GapRadical) + 2 + ((shift + 63) >> 6)) + 1);
 2602384        Span<ulong> bounds = stackalloc ulong[3 * words];
 5204385        Span<ulong> minimum = bounds[..words], maximum = bounds.Slice(words, words);
 2602386        Span<ulong> denominator = bounds[(2 * words)..];
 2602387        GetConvexContactCandidateQuadraticBounds(candidate.GapRational,
 2602388            candidate.GapRationalSign, candidate.GapRadical,
 2602389            candidate.GapRadicalSign, roots, shift, minimum, maximum,
 2602390            out int minimumSign, out _);
 2602391        denominator.Clear();
 2602392        candidate.GapDenominator[..ConvexContactCandidateLength(candidate.GapDenominator)].CopyTo(denominator);
 393        // The true squared magnitude is nonnegative. A negative interval
 394        // minimum is cancellation in the enclosure, so its lower bound is 0.
 2602395        lower = minimumSign > 0
 2602396            ? WideArithmetic.GetRatioFloorSquareRoot(minimum, denominator, cap) : 0;
 2602397        upper = WideArithmetic.GetRatioFloorSquareRoot(maximum, denominator, cap);
 2602398    }
 399
 400    private static void GetConvexContactCandidateNormalFloorBounds(
 401        ReadOnlySpan<ulong> scaledSquares, int radicalSign,
 402        ReadOnlySpan<ulong> roots, int shift,
 403        ReadOnlySpan<ulong> denominatorBounds, ulong cap,
 404        out ulong lower, out ulong upper)
 405    {
 406        // This frame returns before exact searches. Keep its numerator bounds
 407        // out of the live stack of the wide quadratic sign comparisons.
 5228408        int words = denominatorBounds.Length / 2;
 5228409        int coefficientWords = scaledSquares.Length / 2;
 5228410        Span<ulong> minimum = stackalloc ulong[words];
 5228411        Span<ulong> maximum = stackalloc ulong[words];
 5228412        GetConvexContactCandidateQuadraticBounds(scaledSquares[..coefficientWords], 1,
 5228413            scaledSquares[coefficientWords..], radicalSign, roots, shift,
 5228414            minimum, maximum, out int minimumSign, out _);
 5228415        lower = minimumSign > 0
 5228416            ? WideArithmetic.GetRatioFloorSquareRoot(minimum, denominatorBounds[words..], cap) : 0;
 5228417        upper = WideArithmetic.GetRatioFloorSquareRoot(maximum, denominatorBounds[..words], cap);
 5228418    }
 419
 420    /// <summary>Encloses a signed quadratic value using factored, nonnegative root endpoints.</summary>
 421    /// <remarks>Both output magnitudes include all complete shifted products; signs are independent.</remarks>
 422    internal static void GetConvexContactCandidateQuadraticBounds(
 423        ReadOnlySpan<ulong> rational, int rationalSign,
 424        ReadOnlySpan<ulong> radical, int radicalSign,
 425        ReadOnlySpan<ulong> roots, int shift,
 426        Span<ulong> minimum, Span<ulong> maximum, out int minimumSign, out int maximumSign)
 427    {
 64883428        minimum.Clear();
 64883429        rational[..ConvexContactCandidateLength(rational)].CopyTo(minimum);
 64883430        minimum.CopyTo(maximum);
 64883431        minimumSign = IsZero(rational) ? 0 : rationalSign;
 64883432        maximumSign = minimumSign;
 64883433        AddConvexContactCandidateRadicalBounds(radical, radicalSign, roots, shift,
 64883434            minimum, maximum, ref minimumSign, ref maximumSign);
 64883435    }
 436
 437    private static void AddConvexContactCandidateRadicalBounds(
 438        ReadOnlySpan<ulong> radical, int radicalSign, ReadOnlySpan<ulong> roots, int shift,
 439        Span<ulong> minimum, Span<ulong> maximum, ref int minimumSign, ref int maximumSign)
 440    {
 97099441        Span<ulong> product = stackalloc ulong[ConvexContactCandidateLength(radical) + 2];
 442        // Negative coefficients reverse the root endpoints. Accumulate the
 443        // factored root directly, including signed cancellation and the upper
 444        // carry at 2^96, without constructing a much wider shifted root copy.
 97099445        WideArithmetic.MultiplyMagnitudes(radical,
 97099446            radicalSign < 0 ? roots[2..] : roots[..2], product);
 97099447        WideArithmetic.AddShiftedSignedMagnitude(product, radicalSign, shift, minimum, ref minimumSign);
 97099448        WideArithmetic.MultiplyMagnitudes(radical,
 97099449            radicalSign < 0 ? roots[..2] : roots[2..], product);
 97099450        WideArithmetic.AddShiftedSignedMagnitude(product, radicalSign, shift, maximum, ref maximumSign);
 97099451        if (IsZero(minimum))
 754452            minimumSign = 0;
 97099453        if (IsZero(maximum))
 685454            maximumSign = 0;
 97099455    }
 456
 457    private static int CompareConvexContactCandidateNormalComponentToTwiceRaw(
 458        ReadOnlySpan<ulong> squares,
 459        ReadOnlySpan<int> signs,
 460        int words,
 461        int component,
 462        ReadOnlySpan<ulong> radicand,
 463        ulong twiceRaw,
 464        ReadOnlySpan<ulong> scaledSquares)
 465    {
 466        // (2 S n_i)^2 - twiceRaw^2 |n|^2 has the sign of the unsquared
 467        // comparison, since both magnitudes and the norm are nonnegative.
 11110468        Span<ulong> thresholdSquared = stackalloc ulong[2];
 11110469        Fixed64.Multiply64To128(twiceRaw, twiceRaw,
 11110470            out thresholdSquared[1], out thresholdSquared[0]);
 11110471        int productWords = words + 3;
 11110472        Span<ulong> product = stackalloc ulong[productWords];
 11110473        Span<ulong> rational = stackalloc ulong[productWords];
 11110474        Span<ulong> radical = stackalloc ulong[productWords];
 11110475        WideArithmetic.MultiplyMagnitudes(
 11110476            squares.Slice(6 * words, words), thresholdSquared, product);
 11110477        CombineWideSignedMagnitudes(scaledSquares[..productWords], 1, product, -1,
 11110478            rational, out int rationalSign);
 11110479        WideArithmetic.MultiplyMagnitudes(
 11110480            squares.Slice(7 * words, words), thresholdSquared, product);
 11110481        CombineWideSignedMagnitudes(
 11110482            scaledSquares[productWords..], signs[component], product, -signs[3],
 11110483            radical, out int radicalSign);
 11110484        return GetConvexContactCandidateQuadraticSign(
 11110485            rational, rationalSign, radical, radicalSign, radicand);
 486    }
 487
 488    internal static int GetConvexContactCandidateQuadraticSign(
 489        ReadOnlySpan<ulong> rational,
 490        int rationalSign,
 491        ReadOnlySpan<ulong> radical,
 492        int radicalSign,
 493        ReadOnlySpan<ulong> radicand)
 494    {
 608025495        if (IsZero(rational))
 142324496            rationalSign = 0;
 608025497        if (IsZero(radical) || IsZero(radicand))
 389591498            radicalSign = 0;
 608025499        if (rationalSign == 0)
 142382500            return radicalSign;
 465643501        if (radicalSign == 0 || radicalSign == rationalSign)
 360976502            return rationalSign;
 503
 104667504        int words = Math.Max(
 104667505            ConvexContactCandidateLength(rational) * 2,
 104667506            ConvexContactCandidateLength(radical) * 2
 104667507                + ConvexContactCandidateLength(radicand));
 104667508        Span<ulong> rationalSquared = stackalloc ulong[words];
 104667509        Span<ulong> radicalSquared = stackalloc ulong[words];
 104667510        Span<ulong> radicalSquaredK = stackalloc ulong[words];
 104667511        WideArithmetic.MultiplyMagnitudes(rational, rational, rationalSquared);
 104667512        WideArithmetic.MultiplyMagnitudes(radical, radical, radicalSquared);
 104667513        WideArithmetic.MultiplyMagnitudes(
 104667514            radicalSquared, radicand, radicalSquaredK);
 104667515        return rationalSign * WideArithmetic.CompareMagnitudeEqualLength(
 104667516            rationalSquared, radicalSquaredK);
 517    }
 518
 519    private static int GetConvexContactCandidateThreeTermSign(
 520        ReadOnlySpan<ulong> rational,
 521        int rationalSign,
 522        ReadOnlySpan<ulong> firstCoefficient,
 523        int firstSign,
 524        ReadOnlySpan<ulong> firstRadicand,
 525        ReadOnlySpan<ulong> secondCoefficient,
 526        int secondSign,
 527        ReadOnlySpan<ulong> secondRadicand)
 528    {
 111465529        if (IsZero(rational))
 9689530            rationalSign = 0;
 111465531        if (IsZero(firstCoefficient) || IsZero(firstRadicand))
 14144532            firstSign = 0;
 111465533        if (IsZero(secondCoefficient) || IsZero(secondRadicand))
 63318534            secondSign = 0;
 111465535        if (firstSign == 0)
 14144536            return GetConvexContactCandidateQuadraticSign(
 14144537                rational, rationalSign,
 14144538                secondCoefficient, secondSign, secondRadicand);
 97321539        if (secondSign == 0)
 52243540            return GetConvexContactCandidateQuadraticSign(
 52243541                rational, rationalSign,
 52243542                firstCoefficient, firstSign, firstRadicand);
 45078543        if (firstSign == secondSign && rationalSign != -firstSign)
 9884544            return firstSign;
 545
 546        // Pure radical comparisons retain the cheaper exact path, including ties.
 35194547        if (rationalSign != 0)
 548        {
 64432549            Span<ulong> firstRoots = stackalloc ulong[4], secondRoots = stackalloc ulong[4];
 32216550            int firstShift = GetConvexContactCandidateRankRootBounds(firstRadicand, firstRoots);
 32216551            int secondShift = GetConvexContactCandidateRankRootBounds(secondRadicand, secondRoots);
 552            // Complete shifted products plus two carry words retain all three signed terms.
 32216553            int boundWords = Math.Max(ConvexContactCandidateLength(rational), Math.Max(
 32216554                ConvexContactCandidateLength(firstCoefficient) + 2 + (firstShift + 63) / 64,
 32216555                ConvexContactCandidateLength(secondCoefficient) + 2 + (secondShift + 63) / 64)) + 2;
 64432556            Span<ulong> minimum = stackalloc ulong[boundWords], maximum = stackalloc ulong[boundWords];
 32216557            GetConvexContactCandidateQuadraticBounds(rational, rationalSign, firstCoefficient, firstSign,
 32216558                firstRoots, firstShift, minimum, maximum, out int minimumSign, out int maximumSign);
 32216559            AddConvexContactCandidateRadicalBounds(secondCoefficient, secondSign, secondRoots, secondShift,
 32216560                minimum, maximum, ref minimumSign, ref maximumSign);
 561            // Disjoint signed enclosures decide the exact rank before squaring;
 562            // overlapping intervals retain the complete algebra, including ties.
 60733563            if (minimumSign > 0) return 1;
 6814564            if (maximumSign < 0) return -1;
 565        }
 566
 3562567        int words = Math.Max(
 3562568            ConvexContactCandidateLength(rational) * 2,
 3562569            Math.Max(
 3562570                ConvexContactCandidateLength(firstCoefficient) * 2
 3562571                    + ConvexContactCandidateLength(firstRadicand),
 3562572                ConvexContactCandidateLength(secondCoefficient) * 2
 3562573                    + ConvexContactCandidateLength(secondRadicand))) + 1;
 3562574        Span<ulong> radicands = stackalloc ulong[4 * words];
 3562575        Span<ulong> square = stackalloc ulong[words];
 3562576        Span<ulong> product = stackalloc ulong[words];
 3562577        radicands.Clear();
 3562578        WideArithmetic.MultiplyMagnitudes(rational, rational, square);
 3562579        CopyConvexContactCandidateSignedRadicand(
 3562580            square, rationalSign, radicands, words);
 3562581        WideArithmetic.MultiplyMagnitudes(
 3562582            firstCoefficient, firstCoefficient, square);
 3562583        WideArithmetic.MultiplyMagnitudes(square, firstRadicand, product);
 3562584        CopyConvexContactCandidateSignedRadicand(
 3562585            product, firstSign, radicands, words);
 3562586        WideArithmetic.MultiplyMagnitudes(
 3562587            secondCoefficient, secondCoefficient, square);
 3562588        WideArithmetic.MultiplyMagnitudes(square, secondRadicand, product);
 3562589        CopyConvexContactCandidateSignedRadicand(
 3562590            product, secondSign, radicands, words);
 3562591        return CompareRadicalPairs(
 3562592            radicands.Slice(0, words), radicands.Slice(words, words),
 3562593            radicands.Slice(2 * words, words), radicands.Slice(3 * words, words));
 594    }
 595
 596    private static int GetConvexContactCandidateRankRootBounds(ReadOnlySpan<ulong> value, Span<ulong> roots)
 597    {
 64432598        int shift = Math.Max(0, (WideArithmetic.GetMagnitudeBitLength(value) - 63) / 2);
 128864599        int word = 2 * shift / 64, bits = 2 * shift % 64;
 64432600        ulong prefix = value[word] >> bits;
 118567601        if (bits != 0) prefix |= value[word + 1] << (64 - bits);
 64432602        ReadOnlySpan<ulong> leading = stackalloc ulong[1] { prefix };
 64432603        WideArithmetic.GetMagnitudeSquareRootBounds(leading, out Signed192 lower, out _, out _);
 604        // An even shift preserves square-root scale. The 63/64-bit prefix
 605        // gives a 32-bit lower root; lower+1 also covers every discarded bit.
 128864606        roots[0] = lower.Low; roots[1] = 0;
 128864607        roots[2] = lower.Low + 1; roots[3] = 0;
 64432608        return shift;
 609    }
 610
 611    private static void CopyConvexContactCandidateSignedRadicand(
 612        ReadOnlySpan<ulong> value,
 613        int sign,
 614        Span<ulong> radicands,
 615        int words)
 616    {
 10686617        if (sign == 0)
 2978618            return;
 7708619        int offset = sign > 0 ? 0 : 2 * words;
 7708620        CopyPositiveRadicand(value,
 7708621            radicands.Slice(offset, words),
 7708622            radicands.Slice(offset + words, words));
 7708623    }
 624
 625    private static int ConvexContactCandidateLength(
 626        ReadOnlySpan<ulong> value) =>
 1560030627        WideArithmetic.GetActiveMagnitudeLength(value);
 628
 629    private static int ConvexContactCandidateProductWords(
 630        ReadOnlySpan<ulong> left,
 458842631        ReadOnlySpan<ulong> right) => Math.Max(1,
 458842632            ConvexContactCandidateLength(left)
 458842633                + ConvexContactCandidateLength(right));
 634}

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

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12
 13
 14
 15
 16/// <summary>Owns full-domain finite-surface versus convex-prism contact construction.</summary>
 17/// <content>Dispatches finite shapes and ranks exact prism support candidates.</content>
 18internal static partial class WideConvexPrismRelations
 19{
 20    internal static bool TryGetSphereContact(Vector3d center, Fixed64 radius, Vector3d prismOrigin,
 21        Fixed64 prismRotation, ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness,
 22        PrismPointAdmission admission, out FixedContactAnchors contact, out WideDirection3d axis, out ProjectionDepth de
 23    {
 1524        bool found = TryGetContact(center, FixedQuaternion.Identity, Vector3d.Up, Fixed64.Zero, radius,
 1525            prismOrigin, prismRotation, offsets, halfThickness, out contact, admission, out var selected, out overlaps);
 1526        depth = selected.ExactDepth;
 1527        Axis3 direction = selected.Negate ? Negate(selected.Axis) : selected.Axis;
 1528        axis = selected.HasValue ? new WideDirection3d(direction.X, direction.Y, direction.Z) : default;
 1529        return found;
 30    }
 31
 32    internal static bool VisitCapsuleContacts(Vector3d center, FixedQuaternion rotation, Fixed64 length, Fixed64 radius,
 33        Vector3d origin, Fixed64 yaw, ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness,
 34        PrismPointAdmission admission, IPrismCapsuleContactDomain domain, out bool overlaps)
 35    {
 36        // The ordinary finite owner remains the full-solid overlap oracle;
 37        // removing a support from response must never remove geometric overlap.
 24838        overlaps = TryGetContact(center, rotation, Vector3d.Up, length, radius, origin, yaw, offsets,
 24839            halfThickness, out _, null, out var selected, out _);
 32040        if (!overlaps) return false;
 17641        WideOrientedBox.GetRotatedLocalAxisNumerators(rotation, Vector3d.Up, out var x, out var y, out var z, out var de
 17642        var shapeAxis = new RigidAxis3(x, y, z, denominator);
 17643        Axis3 direction = selected.Negate ? Negate(selected.Axis) : selected.Axis;
 17644        int axialSign = GetAxisProjection(direction, shapeAxis).Sign;
 17645        FixedQuaternion prismRotation = FixedQuaternion.FromAxisAngle(Vector3d.Up, -yaw);
 17646        FixedPointAnchor unused = default;
 47        // The unfiltered minimum remains minimal when its exact support has a
 48        // retained interval. Only a rejected original axis requires another SAT.
 17649        if (!TryGetMatchedPrismAnchor(selected, direction, shapeAxis, center, rotation, Vector3d.Up, length,
 17650                default, axialSign, origin, yaw, prismRotation, offsets, halfThickness,
 17651                ref unused, out _, admission, materialize: false, capsuleDomain: domain))
 52        {
 16253            if (!TryGetContact(center, rotation, Vector3d.Up, length, radius, origin, yaw, offsets,
 16254                    halfThickness, out _, admission, out selected, out _, domain, false))
 15955                return !PrismContainsPoint(center, origin, yaw, offsets, halfThickness, out _)
 15956                    && TryGetPrismEdgeContact(shapeAxis, center, rotation, length, radius, origin, yaw,
 15957                        prismRotation, offsets, halfThickness, out _, domain);
 358            direction = selected.Negate ? Negate(selected.Axis) : selected.Axis;
 359            axialSign = GetAxisProjection(direction, shapeAxis).Sign;
 60        }
 1761        Vector3d normal = WideNormalization.GetNormalized(Signed576.ExtendValue(direction.X), Signed576.ExtendValue(dire
 1762        var wideNormal = new WideDirection3d(direction.X, direction.Y, direction.Z);
 1763        Span<Signed832> projected = stackalloc Signed832[6];
 1764        bool prepared = TryGetPrismSupportInterval(selected, direction, shapeAxis, center, length, axialSign, origin, ya
 1765            offsets, halfThickness, projected, out Signed832 pointD, out var solidClip);
 66        System.Diagnostics.Debug.Assert(prepared); // The selected axis already passed this exact finite admission.
 1767        ReadOnlySpan<RationalSegmentInterval> intervals = domain.GetSupportIntervals(wideNormal, projected, pointD, soli
 3468        Signed832 one = Signed832.ExtendValue(Signed192.Signed(1)), two = Signed832.ExtendValue(Signed192.Signed(2));
 1769        bool physical = IsProjectedPrismPointWithinCapsule(projected, pointD, one, two, center - origin, rotation, lengt
 70        System.Diagnostics.Debug.Assert(domain.IncludeRecovery || physical);
 3471        bool freeCore = axialSign == 0 && length > Fixed64.Zero, useExtremes = freeCore && physical;
 1772        FixedPointAnchor baseShape = WideGeometry.GetCenteredCapsuleSupportAnchor(center, rotation, Vector3d.Up, length,
 1773        Vector3d radialOffset = GetPrismCapsuleRadialOffset(direction, rotation, radius);
 1774        Fixed64 depth = GetRoundedDepth(selected.ExactDepth, out bool clamped);
 3475        bool found = false, hasPhysical = false;
 7276        for (int index = 0; index < intervals.Length; index++)
 77        {
 1978            RationalSegmentInterval retained = intervals[index];
 3879            Signed832 lowerN = retained.IsClipped ? retained.WideLowerNumerator : default, lowerD = retained.IsClipped ?
 3880            Signed832 upperN = retained.IsClipped ? retained.WideUpperNumerator : one, upperD = retained.IsClipped ? ret
 1981            bool sameEndpoint = useExtremes && RationalSegmentInterval.CompareParameters(lowerN, lowerD, upperN, upperD)
 5782            Signed832 primaryN = default, primaryD = one; bool primary = false;
 83            // Retain the preferred point for stable pressure placement and both
 84            // genuine extrema for angular leverage. Exact parameter deduplication
 85            // keeps collapsed or endpoint-selected families from adding duplicates.
 12886            for (int sample = 0; sample < (useExtremes ? 3 : 1); sample++)
 87            {
 88                Signed832 tN, tD;
 4589                if (sample == 0)
 90                {
 1991                    primary = TryGetPrismDomainParameter(direction, selected.ExactDepth, projected, pointD, yaw,
 1992                        retained, admission, out primaryN, out primaryD);
 1993                    if (!primary) continue;
 3694                    tN = primaryN; tD = primaryD;
 95                }
 96                else
 97                {
 5298                    tN = sample == 1 ? lowerN : upperN; tD = sample == 1 ? lowerD : upperD;
 2699                    if (sample == 2 && sameEndpoint
 26100                        || primary && RationalSegmentInterval.CompareParameters(tN, tD, primaryN, primaryD) == 0) contin
 101                    // Admission also supplies the consumer's exact incident fan;
 102                    // it must belong to this sample when the callback consumes it.
 20103                    if (!AdmitPrismSpherePoint(direction, selected.ExactDepth, projected, pointD, yaw, admission, tN, tD
 104                }
 31105                FixedPointAnchor shape = freeCore ? GetPrismCoreAnchor(baseShape, Vector3d.Up, length, normal, tN, tD) :
 31106                shape = new FixedPointAnchor(center, rotation, shape.LocalPoint, radialOffset, shape.ExactLocalTerm);
 31107                FixedPointAnchor target = MaterializePrismProjection(projected, pointD, tN, tD, origin, prismRotation);
 31108                var native = new CapsulePrismContactDepth(selected.ExactDepth);
 31109                domain.AddSupportContact(new(shape, target, normal, depth, clamped), native, wideNormal, selected.ExactD
 62110                found = true; hasPhysical |= physical;
 111            }
 112        }
 113        // Match the ordinary finite owner: a physical supporting face already
 114        // supplies the convex contact. Recovery-only/empty faces still need a real edge.
 115        // A core point inside the prism rules out every nonzero separating
 116        // closest-edge pair, independently of which surfaces admit response.
 17117        if (!hasPhysical && !PrismContainsPoint(center, origin, yaw, offsets, halfThickness, out _))
 1118            found |= TryGetPrismEdgeContact(shapeAxis, center, rotation, length, radius, origin, yaw,
 1119                prismRotation, offsets, halfThickness, out _, domain);
 17120        return found;
 121    }
 122
 123    internal static bool TryGetCenteredCapsuleContact(
 124        Vector3d center,
 125        FixedQuaternion rotation,
 126        Vector3d localAxisDirection,
 127        Fixed64 axisLength,
 128        Fixed64 radius,
 129        Vector3d prismOrigin,
 130        Fixed64 prismRotation,
 131        ReadOnlySpan<Vector2d> prismLocalOffsets,
 132        Fixed64 prismHalfThickness,
 133        out FixedContactAnchors contact) =>
 513134        TryGetContact(
 513135            center,
 513136            rotation,
 513137            localAxisDirection,
 513138            axisLength,
 513139            radius,
 513140            prismOrigin,
 513141            prismRotation,
 513142            prismLocalOffsets,
 513143            prismHalfThickness,
 513144            out contact);
 145
 146    internal static bool TryGetCenteredCylinderContact(
 147        Vector3d center,
 148        FixedQuaternion rotation,
 149        Vector3d localAxisDirection,
 150        Fixed64 axisLength,
 151        Fixed64 radius,
 152        Vector3d prismOrigin,
 153        Fixed64 prismRotation,
 154        ReadOnlySpan<Vector2d> prismLocalOffsets,
 155        Fixed64 prismHalfThickness,
 156        out FixedContactAnchors contact) =>
 498157        CurvedPrismContact.TryGetContact(
 498158            center,
 498159            rotation,
 498160            localAxisDirection,
 498161            axisLength,
 498162            radius,
 498163            prismOrigin,
 498164            prismRotation,
 498165            prismLocalOffsets,
 498166            prismHalfThickness,
 498167            false,
 498168            out contact);
 169
 170    internal static bool TryGetCenteredConeContact(
 171        Vector3d center,
 172        FixedQuaternion rotation,
 173        Vector3d localAxisDirection,
 174        Fixed64 height,
 175        Fixed64 radius,
 176        Vector3d prismOrigin,
 177        Fixed64 prismRotation,
 178        ReadOnlySpan<Vector2d> prismLocalOffsets,
 179        Fixed64 prismHalfThickness,
 180        out FixedContactAnchors contact) =>
 390181        CurvedPrismContact.TryGetContact(
 390182            center,
 390183            rotation,
 390184            localAxisDirection,
 390185            height,
 390186            radius,
 390187            prismOrigin,
 390188            prismRotation,
 390189            prismLocalOffsets,
 390190            prismHalfThickness,
 390191            true,
 390192            out contact);
 193
 194    private static bool TryGetContact(
 195        Vector3d center,
 196        FixedQuaternion shapeRotation,
 197        Vector3d localAxisDirection,
 198        Fixed64 axisLength,
 199        Fixed64 radius,
 200        Vector3d prismOrigin,
 201        Fixed64 prismRotation,
 202        ReadOnlySpan<Vector2d> prismLocalOffsets,
 203        Fixed64 prismHalfThickness,
 204        out FixedContactAnchors contact) =>
 513205        TryGetContact(center, shapeRotation, localAxisDirection, axisLength, radius, prismOrigin,
 513206            prismRotation, prismLocalOffsets, prismHalfThickness, out contact, null, out _, out _);
 207
 208    private static bool TryGetContact(Vector3d center, FixedQuaternion shapeRotation, Vector3d localAxisDirection,
 209        Fixed64 axisLength, Fixed64 radius, Vector3d prismOrigin, Fixed64 prismRotation,
 210        ReadOnlySpan<Vector2d> prismLocalOffsets, Fixed64 prismHalfThickness, out FixedContactAnchors contact,
 211        PrismPointAdmission? admission, out PenetrationCandidate best, out bool overlaps,
 212        IPrismCapsuleContactDomain? capsuleDomain = null, bool materialize = true)
 213    {
 938214        best = default;
 938215        overlaps = false;
 938216        WideOrientedBox.GetRotatedLocalAxisNumerators(
 938217            shapeRotation,
 938218            localAxisDirection,
 938219            out Signed192 shapeAxisX,
 938220            out Signed192 shapeAxisY,
 938221            out Signed192 shapeAxisZ,
 938222            out Signed192 shapeRotationDenominator);
 938223        var rigidShapeAxis = new RigidAxis3(
 938224            shapeAxisX,
 938225            shapeAxisY,
 938226            shapeAxisZ,
 938227            shapeRotationDenominator);
 938228        Axis3 shapeAxis = rigidShapeAxis.ToWide();
 938229        Axis3 up = FromDirection(Vector3d.Up);
 938230        FixedQuaternion prismOrientation =
 938231            FixedQuaternion.FromAxisAngle(
 938232                Vector3d.Up,
 938233                -prismRotation);
 938234        if (!TryKeepAxis(
 938235                up,
 938236                rigidShapeAxis,
 938237                center,
 938238                axisLength,
 938239                radius,
 938240                prismOrigin,
 938241                prismRotation,
 938242                prismLocalOffsets,
 938243                prismHalfThickness,
 938244                ref best, admission, capsuleDomain, shapeRotation)
 938245            || !TryKeepAxis(
 938246                shapeAxis,
 938247                rigidShapeAxis,
 938248                center,
 938249                axisLength,
 938250                radius,
 938251                prismOrigin,
 938252                prismRotation,
 938253                prismLocalOffsets,
 938254                prismHalfThickness,
 938255                ref best, admission, capsuleDomain, shapeRotation)
 938256            || !TryKeepAxis(
 938257                Cross(shapeAxis, up),
 938258                rigidShapeAxis,
 938259                center,
 938260                axisLength,
 938261                radius,
 938262                prismOrigin,
 938263                prismRotation,
 938264                prismLocalOffsets,
 938265                prismHalfThickness,
 938266                ref best, admission, capsuleDomain, shapeRotation))
 267        {
 95268            contact = default;
 95269            return false;
 270        }
 271
 6880272        for (int index = 0; index < prismLocalOffsets.Length; index++)
 273        {
 2815274            Vector2d start = prismLocalOffsets[index];
 2815275            Vector2d end = prismLocalOffsets[
 2815276                index + 1 == prismLocalOffsets.Length ? 0 : index + 1];
 2815277            Axis3 edge = GetPlanarEdge(
 2815278                prismRotation,
 2815279                start,
 2815280                end);
 2815281            Axis3 face = new(
 2815282                edge.Z,
 2815283                default,
 2815284                WideArithmetic.Negate(edge.X));
 2815285            Axis3 planarVertexAxis = GetPlanarVertexAxis(
 2815286                center,
 2815287                prismOrigin,
 2815288                prismRotation,
 2815289                start);
 2815290            if (!TryKeepAxis(
 2815291                face,
 2815292                rigidShapeAxis,
 2815293                center,
 2815294                axisLength,
 2815295                radius,
 2815296                prismOrigin,
 2815297                prismRotation,
 2815298                prismLocalOffsets,
 2815299                prismHalfThickness,
 2815300                ref best, admission, capsuleDomain, shapeRotation)
 2815301                || !TryKeepAxis(
 2815302                    Cross(shapeAxis, edge),
 2815303                    rigidShapeAxis,
 2815304                    center,
 2815305                    axisLength,
 2815306                    radius,
 2815307                    prismOrigin,
 2815308                    prismRotation,
 2815309                    prismLocalOffsets,
 2815310                    prismHalfThickness,
 2815311                    ref best, admission, capsuleDomain, shapeRotation)
 2815312                || !TryKeepAxis(
 2815313                    planarVertexAxis,
 2815314                    rigidShapeAxis,
 2815315                    center,
 2815316                    axisLength,
 2815317                    radius,
 2815318                    prismOrigin,
 2815319                    prismRotation,
 2815320                    prismLocalOffsets,
 2815321                    prismHalfThickness,
 2815322                    ref best, admission, capsuleDomain, shapeRotation))
 323            {
 199324                contact = default;
 199325                return false;
 326            }
 327
 2616328            for (int verticalSign = -1;
 7810329                verticalSign <= 1;
 5194330                verticalSign += 2)
 331            {
 5213332                for (int endpointSign = -1;
 15610333                    endpointSign <= 1;
 10397334                    endpointSign += 2)
 335                {
 10416336                    Axis3 endpointAxis = GetEndpointToPrismVertexAxis(
 10416337                        center,
 10416338                        rigidShapeAxis,
 10416339                        axisLength,
 10416340                        endpointSign,
 10416341                        prismOrigin,
 10416342                        prismRotation,
 10416343                        start,
 10416344                        prismHalfThickness,
 10416345                        verticalSign);
 10416346                    if (!TryKeepAxis(
 10416347                            endpointAxis,
 10416348                            rigidShapeAxis,
 10416349                            center,
 10416350                            axisLength,
 10416351                            radius,
 10416352                            prismOrigin,
 10416353                            prismRotation,
 10416354                            prismLocalOffsets,
 10416355                            prismHalfThickness,
 10416356                            ref best, admission, capsuleDomain, shapeRotation))
 357                    {
 19358                        contact = default;
 19359                        return false;
 360                    }
 361                }
 362            }
 363        }
 364
 365        // A point footprint and parallel vertical core have no edge/cross
 366        // normal. Append a perpendicular bound without replacing earlier ties.
 625367        if (WideConvex2dRelations.GetPlanarSweepWinding(prismLocalOffsets) == 0
 625368            && !TryKeepAxis(FromDirection(Vector3d.Right), rigidShapeAxis, center, axisLength, radius,
 625369                prismOrigin, prismRotation, prismLocalOffsets, prismHalfThickness, ref best, admission, capsuleDomain, s
 370        {
 1371            contact = default;
 1372            return false;
 373        }
 374
 624375        if (admission != null && capsuleDomain == null && !KeepSphereEdgeAxes(center, radius, prismOrigin, prismRotation
 624376                prismHalfThickness, rigidShapeAxis, admission, ref best))
 2377        { contact = default; return false; }
 623378        overlaps = true;
 943379        if (!best.HasValue) { contact = default; return false; }
 469380        if (!materialize) { contact = default; return true; }
 460381        return TryMaterializeContact(
 460382            best,
 460383            rigidShapeAxis,
 460384            center,
 460385            shapeRotation,
 460386            localAxisDirection,
 460387            axisLength,
 460388            radius,
 460389            prismOrigin,
 460390            prismRotation,
 460391            prismOrientation,
 460392            prismLocalOffsets,
 460393            prismHalfThickness,
 460394            out contact);
 395    }
 396
 397    private static bool TryKeepAxis(
 398        Axis3 axis,
 399        RigidAxis3 shapeAxis,
 400        Vector3d center,
 401        Fixed64 axisLength,
 402        Fixed64 radius,
 403        Vector3d prismOrigin,
 404        Fixed64 prismRotation,
 405        ReadOnlySpan<Vector2d> prismLocalOffsets,
 406        Fixed64 prismHalfThickness,
 407        ref PenetrationCandidate best, PrismPointAdmission? admission, IPrismCapsuleContactDomain? capsuleDomain,
 408        in FixedQuaternion shapeRotation)
 409    {
 21449410        if (axis.IsZero)
 884411            return true;
 412
 20565413        GetPrismProjection(
 20565414            center,
 20565415            prismOrigin,
 20565416            prismRotation,
 20565417            prismLocalOffsets,
 20565418            prismHalfThickness,
 20565419            axis,
 20565420            out Signed576 prismMinimum,
 20565421            out Signed576 prismMaximum,
 20565422            out Vector3d minimumPrismOffset,
 20565423            out Vector3d maximumPrismOffset);
 20565424        GetCapsuleProjectionDepths(
 20565425            axis,
 20565426            shapeAxis,
 20565427            axisLength,
 20565428            radius,
 20565429            prismMinimum,
 20565430            prismMaximum,
 20565431            out ProjectionDepth positiveDepth,
 20565432            out ProjectionDepth negativeDepth);
 20565433        if (!IsProjectionNonNegative(positiveDepth)
 20565434            || !IsProjectionNonNegative(negativeDepth))
 435        {
 315436            return false;
 437        }
 438
 20250439        if (admission != null)
 440        {
 441            // Solid admission above still checks both sides. Exposure ranks both
 442            // oriented exits independently, including a farther genuine boundary.
 5320443            KeepSphereProjection(axis, false, minimumPrismOffset, positiveDepth, shapeAxis, center,
 5320444                prismOrigin, prismRotation, prismLocalOffsets, prismHalfThickness, admission, ref best, axisLength, caps
 5320445            KeepSphereProjection(axis, true, maximumPrismOffset, negativeDepth, shapeAxis, center,
 5320446                prismOrigin, prismRotation, prismLocalOffsets, prismHalfThickness, admission, ref best, axisLength, caps
 5320447            return true;
 448        }
 449
 450        bool usePositiveNormal;
 451        ProjectionDepth selectedDepth;
 14930452        if (prismMinimum.Sign >= 0)
 453        {
 5744454            usePositiveNormal = true;
 5744455            selectedDepth = positiveDepth;
 456        }
 9186457        else if (prismMaximum.Sign <= 0)
 458        {
 1380459            usePositiveNormal = false;
 1380460            selectedDepth = negativeDepth;
 461        }
 7806462        else if (CompareProjectionDepths(
 7806463                     positiveDepth,
 7806464                     negativeDepth) >= 0)
 465        {
 3431466            usePositiveNormal = false;
 3431467            selectedDepth = negativeDepth;
 468        }
 469        else
 470        {
 4375471            usePositiveNormal = true;
 4375472            selectedDepth = positiveDepth;
 473        }
 474
 14930475        if (best.HasValue
 14930476            && CompareProjectionDepths(
 14930477                selectedDepth,
 14930478                best.ExactDepth) >= 0)
 479        {
 13011480            return true;
 481        }
 482
 483        // +axis pairs the capsule maximum with the prism minimum; -axis
 484        // pairs its minimum with the prism maximum, including containment.
 1919485        Vector3d prismOffset = usePositiveNormal
 1919486            ? minimumPrismOffset
 1919487            : maximumPrismOffset;
 1919488        best = new PenetrationCandidate(
 1919489            axis,
 1919490            !usePositiveNormal,
 1919491            prismOffset,
 1919492            selectedDepth);
 1919493        return true;
 494    }
 495
 496    private static void GetPrismProjection(
 497        Vector3d sourceOrigin,
 498        Vector3d prismOrigin,
 499        Fixed64 prismRotation,
 500        ReadOnlySpan<Vector2d> offsets,
 501        Fixed64 halfThickness,
 502        Axis3 axis,
 503        out Signed576 minimum,
 504        out Signed576 maximum,
 505        out Vector3d minimumOffset,
 506        out Vector3d maximumOffset)
 507    {
 20565508        Signed576 originProjection = ScaleProjection(
 20565509            GetDifferenceProjection(
 20565510                prismOrigin,
 20565511                sourceOrigin,
 20565512                axis));
 20565513        Fixed64 minimumY = axis.Y.Sign >= 0
 20565514            ? -halfThickness
 20565515            : halfThickness;
 20565516        Fixed64 maximumY = -minimumY;
 20565517        int minimumIndex = 0;
 20565518        int maximumIndex = 0;
 20565519        Signed576 minimumPlanar = GetRotatedPlanarProjection(
 20565520            offsets[0],
 20565521            prismRotation,
 20565522            axis);
 20565523        Signed576 maximumPlanar = minimumPlanar;
 164308524        for (int index = 1; index < offsets.Length; index++)
 525        {
 61589526            Signed576 projection = GetRotatedPlanarProjection(
 61589527                offsets[index],
 61589528                prismRotation,
 61589529                axis);
 61589530            int minimumComparison = Compare(projection, minimumPlanar);
 61589531            if (minimumComparison < 0)
 532            {
 18525533                minimumPlanar = projection;
 18525534                minimumIndex = index;
 535            }
 536
 61589537            int maximumComparison = Compare(projection, maximumPlanar);
 61589538            if (maximumComparison > 0)
 539            {
 15840540                maximumPlanar = projection;
 15840541                maximumIndex = index;
 542            }
 543        }
 544
 20565545        Signed576 verticalRadius = ScaleProjection(
 20565546            WideArithmetic.MultiplySigned320(
 20565547                axis.Y.Sign < 0 ? WideArithmetic.Negate(axis.Y) : axis.Y,
 20565548                Signed320.ExtendValue(Signed192.Raw(halfThickness))));
 20565549        minimum = WideArithmetic.SubtractSigned576(
 20565550            WideArithmetic.AddSigned576(originProjection, minimumPlanar),
 20565551            verticalRadius);
 20565552        maximum = WideArithmetic.AddSigned576(
 20565553            WideArithmetic.AddSigned576(originProjection, maximumPlanar),
 20565554            verticalRadius);
 20565555        Vector2d minimumPlanarOffset = offsets[minimumIndex];
 20565556        Vector2d maximumPlanarOffset = offsets[maximumIndex];
 20565557        minimumOffset = new Vector3d(
 20565558            minimumPlanarOffset.X,
 20565559            minimumY,
 20565560            minimumPlanarOffset.Y);
 20565561        maximumOffset = new Vector3d(
 20565562            maximumPlanarOffset.X,
 20565563            maximumY,
 20565564            maximumPlanarOffset.Y);
 20565565    }
 566
 567    private static Axis3 GetEndpointToPrismVertexAxis(
 568        Vector3d sourceCenter,
 569        RigidAxis3 sourceAxis,
 570        Fixed64 sourceLength,
 571        int endpointSign,
 572        Vector3d prismOrigin,
 573        Fixed64 prismRotation,
 574        Vector2d prismOffset,
 575        Fixed64 prismHalfThickness,
 576        int verticalSign)
 577    {
 10416578        WideConvex2dRelations.GetRelativePointNumerators(
 10416579            new Vector2d(prismOrigin.X, prismOrigin.Z),
 10416580            prismRotation,
 10416581            prismOffset,
 10416582            new Vector2d(sourceCenter.X, sourceCenter.Z),
 10416583            out Signed192 relativeX,
 10416584            out Signed192 relativeZ);
 10416585        Signed192 relativeY = Signed192.NarrowProven(
 10416586            WideArithmetic.MultiplySigned192(
 10416587                WideArithmetic.AddSigned192(
 10416588                    WideArithmetic.SubtractSigned192(
 10416589                        Signed192.Raw(prismOrigin.Y),
 10416590                        Signed192.Raw(sourceCenter.Y)),
 10416591                    Signed192.Raw(verticalSign * prismHalfThickness)),
 10416592                Scale));
 10416593        return new Axis3(
 10416594            GetScaledEndpointDifference(
 10416595                relativeX,
 10416596                sourceAxis.X,
 10416597                sourceAxis.RotationDenominator,
 10416598                sourceLength,
 10416599                endpointSign),
 10416600            GetScaledEndpointDifference(
 10416601                relativeY,
 10416602                sourceAxis.Y,
 10416603                sourceAxis.RotationDenominator,
 10416604                sourceLength,
 10416605                endpointSign),
 10416606            GetScaledEndpointDifference(
 10416607                relativeZ,
 10416608                sourceAxis.Z,
 10416609                sourceAxis.RotationDenominator,
 10416610                sourceLength,
 10416611                endpointSign));
 612    }
 613
 614    private static Signed320 GetScaledEndpointDifference(
 615        Signed192 relativeNumerator,
 616        Signed192 sourceAxisNumerator,
 617        Signed192 sourceRotationDenominator,
 618        Fixed64 sourceLength,
 619        int endpointSign)
 620    {
 31248621        Signed320 scaledRelative = WideArithmetic.MultiplySigned192(
 31248622            relativeNumerator,
 31248623            sourceRotationDenominator);
 31248624        return
 31248625        WideArithmetic.SubtractSigned320(
 31248626            WideArithmetic.AddSigned320(
 31248627                scaledRelative,
 31248628                scaledRelative),
 31248629            WideArithmetic.MultiplySigned192(
 31248630                sourceAxisNumerator,
 31248631                Signed192.Signed(
 31248632                    endpointSign * sourceLength.m_rawValue)));
 633    }
 634
 635    private static Axis3 GetPlanarEdge(
 636        Fixed64 rotation,
 637        Vector2d start,
 638        Vector2d end)
 639    {
 5833640        WideConvex2dRelations.GetRelativePointNumerators(
 5833641            Vector2d.Zero,
 5833642            rotation,
 5833643            end,
 5833644            Vector2d.Zero,
 5833645            out Signed192 endX,
 5833646            out Signed192 endY);
 5833647        WideConvex2dRelations.GetRelativePointNumerators(
 5833648            Vector2d.Zero,
 5833649            rotation,
 5833650            start,
 5833651            Vector2d.Zero,
 5833652            out Signed192 startX,
 5833653            out Signed192 startY);
 5833654        return new Axis3(
 5833655            Signed320.ExtendValue(
 5833656                WideArithmetic.SubtractSigned192(endX, startX)),
 5833657            default,
 5833658            Signed320.ExtendValue(
 5833659                WideArithmetic.SubtractSigned192(endY, startY)));
 660    }
 661
 662    private static Axis3 GetPlanarVertexAxis(
 663        Vector3d sourceCenter,
 664        Vector3d prismOrigin,
 665        Fixed64 prismRotation,
 666        Vector2d prismOffset)
 667    {
 2815668        WideConvex2dRelations.GetRelativePointNumerators(
 2815669            new Vector2d(prismOrigin.X, prismOrigin.Z),
 2815670            prismRotation,
 2815671            prismOffset,
 2815672            new Vector2d(sourceCenter.X, sourceCenter.Z),
 2815673            out Signed192 x,
 2815674            out Signed192 z);
 2815675        return new Axis3(
 2815676            Signed320.ExtendValue(x),
 2815677            default,
 2815678            Signed320.ExtendValue(z));
 679    }
 680
 681    private static Axis3 Cross(Axis3 left, Axis3 right) =>
 17108682        new(
 17108683            SubtractProducts(left.Y, right.Z, left.Z, right.Y),
 17108684            SubtractProducts(left.Z, right.X, left.X, right.Z),
 17108685            SubtractProducts(left.X, right.Y, left.Y, right.X));
 686
 687    private static Signed320 SubtractProducts(
 688        Signed320 firstLeft,
 689        Signed320 firstRight,
 690        Signed320 secondLeft,
 691        Signed320 secondRight)
 692    {
 51324693        Signed576 value = WideArithmetic.SubtractSigned576(
 51324694            WideArithmetic.MultiplySigned320(firstLeft, firstRight),
 51324695            WideArithmetic.MultiplySigned320(secondLeft, secondRight));
 51324696        _ = Signed320.TryNarrowSigned(value, out Signed320 result);
 51324697        return result;
 698    }
 699
 700    private static Signed576 GetDifferenceProjection(
 701        Vector3d end,
 702        Vector3d start,
 703        Axis3 axis) =>
 21008704        WideArithmetic.AddSigned576(
 21008705            WideArithmetic.AddSigned576(
 21008706                WideArithmetic.MultiplySigned320(
 21008707                    Signed320.ExtendValue(
 21008708                        WideArithmetic.SubtractSigned192(
 21008709                            Signed192.Raw(end.X),
 21008710                            Signed192.Raw(start.X))),
 21008711                    axis.X),
 21008712                WideArithmetic.MultiplySigned320(
 21008713                    Signed320.ExtendValue(
 21008714                        WideArithmetic.SubtractSigned192(
 21008715                            Signed192.Raw(end.Y),
 21008716                            Signed192.Raw(start.Y))),
 21008717                    axis.Y)),
 21008718            WideArithmetic.MultiplySigned320(
 21008719                Signed320.ExtendValue(
 21008720                    WideArithmetic.SubtractSigned192(
 21008721                        Signed192.Raw(end.Z),
 21008722                        Signed192.Raw(start.Z))),
 21008723                axis.Z));
 724
 725    private static Signed576 GetRotatedPlanarProjection(
 726        Vector2d offset,
 727        Fixed64 rotation,
 728        Axis3 axis)
 729    {
 82154730        WideConvex2dRelations.GetRelativePointNumerators(
 82154731            Vector2d.Zero,
 82154732            rotation,
 82154733            offset,
 82154734            Vector2d.Zero,
 82154735            out Signed192 x,
 82154736            out Signed192 y);
 82154737        return WideArithmetic.AddSigned576(
 82154738            WideArithmetic.MultiplySigned320(
 82154739                Signed320.ExtendValue(x),
 82154740                axis.X),
 82154741            WideArithmetic.MultiplySigned320(
 82154742                Signed320.ExtendValue(y),
 82154743                axis.Z));
 744    }
 745
 746    private static Signed576 ScaleProjection(Signed576 projection) =>
 41130747        WideArithmetic.MultiplySigned576(projection, Scale);
 748}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CurvedAdmission.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CurvedAdmission.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Owns complete closed cylinder/cone admission against the finite prism boundary.</content>
 13internal static partial class WideConvexPrismRelations
 14{
 15    internal static bool IntersectsCurvedPrism(bool cone, Vector3d center, FixedQuaternion rotation,
 16        Vector3d localAxis, Fixed64 length, Fixed64 radius, Vector3d origin, Fixed64 yaw,
 17        ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness)
 18    {
 82919        WideOrientedBox.GetRotatedLocalAxisNumerators(rotation, localAxis,
 82920            out Signed192 x, out Signed192 y, out Signed192 z, out Signed192 denominator);
 82921        return IntersectsCurvedPrism(cone ? FiniteShapeKind.Cone : FiniteShapeKind.Cylinder,
 82922            center, new RigidAxis3(x, y, z, denominator), length, radius, origin, yaw, offsets, halfThickness);
 23    }
 24
 25    private static bool IntersectsCurvedPrism(FiniteShapeKind kind, Vector3d center,
 26        RigidAxis3 axis, Fixed64 length, Fixed64 radius, Vector3d origin, Fixed64 yaw,
 27        ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness)
 28    {
 82929        if (PrismContainsPoint(center, origin, yaw, offsets, halfThickness, out bool footprintContainsCenter))
 5130            return true;
 31
 32        // The projected contact already proved world-Up overlap. An upright
 33        // finite core therefore meets the prism when its footprint is inside.
 77834        if (footprintContainsCenter && axis.X.IsZero && axis.Z.IsZero)
 835            return true;
 36
 77037        var geometry = new CurvedPrismAdmission(kind, axis, center, length, radius,
 77038            origin, yaw, offsets, halfThickness);
 39        // Every certified section point is already a positive intersection
 40        // certificate. Try these before constructing any edge quadratics.
 154041        Axis3 bottom = geometry.Vertex(0, -1), top = geometry.Vertex(0, 1);
 77042        Axis3 up = FromDirection(Vector3d.Up);
 77043        if (geometry.IntersectsFace(up, bottom) || geometry.IntersectsFace(up, top))
 1844            return true;
 745045        for (int index = 0; index < offsets.Length; index++)
 46        {
 301847            int next = index + 1 == offsets.Length ? 0 : index + 1;
 301848            Axis3 edge = GetPlanarEdge(yaw, offsets[index], offsets[next]);
 301849            if (!edge.IsZero && geometry.IntersectsFace(
 301850                    new Axis3(edge.Z, default, WideArithmetic.Negate(edge.X)), geometry.Vertex(index, -1)))
 4551                return true;
 52        }
 53
 54        // A convex plane section that meets a face without meeting its boundary
 55        // lies wholly in that face. Thus one intrinsic point per nonempty
 56        // section, together with all real prism edges, is complete. Center
 57        // containment covers a primitive wholly enclosed by the prism.
 674658        for (int index = 0; index < offsets.Length; index++)
 59        {
 276560            int next = index + 1 == offsets.Length ? 0 : index + 1;
 553061            bottom = geometry.Vertex(index, -1); top = geometry.Vertex(index, 1);
 276562            if (geometry.IntersectsEdge(bottom, top)
 276563                || geometry.IntersectsEdge(bottom, geometry.Vertex(next, -1))
 276564                || geometry.IntersectsEdge(top, geometry.Vertex(next, 1)))
 9965                return true;
 66        }
 60867        return false;
 68    }
 69
 70    private static bool PrismContainsPoint(Vector3d point, Vector3d origin, Fixed64 yaw,
 71        ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness, out bool footprintContainsPoint)
 72    {
 99373        footprintContainsPoint = WideConvex2dRelations.ContainsPoint(new Vector2d(point.X, point.Z),
 99374            new Vector2d(origin.X, origin.Z), yaw, offsets);
 99375        Signed192 height = WideArithmetic.Difference(point.Y, origin.Y);
 99376        return footprintContainsPoint
 99377            && WideArithmetic.AddSigned192(height, Signed192.Raw(halfThickness)).Sign >= 0
 99378            && WideArithmetic.SubtractSigned192(height, Signed192.Raw(halfThickness)).Sign <= 0;
 79    }
 80
 81    private readonly ref struct CurvedPrismAdmission
 82    {
 83        // Native yaw numerators V use a Q denominator. P=2*D*V,
 84        // H=Lraw*A and R=2*D*Q*Rraw therefore share one exact coordinate
 85        // system without materializing world points or a rounded axis frame.
 86        // D<2^65, |A|<2^98, |P|<2^164: axial values fit 320 bits.
 87        // For side normals |n|<2^98, J=q*n²-(n.A)² fits 396 bits;
 88        // cap-foot numerators fit 562 bits and containment/disk products
 89        // fit 832 bits. Every narrowing below follows these bounds.
 90        private readonly FiniteShapeKind _kind;
 91        private readonly RigidAxis3 _axis;
 92        private readonly Axis3 _direction;
 93        private readonly Vector3d _center, _origin;
 94        private readonly Fixed64 _yaw, _halfThickness;
 95        private readonly ReadOnlySpan<Vector2d> _offsets;
 96        private readonly Signed192 _length;
 97        private readonly Signed320 _squared, _cap;
 98        private readonly Signed576 _radiusSquared;
 99
 100        internal CurvedPrismAdmission(FiniteShapeKind kind, RigidAxis3 axis, Vector3d center,
 101            Fixed64 length, Fixed64 radius, Vector3d origin, Fixed64 yaw,
 102            ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness)
 103        {
 2310104            _kind = kind; _axis = axis; _direction = axis.ToWide();
 2310105            _center = center; _origin = origin; _yaw = yaw;
 2310106            _offsets = offsets; _halfThickness = halfThickness; _length = Signed192.Raw(length);
 770107            _squared = GetAxisSquared(axis);
 770108            _cap = Signed320.NarrowValue(WideArithmetic.MultiplySigned320(
 770109                _squared, Signed320.ExtendValue(_length)));
 770110            Signed320 radial = WideArithmetic.MultiplySigned192(axis.RotationDenominator, Signed192.Raw(radius));
 770111            radial = Signed320.NarrowValue(WideArithmetic.MultiplySigned320(radial, Signed320.ExtendValue(Scale)));
 770112            radial = WideArithmetic.AddSigned320(radial, radial);
 770113            _radiusSquared = WideArithmetic.MultiplySigned320(radial, radial);
 770114        }
 115
 116        internal Axis3 Vertex(int index, int verticalSign)
 117        {
 26955118            WideConvex2dRelations.GetRelativePointNumerators(
 26955119                new Vector2d(_origin.X, _origin.Z), _yaw, _offsets[index],
 26955120                new Vector2d(_center.X, _center.Z), out Signed192 x, out Signed192 z);
 26955121            Signed192 y = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 26955122                WideArithmetic.AddSigned192(WideArithmetic.SubtractSigned192(Signed192.Raw(_origin.Y),
 26955123                    Signed192.Raw(_center.Y)), Signed192.Raw(verticalSign * _halfThickness)), Scale));
 26955124            return new Axis3(ScaleCoordinate(x), ScaleCoordinate(y), ScaleCoordinate(z));
 125        }
 126
 127        private Signed320 ScaleCoordinate(Signed192 value)
 128        {
 80865129            Signed320 result = WideArithmetic.MultiplySigned192(value, _axis.RotationDenominator);
 80865130            return WideArithmetic.AddSigned320(result, result);
 131        }
 132
 133        internal bool Contains(Signed576 x, Signed576 y, Signed576 z, Signed576 denominator)
 134        {
 5176135            Axis3 first = Vertex(0, -1), top = Vertex(0, 1);
 2588136            if (Relative(y, first.Y, denominator).Sign < 0 || Relative(y, top.Y, denominator).Sign > 0)
 1603137                return false;
 3940138            Signed320 minimumX = first.X, maximumX = first.X, minimumZ = first.Z, maximumZ = first.Z;
 985139            int side = 0;
 6606140            for (int index = 0; index < _offsets.Length; index++)
 141            {
 3226142                Axis3 start = Vertex(index, -1);
 3226143                Axis3 end = Vertex(index + 1 == _offsets.Length ? 0 : index + 1, -1);
 3226144                Signed320 edgeX = WideArithmetic.SubtractSigned320(end.X, start.X);
 3226145                Signed320 edgeZ = WideArithmetic.SubtractSigned320(end.Z, start.Z);
 3226146                Signed832 cross = WideArithmetic.SubtractSigned832(
 3226147                    WideArithmetic.MultiplySigned576ToSigned832(Relative(z, start.Z, denominator), edgeX),
 3226148                    WideArithmetic.MultiplySigned576ToSigned832(Relative(x, start.X, denominator), edgeZ));
 3226149                if (cross.Sign != 0)
 150                {
 3539151                    if (side != 0 && side != cross.Sign) return false;
 1723152                    side = cross.Sign;
 153                }
 2334154                if (WideArithmetic.SubtractSigned320(start.X, minimumX).Sign < 0) minimumX = start.X;
 2907155                if (WideArithmetic.SubtractSigned320(start.X, maximumX).Sign > 0) maximumX = start.X;
 2381156                if (WideArithmetic.SubtractSigned320(start.Z, minimumZ).Sign < 0) minimumZ = start.Z;
 2542157                if (WideArithmetic.SubtractSigned320(start.Z, maximumZ).Sign > 0) maximumZ = start.Z;
 158            }
 159            // Collinear and point footprints have no interior halfspaces;
 160            // their finite coordinate ranges must still contain the point.
 77161            return side != 0 || (Relative(x, minimumX, denominator).Sign >= 0
 77162                && Relative(x, maximumX, denominator).Sign <= 0
 77163                && Relative(z, minimumZ, denominator).Sign >= 0
 77164                && Relative(z, maximumZ, denominator).Sign <= 0);
 165        }
 166
 167        internal bool IntersectsFace(Axis3 normal, Axis3 point)
 168        {
 4363169            Signed320 c = Signed320.NarrowValue(CurvedDot(normal, point));
 4363170            Signed320 b = Signed320.NarrowValue(CurvedDot(normal, _direction));
 4363171            if (b.IsZero)
 172            {
 173                // Center containment was already rejected; a coplanar section
 174                // can enter this face only through its real boundary edges.
 1600175                if (c.IsZero) return false;
 176            }
 177            else
 178            {
 2770179                Signed320 absoluteB = b.Sign < 0 ? WideArithmetic.Negate(b) : b;
 2770180                Signed320 absoluteC = c.Sign < 0 ? WideArithmetic.Negate(c) : c;
 2770181                if (WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(
 2770182                        absoluteB, Signed320.ExtendValue(_length)), Signed576.ExtendValue(absoluteC)).Sign >= 0)
 183                {
 695184                    int sign = b.Sign;
 695185                    Signed320 numerator = sign < 0 ? WideArithmetic.Negate(c) : c;
 695186                    return Contains(WideArithmetic.MultiplySigned320(_direction.X, numerator),
 695187                        WideArithmetic.MultiplySigned320(_direction.Y, numerator),
 695188                        WideArithmetic.MultiplySigned320(_direction.Z, numerator), Signed576.ExtendValue(absoluteB));
 189                }
 190            }
 191
 3661192            Signed320 normalSquared = Signed320.NarrowValue(GetAxisSquared(normal));
 3661193            Signed576 j = WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(_squared, normalSquared),
 3661194                WideArithmetic.MultiplySigned320(b, b));
 3994195            if (j.IsZero) return false;
 3328196            Axis3 w = new(CurvedDifferenceProduct(_squared, normal.X, b, _direction.X),
 3328197                CurvedDifferenceProduct(_squared, normal.Y, b, _direction.Y),
 3328198                CurvedDifferenceProduct(_squared, normal.Z, b, _direction.Z));
 199            // The plane residual divided by the cylinder's constant radius or
 200            // cone's affine radius is monotone away from an axis crossing.
 201            // Without a finite axis crossing, a nonempty section therefore
 202            // meets a cap disk (only the base disk for a cone).
 11184203            for (int capSign = -1; capSign <= (_kind == FiniteShapeKind.Cone ? -1 : 1); capSign += 2)
 204            {
 4157205                Signed192 length = capSign < 0 ? WideArithmetic.Negate(_length) : _length;
 4157206                Signed320 delta = WideArithmetic.SubtractSigned320(c,
 4157207                    Signed320.NarrowValue(WideArithmetic.MultiplySigned320(b, Signed320.ExtendValue(length))));
 4157208                Signed576 deltaSquared = WideArithmetic.MultiplySigned320(delta, delta);
 4157209                if (WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned576ToSigned832(deltaSquared, _squared)
 4157210                        WideArithmetic.MultiplySigned576ToSigned832(_radiusSquared, j)).Sign > 0)
 211                    continue;
 1893212                return Contains(FootCoordinate(_direction.X, length, j, delta, w.X),
 1893213                    FootCoordinate(_direction.Y, length, j, delta, w.Y),
 1893214                    FootCoordinate(_direction.Z, length, j, delta, w.Z), j);
 215            }
 1435216            return false;
 217        }
 218
 219        internal bool IntersectsEdge(Axis3 start, Axis3 end)
 220        {
 8186221            Axis3 edge = new(WideArithmetic.SubtractSigned320(end.X, start.X),
 8186222                WideArithmetic.SubtractSigned320(end.Y, start.Y), WideArithmetic.SubtractSigned320(end.Z, start.Z));
 8186223            Signed320 axialStart = Signed320.NarrowValue(CurvedDot(_direction, start));
 8186224            Signed320 axialDelta = Signed320.NarrowValue(CurvedDot(_direction, edge));
 8186225            if (!CapsuleFiniteSectionFrame.TryGetCenteredAxisInterval(axialStart, axialDelta, _cap,
 8186226                    out Signed320 ln, out Signed320 ld, out Signed320 un, out Signed320 ud))
 3863227                return false;
 8646228            Axis3 radialStart = Cross(_direction, start), radialEdge = Cross(_direction, edge);
 8646229            Signed576 a = CurvedDot(radialEdge, radialEdge), b = CurvedDot(radialStart, radialEdge),
 4323230                c = CurvedDot(radialStart, radialStart);
 4323231            if (_kind == FiniteShapeKind.Cylinder)
 232            {
 1691233                c = WideArithmetic.SubtractSigned576(c, Signed576.NarrowValue(
 1691234                    WideArithmetic.MultiplySigned576ToSigned832(_radiusSquared, _squared)));
 1691235                return CapsuleFiniteSectionFrame.IntersectsQuadratic(a, b, c, ln, ld, un, ud);
 236            }
 237
 238            // Exact normalized quaternion/yaw bounds give |A| < 2^98 and
 239            // |P| < 2^164 in the shared 2*D*Q raw-coordinate denominator.
 240            // Cross products fit 320 bits, radial coefficients 576 bits;
 241            // cone coefficients need fewer than 870 bits and use 16 limbs.
 242            const int words = 16;
 2632243            Span<ulong> storage = stackalloc ulong[10 * words];
 2632244            Span<int> signs = stackalloc int[10];
 2632245            ContactQuadratic qa = ContactQuadratic.At(storage, signs, 0, words),
 5264246                qb = ContactQuadratic.At(storage, signs, 1, words), qc = ContactQuadratic.At(storage, signs, 2, words),
 5264247                work = ContactQuadratic.At(storage, signs, 3, words), term = ContactQuadratic.At(storage, signs, 4, word
 2632248            Signed576 scale = WideArithmetic.MultiplySigned320(
 2632249                WideArithmetic.MultiplySigned192(_length, _length), _squared);
 5264250            scale = WideArithmetic.AddSigned576(scale, scale); scale = WideArithmetic.AddSigned576(scale, scale);
 2632251            Signed320 axial = WideArithmetic.SubtractSigned320(_cap, axialStart);
 2632252            Signed320 velocity = WideArithmetic.Negate(axialDelta);
 2632253            SetConeCoefficient(a, velocity, velocity, scale, _radiusSquared, qa, work, term);
 2632254            SetConeCoefficient(b, axial, velocity, scale, _radiusSquared, qb, work, term);
 2632255            SetConeCoefficient(c, axial, axial, scale, _radiusSquared, qc, work, term);
 2632256            return CapsuleFiniteSectionFrame.IntersectsQuadratic(qa, qb, qc, ln, ld, un, ud);
 257        }
 258    }
 259
 38067260    private static Signed576 CurvedDot(Axis3 first, Axis3 second) => WideArithmetic.AddSigned576(
 38067261        WideArithmetic.AddSigned576(WideArithmetic.MultiplySigned320(first.X, second.X),
 38067262            WideArithmetic.MultiplySigned320(first.Y, second.Y)), WideArithmetic.MultiplySigned320(first.Z, second.Z));
 263
 264    private static Signed576 Relative(Signed576 coordinate, Signed320 origin, Signed576 denominator) =>
 10991265        WideArithmetic.SubtractSigned576(coordinate, Signed576.NarrowValue(
 10991266            WideArithmetic.MultiplySigned576ToSigned832(denominator, origin)));
 267
 268    private static Signed320 CurvedDifferenceProduct(Signed320 a, Signed320 b, Signed320 c, Signed320 d) =>
 9984269        Signed320.NarrowValue(WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(a, b),
 9984270            WideArithmetic.MultiplySigned320(c, d)));
 271
 272    private static Signed576 FootCoordinate(Signed320 axis, Signed192 length, Signed576 j, Signed320 delta, Signed320 w)
 5679273        WideArithmetic.AddSigned576(Signed576.NarrowValue(WideArithmetic.MultiplySigned576ToSigned832(j,
 5679274            Signed320.NarrowValue(WideArithmetic.MultiplySigned320(axis, Signed320.ExtendValue(length))))),
 5679275            WideArithmetic.MultiplySigned320(delta, w));
 276
 277    private static void SetConeCoefficient(Signed576 radial, Signed320 first, Signed320 second,
 278        Signed576 scale, Signed576 radiusSquared, ContactQuadratic result, ContactQuadratic work, ContactQuadratic term)
 279    {
 15792280        result.Set(radial); ContactQuadratic.Scale(result, scale, work);
 15792281        result.Set(WideArithmetic.MultiplySigned320(first, second)); ContactQuadratic.Scale(result, radiusSquared, term)
 15792282        work.CopyTo(result); result.Add(term, -1);
 7896283    }
 284}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderCapsuleEllipse.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CylinderCapsuleEllipse.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact projected-cap ellipse candidates for finite cylinder/capsule contact.
 14/// The capsule-interior normal is retained as one real quartic root; neither
 15/// the authored axes nor the candidate depth are rounded during selection.
 16/// </content>
 17internal static partial class WideConvexPrismRelations
 18{
 19    // Admitted normalized quaternions have squared raw norm < 2^65, and
 20    // local axes have raw norm < 2^33. Exact rotation preserves the latter
 21    // norm, so rotated-axis numerators and F are < 2^100. With the
 22    // intentionally loose bounds used here, U,B,C < 2^202, E < 2^404,
 23    // F < 2^100, |P| < 2^367, |Q| < 2^470, and K < 2^164.
 24    // The cross axes are < 2^201 and < 2^302 per component. A center
 25    // difference is < 2^64, so their three-term projections are < 2^267
 26    // and < 2^368. Full cylinder length < 2^64 gives E*length < 2^468;
 27    // subtracting the minor projection times F keeps |Q| < 2^470.
 28    // These bounds include unrestricted opposite-limit origins and doubled
 29    // Fixed64 half-thicknesses without increasing the polynomial slots;
 30    // the positive GCD reduction below can only reduce them.
 31    // J has coefficients < 2^570; W < 2^1818 and S < 2^1244. Squaring S
 32    // sums at most three products, giving N < 2^2490; D < 2^2354.
 33    // Forty words preserve every bit, including coefficient-sum carries.
 34    private const int CylinderCapsuleEllipseWords = 40;
 35    // The quadratic analytic candidate uses at most forty words per scalar.
 36    // Eliminating its one radical from N/D - (A+B sqrt(C))/D0 produces
 37    // (N D0-D A)^2-D^2 B^2 C: five scalar widths plus sum carry bits.
 38    private const int CylinderCapsuleEllipseComparisonWords =
 39        5 * CylinderCapsuleEllipseWords + 2;
 40
 41    /// <summary>
 42    /// Rational geometry for n(t) = (Major + t Minor) / sqrt(E(1+B t^2)).
 43    /// Lengths, center differences, and the eventual gap are in raw units.
 44    /// </summary>
 45    private readonly struct CylinderCapsuleEllipse
 46    {
 47        internal readonly Axis3 Major;
 48        internal readonly Axis3 Minor;
 49        internal readonly Signed320 U;
 50        internal readonly Signed320 B;
 51        internal readonly Signed320 C;
 52        internal readonly Signed576 E;
 53        internal readonly Signed192 F;
 54        internal readonly Signed320 K;
 55        internal readonly Signed576 P;
 56        internal readonly Signed576 Q;
 57
 58        internal CylinderCapsuleEllipse(
 59            Axis3 major, Axis3 minor,
 60            Signed320 u, Signed320 b, Signed320 c, Signed576 e,
 61            Signed192 f, Signed320 k, Signed576 p, Signed576 q)
 62        {
 47363            Major = major;
 47364            Minor = minor;
 47365            U = u;
 47366            B = b;
 47367            C = c;
 47368            E = e;
 47369            F = f;
 47370            K = k;
 47371            P = p;
 47372            Q = q;
 47373        }
 74    }
 75
 76    /// <summary>
 77    /// Prepares the genuinely oblique family. The caller proves positive
 78    /// radius/core length and nonparallel, nonperpendicular axes. False means
 79    /// the query lies on a principal axis, whose boundaries already suffice.
 80    /// </summary>
 81    private static bool TryPrepareCylinderCapsuleEllipse(
 82        Vector3d cylinderCenter,
 83        in RigidAxis3 cylinderAxis,
 84        Signed192 cylinderLength,
 85        Fixed64 cylinderRadius,
 86        Vector3d capsuleCenter,
 87        in RigidAxis3 capsuleAxis,
 88        out CylinderCapsuleEllipse ellipse)
 89    {
 51990        ellipse = default;
 91        // Cylinder half-axis raw coordinates are a*length/F, so their full
 92        // positive integer content cancels only jointly with F. The capsule
 93        // axis is used solely to define its perpendicular plane here; its
 94        // direction may be made primitive without changing authored length.
 95        // This removes quaternion normalization factors before forming any
 96        // products, and avoids a tiny artificial t caused by an oversized b.
 51997        Signed192 f = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 51998            cylinderAxis.RotationDenominator, Signed192.Raw(Fixed64.Two)));
 51999        RigidAxis3 reducedCylinder = ReduceCylinderCapsuleEllipseAxis(cylinderAxis, ref f);
 519100        Signed192 unusedDenominator = default;
 519101        RigidAxis3 reducedCapsule = ReduceCylinderCapsuleEllipseAxis(capsuleAxis, ref unusedDenominator);
 519102        Axis3 a = reducedCylinder.ToWide();
 519103        Axis3 b = reducedCapsule.ToWide();
 519104        Axis3 major = Cross(a, b);
 519105        Signed320 c = Signed320.NarrowValue(GetAxisProjection(a, reducedCapsule));
 519106        Signed576 majorProjection = GetCylinderCapsuleEllipseProjection(
 519107            capsuleCenter, cylinderCenter, major);
 519108        if (majorProjection.IsZero)
 46109            return false;
 110
 473111        Axis3 minor = Cross(b, major);
 473112        Signed576 minorProjection = GetCylinderCapsuleEllipseProjection(
 473113            capsuleCenter, cylinderCenter, minor);
 473114        Signed576 e = GetAxisSquared(major);
 473115        Signed576 p = WideArithmetic.SubtractSigned576(
 473116            default,
 473117            WideArithmetic.MultiplySigned576(GetMagnitude576(majorProjection), f));
 473118        Signed576 q = WideArithmetic.SubtractSigned576(
 473119            WideArithmetic.MultiplySigned576(e, cylinderLength),
 473120            WideArithmetic.MultiplySigned576(GetMagnitude576(minorProjection), f));
 121
 122        // Both reflection choices can only decrease the support gap: the
 123        // projected cylinder is symmetric in these orthogonal principal axes.
 124        // A zero minor projection uses the original axis as the canonical tie.
 473125        ellipse = new CylinderCapsuleEllipse(
 473126            OrientCylinderCapsuleEllipseAxis(major, majorProjection.Sign),
 473127            OrientCylinderCapsuleEllipseAxis(minor, minorProjection.Sign),
 473128            GetAxisSquared(reducedCylinder), GetAxisSquared(reducedCapsule), c, e,
 473129            f, WideArithmetic.MultiplySigned192(f, Signed192.Raw(cylinderRadius)),
 473130            p, q);
 473131        return true;
 132    }
 133
 134    private static RigidAxis3 ReduceCylinderCapsuleEllipseAxis(
 135        in RigidAxis3 axis, ref Signed192 denominator)
 136    {
 1038137        Signed192 divisor = WideArithmetic.GetGreatestCommonDivisor(
 1038138            WideArithmetic.GetGreatestCommonDivisor(axis.X, axis.Y),
 1038139            WideArithmetic.GetGreatestCommonDivisor(axis.Z, denominator));
 1038140        denominator = WideArithmetic.DivideExactSigned192(denominator, divisor);
 1038141        return new RigidAxis3(
 1038142            WideArithmetic.DivideExactSigned192(axis.X, divisor),
 1038143            WideArithmetic.DivideExactSigned192(axis.Y, divisor),
 1038144            WideArithmetic.DivideExactSigned192(axis.Z, divisor), default);
 145    }
 146
 147    private static Axis3 OrientCylinderCapsuleEllipseAxis(Axis3 axis, int sign) =>
 946148        sign < 0
 946149            ? new Axis3(
 946150                WideArithmetic.Negate(axis.X),
 946151                WideArithmetic.Negate(axis.Y),
 946152                WideArithmetic.Negate(axis.Z))
 946153            : axis;
 154
 155    private static Signed576 GetCylinderCapsuleEllipseProjection(
 156        Vector3d end, Vector3d start, Axis3 axis) =>
 992157        WideArithmetic.AddSigned576(
 992158            WideArithmetic.AddSigned576(
 992159                WideArithmetic.MultiplySigned320(axis.X,
 992160                    WideArithmetic.SubtractSigned192(
 992161                        Signed192.Raw(end.X), Signed192.Raw(start.X))),
 992162                WideArithmetic.MultiplySigned320(axis.Y,
 992163                    WideArithmetic.SubtractSigned192(
 992164                        Signed192.Raw(end.Y), Signed192.Raw(start.Y)))),
 992165            WideArithmetic.MultiplySigned320(axis.Z,
 992166                WideArithmetic.SubtractSigned192(
 992167                    Signed192.Raw(end.Z), Signed192.Raw(start.Z))));
 168
 169    /// <summary>
 170    /// Competes the sole curved interior candidate against the already ranked
 171    /// analytic families. Exact ties retain that earlier canonical candidate.
 172    /// True means this family won; intersects is then the closed-contact result.
 173    /// </summary>
 174    private static bool TryImproveCylinderCapsuleEllipse(
 175        in CylinderCapsuleEllipse ellipse,
 176        in ConvexContactCandidate analytic,
 177        Fixed64 cylinderRadius, Fixed64 capsuleRadius,
 178        out bool intersects,
 179        out Vector3d normal,
 180        out Fixed64 depth,
 181        out bool depthIsClamped)
 182    {
 304183        intersects = false;
 304184        normal = default;
 304185        depth = default;
 304186        depthIsClamped = false;
 187        const int words = CylinderCapsuleEllipseWords;
 304188        Span<ulong> stationary = stackalloc ulong[5 * words];
 304189        Span<sbyte> stationarySigns = stackalloc sbyte[5];
 304190        Span<ulong> signedGap = stackalloc ulong[3 * words];
 304191        Span<sbyte> signedGapSigns = stackalloc sbyte[3];
 304192        Span<ulong> squaredGap = stackalloc ulong[10 * words];
 304193        Span<sbyte> squaredGapSigns = stackalloc sbyte[10];
 304194        Span<ulong> numerator = squaredGap.Slice(0, 5 * words);
 304195        Span<sbyte> numeratorSigns = squaredGapSigns.Slice(0, 5);
 304196        Span<ulong> denominator = squaredGap.Slice(5 * words, 5 * words);
 304197        Span<sbyte> denominatorSigns = squaredGapSigns.Slice(5, 5);
 304198        BuildCylinderCapsuleEllipsePolynomials(
 304199            ellipse, stationary, stationarySigns, signedGap, signedGapSigns,
 304200            numerator, numeratorSigns, denominator, denominatorSigns);
 304201        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(stationary, stationarySigns);
 304202        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(signedGap, signedGapSigns);
 203        // N and D must share exactly the same positive scale. Normalizing
 204        // them as one ten-coefficient polynomial preserves their quotient.
 304205        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(squaredGap, squaredGapSigns);
 304206        Span<ulong> rootCell = stackalloc ulong[
 304207            WideFiniteAxisIntersection.GetFiniteAxisRootCellWords(
 304208                stationary, stationarySigns)];
 304209        if (!WideFiniteAxisIntersection.TryGetLargestPositiveFiniteAxisRoot(
 304210                stationary, stationarySigns, rootCell,
 304211                out FiniteAxisPolynomialRoot root))
 212        {
 112213            return false;
 214        }
 215
 192216        int gapSign = WideFiniteAxisIntersection.GetSignAtFiniteAxisRoot(
 192217            ref root, signedGap, signedGapSigns);
 192218        int comparison = gapSign.CompareTo(analytic.GapSign);
 192219        if (comparison == 0 && gapSign != 0)
 220        {
 180221            comparison = gapSign * CompareCylinderCapsuleEllipseSquaredGap(
 180222                ref root, numerator, numeratorSigns,
 180223                denominator, denominatorSigns, analytic);
 224        }
 192225        if (comparison >= 0)
 43226            return false;
 227
 149228        int depthSign = CompareCylinderCapsuleEllipseDepthToTwiceRaw(
 149229            ref root, numerator, numeratorSigns, denominator, denominatorSigns,
 149230            gapSign, capsuleRadius, 0UL);
 149231        if (depthSign < 0)
 232        {
 82233            return true;
 234        }
 67235        intersects = true;
 236        // The admission query already proves an exact touch has zero depth.
 67237        if (depthSign > 0)
 61238            GetRoundedCylinderCapsuleEllipseDepth(
 61239                ref root, numerator, numeratorSigns, denominator, denominatorSigns,
 61240                gapSign, cylinderRadius, capsuleRadius, out depth, out depthIsClamped);
 67241        normal = new Vector3d(
 67242            GetRoundedCylinderCapsuleEllipseNormalComponent(ref root, ellipse, 0),
 67243            GetRoundedCylinderCapsuleEllipseNormalComponent(ref root, ellipse, 1),
 67244            GetRoundedCylinderCapsuleEllipseNormalComponent(ref root, ellipse, 2));
 67245        return true;
 246    }
 247
 248    private static int CompareCylinderCapsuleEllipseSquaredGap(
 249        ref FiniteAxisPolynomialRoot root,
 250        scoped ReadOnlySpan<ulong> numerator, scoped ReadOnlySpan<sbyte> numeratorSigns,
 251        scoped ReadOnlySpan<ulong> denominator, scoped ReadOnlySpan<sbyte> denominatorSigns,
 252        scoped in ConvexContactCandidate analytic)
 253    {
 254        const int sourceWords = CylinderCapsuleEllipseWords;
 255        const int words = CylinderCapsuleEllipseComparisonWords;
 283256        Span<ulong> difference = stackalloc ulong[5 * words];
 283257        Span<sbyte> differenceSigns = stackalloc sbyte[5];
 283258        Span<ulong> term = stackalloc ulong[words];
 283259        difference.Clear();
 283260        differenceSigns.Clear();
 3396261        for (int index = 0; index < 5; index++)
 262        {
 1415263            WideArithmetic.MultiplyMagnitudes(
 1415264                numerator.Slice(index * sourceWords, sourceWords),
 1415265                analytic.GapDenominator, term);
 1415266            AddCylinderCapsuleEllipseCoefficient(
 1415267                difference, differenceSigns, index, term, numeratorSigns[index], words);
 1415268            WideArithmetic.MultiplyMagnitudes(
 1415269                denominator.Slice(index * sourceWords, sourceWords),
 1415270                analytic.GapRational, term);
 1415271            AddCylinderCapsuleEllipseCoefficient(
 1415272                difference, differenceSigns, index, term,
 1415273                -denominatorSigns[index] * analytic.GapRationalSign, words);
 274        }
 283275        int rationalSign = WideFiniteAxisIntersection.GetSignAtFiniteAxisRoot(
 283276            ref root, difference, differenceSigns);
 283277        int radicalSign = IsZero(analytic.GapRadicand)
 283278            ? 0 : analytic.GapRadicalSign;
 283279        if (radicalSign == 0)
 2280            return rationalSign;
 281281        if (rationalSign != radicalSign)
 15282            return -radicalSign;
 283
 266284        Span<ulong> query = stackalloc ulong[9 * words];
 266285        Span<sbyte> querySigns = stackalloc sbyte[9];
 266286        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 266287            difference, differenceSigns, difference, differenceSigns,
 266288            query, querySigns, term);
 266289        difference.Clear();
 3192290        for (int index = 0; index < 5; index++)
 291        {
 1330292            denominator.Slice(index * sourceWords, sourceWords)
 1330293                .CopyTo(difference.Slice(index * words, sourceWords));
 1330294            differenceSigns[index] = denominatorSigns[index];
 295        }
 266296        Span<ulong> squaredDenominator = stackalloc ulong[9 * words];
 266297        Span<sbyte> squaredDenominatorSigns = stackalloc sbyte[9];
 266298        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 266299            difference, differenceSigns, difference, differenceSigns,
 266300            squaredDenominator, squaredDenominatorSigns, term);
 266301        WideArithmetic.MultiplyMagnitudes(
 266302            analytic.GapRadical, analytic.GapRadical, term);
 266303        MultiplyBy(term, analytic.GapRadicand);
 266304        ScaleCylinderCapsuleEllipsePolynomial(squaredDenominator, term, words);
 5320305        for (int index = 0; index < 9; index++)
 306        {
 2394307            AddCylinderCapsuleEllipseCoefficient(
 2394308                query, querySigns, index,
 2394309                squaredDenominator.Slice(index * words, words),
 2394310                -squaredDenominatorSigns[index], words);
 311        }
 266312        return rationalSign * WideFiniteAxisIntersection.GetSignAtFiniteAxisRoot(
 266313            ref root, query, querySigns);
 314    }
 315
 316    private static int CompareCylinderCapsuleEllipseDepthToTwiceRaw(
 317        ref FiniteAxisPolynomialRoot root,
 318        scoped ReadOnlySpan<ulong> numerator, scoped ReadOnlySpan<sbyte> numeratorSigns,
 319        scoped ReadOnlySpan<ulong> denominator, scoped ReadOnlySpan<sbyte> denominatorSigns,
 320        int gapSign, Fixed64 capsuleRadius, ulong twiceRaw)
 321    {
 1763322        ulong twiceRadius = (ulong)capsuleRadius.m_rawValue << 1;
 1763323        int thresholdSign = twiceRaw.CompareTo(twiceRadius);
 1763324        if (gapSign != thresholdSign)
 163325            return gapSign.CompareTo(thresholdSign);
 1600326        if (gapSign == 0)
 1327            return 0;
 328
 1599329        ulong magnitude = thresholdSign < 0
 1599330            ? twiceRadius - twiceRaw : twiceRaw - twiceRadius;
 1599331        return gapSign * CompareCylinderCapsuleEllipseMagnitudeToTwiceRaw(
 1599332            ref root, numerator, numeratorSigns, denominator, denominatorSigns, magnitude);
 333    }
 334
 335    private static int CompareCylinderCapsuleEllipseMagnitudeToTwiceRaw(
 336        ref FiniteAxisPolynomialRoot root,
 337        scoped ReadOnlySpan<ulong> numerator, scoped ReadOnlySpan<sbyte> numeratorSigns,
 338        scoped ReadOnlySpan<ulong> denominator, scoped ReadOnlySpan<sbyte> denominatorSigns,
 339        ulong twiceRaw)
 340    {
 6339341        int count = numeratorSigns.Length;
 6339342        int sourceWords = numerator.Length / count;
 6339343        int words = sourceWords + 3;
 6339344        Span<ulong> query = stackalloc ulong[count * words];
 6339345        Span<sbyte> querySigns = stackalloc sbyte[count];
 6339346        Span<ulong> threshold = stackalloc ulong[words];
 6339347        Span<ulong> term = stackalloc ulong[words];
 6339348        query.Clear();
 6339349        querySigns.Clear();
 6339350        threshold.Clear();
 6339351        threshold[0] = twiceRaw;
 6339352        SquareCylinderCapsuleEllipseMagnitude(threshold);
 49186353        for (int index = 0; index < count; index++)
 354        {
 18254355            term.Clear();
 18254356            numerator.Slice(index * sourceWords, sourceWords).CopyTo(term);
 18254357            ShiftLeft(term, 2);
 18254358            AddCylinderCapsuleEllipseCoefficient(
 18254359                query, querySigns, index, term, numeratorSigns[index], words);
 18254360            WideArithmetic.MultiplyMagnitudes(
 18254361                denominator.Slice(index * sourceWords, sourceWords), threshold, term);
 18254362            AddCylinderCapsuleEllipseCoefficient(
 18254363                query, querySigns, index, term, -denominatorSigns[index], words);
 364        }
 6339365        return WideFiniteAxisIntersection.GetSignAtFiniteAxisRoot(
 6339366            ref root, query, querySigns);
 367    }
 368
 369    private static void GetRoundedCylinderCapsuleEllipseDepth(
 370        ref FiniteAxisPolynomialRoot root,
 371        scoped ReadOnlySpan<ulong> numerator, scoped ReadOnlySpan<sbyte> numeratorSigns,
 372        scoped ReadOnlySpan<ulong> denominator, scoped ReadOnlySpan<sbyte> denominatorSigns,
 373        int gapSign, Fixed64 cylinderRadius, Fixed64 capsuleRadius,
 374        out Fixed64 depth, out bool depthIsClamped)
 375    {
 61376        depthIsClamped = false;
 61377        if (gapSign == 0)
 378        {
 2379            depth = capsuleRadius;
 2380            return;
 381        }
 382        // The admitted major-axis candidate has gap R-|d.major|/sqrt(E)<R.
 383        // This ellipse won against that candidate, so its positive gap is
 384        // also below the cylinder radius. The sum of two nonnegative raw
 385        // Fixed64 radii fits ulong; it never wraps even at both maxima.
 59386        ulong radiusRaw = (ulong)capsuleRadius.m_rawValue;
 59387        ulong lower = gapSign > 0 ? radiusRaw : 0UL;
 59388        ulong upper = gapSign < 0 ? radiusRaw : radiusRaw + (ulong)cylinderRadius.m_rawValue;
 59389        if (upper > (ulong)long.MaxValue)
 390        {
 4391            depthIsClamped = CompareCylinderCapsuleEllipseDepthToTwiceRaw(
 4392                ref root, numerator, numeratorSigns, denominator, denominatorSigns,
 4393                gapSign, capsuleRadius, (ulong)long.MaxValue << 1) > 0;
 4394            if (depthIsClamped)
 395            {
 2396                depth = Fixed64.MaxValue;
 2397                return;
 398            }
 2399            upper = (ulong)long.MaxValue;
 400        }
 57401        WideFiniteAxisIntersection.GetFiniteAxisRootSquareRootBounds(root,
 57402            numerator, numeratorSigns, denominator, denominatorSigns,
 57403            gapSign < 0 ? radiusRaw : (ulong)cylinderRadius.m_rawValue,
 57404            out ulong lowerMagnitude, out ulong upperMagnitude);
 405        // These bound the FLOOR of the magnitude, not its real value. For a
 406        // negative gap m<upperMagnitude+1, so floor(radius-m) may be one less.
 407        // Contact admission proves m<=radius; the positive winning gap is <R.
 408        // The exact overflow comparison above retains sole ownership of clamping.
 57409        if (gapSign < 0)
 410        {
 14411            lower = upperMagnitude >= radiusRaw ? 0 : radiusRaw - upperMagnitude - 1;
 14412            upper = radiusRaw - lowerMagnitude;
 413        }
 414        else
 415        {
 43416            lower = radiusRaw + lowerMagnitude;
 43417            upper = Math.Min(upper, radiusRaw + upperMagnitude);
 418        }
 419        // Every remaining threshold uses the same N/D. Reduce the pair once,
 420        // with a shared positive scale, rather than pseudo-dividing each query.
 421        // Bound the original structured denominator first: reduction can weaken
 422        // its interval enclosure even though the exact quotient is unchanged.
 57423        int sourceWords = numerator.Length / numeratorSigns.Length;
 57424        int ratioWords = WideFiniteAxisIntersection.GetFiniteAxisRootRatioWords(root,
 57425            numerator, numeratorSigns, denominator, denominatorSigns, out int first, out int count);
 57426        Span<ulong> ratio = stackalloc ulong[2 * count * ratioWords];
 57427        Span<sbyte> ratioSigns = stackalloc sbyte[2 * count];
 57428        int reducedCount = WideFiniteAxisIntersection.ReduceFiniteAxisRootRatio(root,
 57429            numerator.Slice(first * sourceWords, count * sourceWords), numeratorSigns.Slice(first, count),
 57430            denominator.Slice(first * sourceWords, count * sourceWords), denominatorSigns.Slice(first, count),
 57431            ratio, ratioSigns);
 57432        numerator = ratio[..(reducedCount * ratioWords)];
 57433        numeratorSigns = ratioSigns[..reducedCount];
 57434        denominator = ratio.Slice(count * ratioWords, reducedCount * ratioWords);
 57435        denominatorSigns = ratioSigns.Slice(count, reducedCount);
 1507436        while (lower < upper)
 437        {
 1450438            ulong middle = lower + ((upper - lower + 1UL) >> 1);
 1450439            int comparison = CompareCylinderCapsuleEllipseDepthToTwiceRaw(
 1450440                ref root, numerator, numeratorSigns, denominator, denominatorSigns,
 1450441                gapSign, capsuleRadius, middle << 1);
 1450442            if (comparison >= 0)
 721443                lower = middle;
 444            else
 729445                upper = middle - 1UL;
 446        }
 57447        int midpointComparison = CompareCylinderCapsuleEllipseDepthToTwiceRaw(
 57448            ref root, numerator, numeratorSigns, denominator, denominatorSigns,
 57449            gapSign, capsuleRadius, (lower << 1) + 1UL);
 57450        lower += GetNearestEvenIncrement(midpointComparison, lower);
 57451        depth = Fixed64.FromRaw((long)lower);
 57452    }
 453
 454    private static Fixed64 GetRoundedCylinderCapsuleEllipseNormalComponent(
 455        ref FiniteAxisPolynomialRoot root,
 456        in CylinderCapsuleEllipse ellipse,
 457        int component)
 458    {
 459        const int words = CylinderCapsuleEllipseWords;
 510460        Span<ulong> direction = stackalloc ulong[2 * words];
 510461        Span<sbyte> directionSigns = stackalloc sbyte[2];
 510462        Span<ulong> numerator = stackalloc ulong[3 * words];
 510463        Span<sbyte> numeratorSigns = stackalloc sbyte[3];
 510464        Span<ulong> denominator = stackalloc ulong[3 * words];
 510465        Span<sbyte> denominatorSigns = stackalloc sbyte[3];
 510466        BuildCylinderCapsuleEllipseNormalPolynomials(
 510467            ellipse, component, direction, directionSigns,
 510468            numerator, numeratorSigns, denominator, denominatorSigns);
 510469        int sign = WideFiniteAxisIntersection.GetSignAtFiniteAxisRoot(
 510470            ref root, direction, directionSigns);
 510471        if (sign == 0)
 100472            return Fixed64.Zero;
 473
 410474        WideFiniteAxisIntersection.GetFiniteAxisRootSquareRootBounds(root,
 410475            numerator, numeratorSigns, denominator, denominatorSigns,
 410476            1UL << FixedMath.SHIFT_AMOUNT_I, out ulong lower, out ulong upper);
 4740477        while (lower < upper)
 478        {
 4330479            ulong middle = lower + ((upper - lower + 1UL) >> 1);
 4330480            if (CompareCylinderCapsuleEllipseMagnitudeToTwiceRaw(
 4330481                    ref root, numerator, numeratorSigns, denominator, denominatorSigns, middle << 1) >= 0)
 482            {
 2272483                lower = middle;
 484            }
 485            else
 486            {
 2058487                upper = middle - 1UL;
 488            }
 489        }
 410490        int midpointComparison = CompareCylinderCapsuleEllipseMagnitudeToTwiceRaw(
 410491            ref root, numerator, numeratorSigns, denominator, denominatorSigns, (lower << 1) + 1UL);
 410492        lower += GetNearestEvenIncrement(midpointComparison, lower);
 410493        return Fixed64.FromRaw(sign < 0 ? -(long)lower : (long)lower);
 494    }
 495
 496    /// <summary>
 497    /// Builds W(t), S(t), and the exact squared-gap N(t)/D(t), in ascending
 498    /// coefficient order with a separate sign per magnitude. W,N,D have five
 499    /// coefficient slots, S three; every slot has CylinderCapsuleEllipseWords.
 500    /// </summary>
 501    private static void BuildCylinderCapsuleEllipsePolynomials(
 502        in CylinderCapsuleEllipse ellipse,
 503        Span<ulong> stationary, Span<sbyte> stationarySigns,
 504        Span<ulong> signedGap, Span<sbyte> signedGapSigns,
 505        Span<ulong> squaredGapNumerator, Span<sbyte> numeratorSigns,
 506        Span<ulong> squaredGapDenominator, Span<sbyte> denominatorSigns)
 507    {
 508        const int words = CylinderCapsuleEllipseWords;
 473509        Span<ulong> scalars = stackalloc ulong[8 * words];
 473510        scalars.Clear();
 473511        Span<ulong> u = scalars.Slice(0 * words, words);
 473512        Span<ulong> b = scalars.Slice(1 * words, words);
 473513        Span<ulong> cSquared = scalars.Slice(2 * words, words);
 473514        Span<ulong> e = scalars.Slice(3 * words, words);
 473515        Span<ulong> f = scalars.Slice(4 * words, words);
 473516        Span<ulong> kSquared = scalars.Slice(5 * words, words);
 473517        Span<ulong> p = scalars.Slice(6 * words, words);
 473518        Span<ulong> q = scalars.Slice(7 * words, words);
 473519        CopyCylinderCapsuleEllipseMagnitude(ellipse.U, u);
 473520        CopyCylinderCapsuleEllipseMagnitude(ellipse.B, b);
 473521        CopyCylinderCapsuleEllipseMagnitude(ellipse.C, cSquared);
 473522        SquareCylinderCapsuleEllipseMagnitude(cSquared);
 473523        WideArithmetic.GetMagnitude(ellipse.E, e);
 473524        CopyCylinderCapsuleEllipseMagnitude(Signed320.ExtendValue(ellipse.F), f);
 473525        CopyCylinderCapsuleEllipseMagnitude(ellipse.K, kSquared);
 473526        SquareCylinderCapsuleEllipseMagnitude(kSquared);
 473527        WideArithmetic.GetMagnitude(ellipse.P, p);
 473528        WideArithmetic.GetMagnitude(ellipse.Q, q);
 529
 473530        Span<ulong> j = stackalloc ulong[2 * words];
 473531        Span<sbyte> jSigns = stackalloc sbyte[2];
 473532        j.Clear();
 473533        q.CopyTo(j.Slice(0, words));
 473534        WideArithmetic.MultiplyMagnitudes(b, p, j.Slice(words, words));
 473535        jSigns[0] = (sbyte)ellipse.Q.Sign;
 473536        jSigns[1] = 1; // P < 0, B > 0: J = Q - B P t.
 473537        Span<ulong> jSquared = stackalloc ulong[3 * words];
 473538        Span<sbyte> jSquaredSigns = stackalloc sbyte[3];
 473539        Span<ulong> term = stackalloc ulong[words];
 473540        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 473541            j, jSigns, j, jSigns, jSquared, jSquaredSigns, term);
 542
 473543        Span<ulong> radial = stackalloc ulong[3 * words];
 473544        Span<sbyte> radialSigns = stackalloc sbyte[3];
 473545        radial.Clear();
 473546        radialSigns.Clear();
 473547        WideArithmetic.MultiplyMagnitudes(u, u, radial.Slice(0, words));
 473548        WideArithmetic.MultiplyMagnitudes(u, cSquared, radial.Slice(2 * words, words));
 473549        radialSigns[0] = 1;
 473550        radialSigns[2] = 1;
 473551        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 473552            radial, radialSigns, jSquared, jSquaredSigns,
 473553            stationary, stationarySigns, term);
 473554        kSquared.CopyTo(term);
 473555        MultiplyBy(term, e);
 473556        MultiplyBy(term, e);
 473557        MultiplyBy(term, e);
 473558        AddCylinderCapsuleEllipseCoefficient(
 473559            stationary, stationarySigns, 2, term, -1, words);
 560
 561        // At the largest positive W root, J > 0 and the unsquared
 562        // stationary equation is J sqrt(U(U+C^2 t^2)) = K E sqrt(E) t.
 563        // Therefore gap = S / (F U J sqrt(E(1+B t^2))), where
 564        // S = K^2 E^2 t + U J(P+Q t). Its denominator is strictly positive.
 473565        radial.Clear();
 473566        radialSigns.Clear();
 473567        p.CopyTo(radial.Slice(0, words));
 473568        q.CopyTo(radial.Slice(words, words));
 473569        radialSigns[0] = -1;
 473570        radialSigns[1] = (sbyte)ellipse.Q.Sign;
 473571        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 473572            j, jSigns, radial.Slice(0, 2 * words), radialSigns.Slice(0, 2),
 473573            signedGap, signedGapSigns, term);
 473574        ScaleCylinderCapsuleEllipsePolynomial(signedGap, u, words);
 473575        kSquared.CopyTo(term);
 473576        MultiplyBy(term, e);
 473577        MultiplyBy(term, e);
 473578        AddCylinderCapsuleEllipseCoefficient(
 473579            signedGap, signedGapSigns, 1, term, 1, words);
 473580        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 473581            signedGap, signedGapSigns, signedGap, signedGapSigns,
 473582            squaredGapNumerator, numeratorSigns, term);
 583
 473584        radial.Clear();
 473585        radialSigns.Clear();
 473586        radial[0] = 1UL;
 473587        b.CopyTo(radial.Slice(2 * words, words));
 473588        radialSigns[0] = 1;
 473589        radialSigns[2] = 1;
 473590        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 473591            jSquared, jSquaredSigns, radial, radialSigns,
 473592            squaredGapDenominator, denominatorSigns, term);
 473593        WideArithmetic.MultiplyMagnitudes(f, f, term);
 473594        MultiplyBy(term, u);
 473595        MultiplyBy(term, u);
 473596        MultiplyBy(term, e);
 473597        ScaleCylinderCapsuleEllipsePolynomial(squaredGapDenominator, term, words);
 473598    }
 599
 600    // Completeness of the retained root (the t=0 and t=infinity boundaries
 601    // are ordinary rational-axis candidates in the pair owner):
 602    //
 603    // Before reflection, e=a cross b, f=b cross e, e^2=E, f^2=B E,
 604    // and E=U B-C^2. The projected cap disk and projected cylinder half-axis
 605    // are symmetric about e and f, so reflecting either normal coordinate
 606    // toward its matching d projection cannot increase the support gap.
 607    // For n=(e+t f)/sqrt(E(1+B t^2)) after those independent reflections,
 608    // F gap(t)=[K sqrt(E(U+C^2 t^2)/U)+P+Q t]/sqrt(E(1+B t^2)).
 609    // Here P<0 and Q is unrestricted. For t>0 its derivative has the sign of
 610    // h(t)=Q/t+B|P|-K E sqrt(E)/(sqrt(U)*sqrt(U+C^2 t^2)).
 611    // If Q<=0, h is strictly increasing. If Q>0, h' changes sign at most
 612    // once, negative to positive, because t^3/(U+C^2 t^2)^(3/2) increases.
 613    // Thus at most one interior local minimum exists. Since h(infinity)>0,
 614    // it is the largest positive root; an even last root is harmless to retain.
 615    // When Q<0, J is nonpositive only below -Q/(B|P|), where h is negative;
 616    // the genuine root above that threshold is larger than every squared-sign
 617    // artifact. When Q>=0 every positive root already has J>0.
 618    // When P=0, the derivative has at most a positive-to-negative crossing,
 619    // so only the two boundaries can minimize. C=0, E=0 and K=0 are the
 620    // rectangle/circle/segment reductions, handled before this construction.
 621
 622    /// <summary>
 623    /// Builds the signed component direction and its squared raw magnitude
 624    /// numerator/denominator once. Only the threshold changes during rounding;
 625    /// every comparison uses the same exact polynomials at the retained root.
 626    /// </summary>
 627    private static void BuildCylinderCapsuleEllipseNormalPolynomials(
 628        in CylinderCapsuleEllipse ellipse,
 629        int component,
 630        Span<ulong> direction, Span<sbyte> directionSigns,
 631        Span<ulong> numerator, Span<sbyte> numeratorSigns,
 632        Span<ulong> denominator, Span<sbyte> denominatorSigns)
 633    {
 634        const int words = CylinderCapsuleEllipseWords;
 510635        Signed320 first = component == 0 ? ellipse.Major.X
 510636            : component == 1 ? ellipse.Major.Y : ellipse.Major.Z;
 510637        Signed320 second = component == 0 ? ellipse.Minor.X
 510638            : component == 1 ? ellipse.Minor.Y : ellipse.Minor.Z;
 510639        direction.Clear();
 510640        CopyCylinderCapsuleEllipseMagnitude(first, direction.Slice(0, words));
 510641        CopyCylinderCapsuleEllipseMagnitude(second, direction.Slice(words, words));
 510642        directionSigns[0] = (sbyte)first.Sign;
 510643        directionSigns[1] = (sbyte)second.Sign;
 510644        Span<ulong> factor = stackalloc ulong[words];
 510645        WideFiniteAxisIntersection.MultiplyFiniteAxisPolynomials(
 510646            direction, directionSigns, direction, directionSigns,
 510647            numerator, numeratorSigns, factor);
 648        // Raw squared normal = 2^64 * component(t)^2 / (E(1+B t^2)).
 649        // The shared half-raw comparison supplies the remaining factor four.
 4080650        for (int index = 0; index < 3; index++)
 1530651            ShiftLeft(numerator.Slice(index * words, words), 64);
 510652        denominator.Clear();
 510653        denominatorSigns.Clear();
 510654        WideArithmetic.GetMagnitude(ellipse.E, denominator.Slice(0, words));
 510655        CopyCylinderCapsuleEllipseMagnitude(ellipse.B, factor);
 510656        WideArithmetic.MultiplyMagnitudes(denominator.Slice(0, words), factor,
 510657            denominator.Slice(2 * words, words));
 510658        denominatorSigns[0] = 1;
 510659        denominatorSigns[2] = 1;
 510660    }
 661
 662    private static void CopyCylinderCapsuleEllipseMagnitude(
 663        Signed320 value, Span<ulong> destination)
 664    {
 3895665        destination.Clear();
 3895666        GetMagnitude(value, destination.Slice(0, 5));
 3895667    }
 668
 669    private static void SquareCylinderCapsuleEllipseMagnitude(Span<ulong> value)
 670    {
 7285671        Span<ulong> square = stackalloc ulong[value.Length];
 7285672        WideArithmetic.MultiplyMagnitudes(value, value, square);
 7285673        square.CopyTo(value);
 7285674    }
 675
 676    private static void ScaleCylinderCapsuleEllipsePolynomial(
 677        Span<ulong> coefficients, ReadOnlySpan<ulong> factor, int words)
 678    {
 14780679        for (int index = 0; index < coefficients.Length; index += words)
 6178680            MultiplyBy(coefficients.Slice(index, words), factor);
 1212681    }
 682
 683    private static void AddCylinderCapsuleEllipseCoefficient(
 684        Span<ulong> coefficients, Span<sbyte> signs, int index,
 685        ReadOnlySpan<ulong> value, int sign, int words)
 686    {
 42678687        Span<ulong> result = stackalloc ulong[words];
 42678688        Span<ulong> coefficient = coefficients.Slice(index * words, words);
 42678689        CombineWideSignedMagnitudes(
 42678690            coefficient, signs[index], value, sign, result, out int combinedSign);
 42678691        result.CopyTo(coefficient);
 42678692        signs[index] = (sbyte)combinedSign;
 42678693    }
 694}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderCapsuleFeatures.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CylinderCapsuleFeatures.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Analytic normal strata and certified endpoint/rim contacts for a finite
 14/// cylinder and capsule. All intermediate positions retain authored rational
 15/// axes; only the selected normal and complete contact depth are rounded.
 16/// </content>
 17internal static partial class WideConvexPrismRelations
 18{
 19    private readonly struct CylinderCapsuleDirection
 20    {
 21        internal readonly Signed576 X;
 22        internal readonly Signed576 Y;
 23        internal readonly Signed576 Z;
 24
 25        internal CylinderCapsuleDirection(Signed576 x, Signed576 y, Signed576 z)
 26        {
 1500327            X = x;
 1500328            Y = y;
 1500329            Z = z;
 1500330        }
 31
 32        internal CylinderCapsuleDirection(Axis3 axis)
 2455633            : this(Signed576.ExtendValue(axis.X), Signed576.ExtendValue(axis.Y), Signed576.ExtendValue(axis.Z)) { }
 34
 763435        internal bool IsZero => X.IsZero && Y.IsZero && Z.IsZero;
 36    }
 37
 38    private readonly struct CylinderCapsuleFeatureGeometry
 39    {
 40        internal readonly RigidAxis3 Cylinder;
 41        internal readonly RigidAxis3 Capsule;
 42        internal readonly Signed192 DifferenceX;
 43        internal readonly Signed192 DifferenceY;
 44        internal readonly Signed192 DifferenceZ;
 45        internal readonly Signed192 Scale;
 46        internal readonly Signed320 CenterX;
 47        internal readonly Signed320 CenterY;
 48        internal readonly Signed320 CenterZ;
 49        internal readonly Axis3 CylinderHalf;
 50        internal readonly Axis3 CapsuleHalf;
 51        internal readonly Signed320 CylinderSquared;
 52        internal readonly Signed192 CylinderLength;
 53        internal readonly Signed192 CapsuleLength;
 54        internal readonly Fixed64 Radius;
 55
 56        internal CylinderCapsuleFeatureGeometry(
 57            Vector3d cylinderCenter, RigidAxis3 cylinder, Signed192 cylinderLength, Fixed64 radius,
 58            Vector3d capsuleCenter, RigidAxis3 capsule, Signed192 capsuleLength)
 59        {
 159860            Cylinder = cylinder;
 159861            Capsule = capsule;
 159862            CylinderLength = cylinderLength;
 159863            CapsuleLength = capsuleLength;
 159864            Radius = radius;
 159865            DifferenceX = WideArithmetic.Difference(capsuleCenter.X, cylinderCenter.X);
 159866            DifferenceY = WideArithmetic.Difference(capsuleCenter.Y, cylinderCenter.Y);
 159867            DifferenceZ = WideArithmetic.Difference(capsuleCenter.Z, cylinderCenter.Z);
 159868            Signed192 denominators = Signed192.NarrowProven(
 159869                WideArithmetic.MultiplySigned192(cylinder.RotationDenominator, capsule.RotationDenominator));
 159870            Scale = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 159871                denominators, Signed192.Raw(Fixed64.Two)));
 159872            CenterX = WideArithmetic.MultiplySigned192(DifferenceX, Scale);
 159873            CenterY = WideArithmetic.MultiplySigned192(DifferenceY, Scale);
 159874            CenterZ = WideArithmetic.MultiplySigned192(DifferenceZ, Scale);
 159875            CylinderHalf = GetCylinderCapsuleScaledHalf(cylinder, cylinderLength, capsule.RotationDenominator);
 159876            CapsuleHalf = GetCylinderCapsuleScaledHalf(capsule, capsuleLength, cylinder.RotationDenominator);
 159877            CylinderSquared = GetAxisSquared(cylinder);
 159878        }
 79
 657980        internal Axis3 EndpointOffset(int cylinderSign, int capsuleSign) => new(
 657981            GetCylinderCapsuleEndpointCoordinate(CenterX, CylinderHalf.X, cylinderSign, CapsuleHalf.X, capsuleSign),
 657982            GetCylinderCapsuleEndpointCoordinate(CenterY, CylinderHalf.Y, cylinderSign, CapsuleHalf.Y, capsuleSign),
 657983            GetCylinderCapsuleEndpointCoordinate(CenterZ, CylinderHalf.Z, cylinderSign, CapsuleHalf.Z, capsuleSign));
 84    }
 85
 319686    private static Axis3 GetCylinderCapsuleScaledHalf(RigidAxis3 axis, Signed192 length, Signed192 otherDenominator) => 
 319687        Signed320.NarrowValue(WideArithmetic.MultiplySigned320(
 319688            WideArithmetic.MultiplySigned192(axis.X, length), otherDenominator)),
 319689        Signed320.NarrowValue(WideArithmetic.MultiplySigned320(
 319690            WideArithmetic.MultiplySigned192(axis.Y, length), otherDenominator)),
 319691        Signed320.NarrowValue(WideArithmetic.MultiplySigned320(
 319692            WideArithmetic.MultiplySigned192(axis.Z, length), otherDenominator)));
 93
 94    private static Signed320 GetCylinderCapsuleEndpointCoordinate(
 95        Signed320 center, Signed320 cylinderHalf, int cylinderSign, Signed320 capsuleHalf, int capsuleSign)
 96    {
 2300897        Signed320 result = cylinderSign == 0 ? center : cylinderSign > 0
 2300898            ? WideArithmetic.SubtractSigned320(center, cylinderHalf)
 2300899            : WideArithmetic.AddSigned320(center, cylinderHalf);
 23008100        return capsuleSign == 0 ? result : capsuleSign > 0
 23008101            ? WideArithmetic.SubtractSigned320(result, capsuleHalf)
 23008102            : WideArithmetic.AddSigned320(result, capsuleHalf);
 103    }
 104
 105    private static Signed576 DotCylinderCapsuleDirection(CylinderCapsuleDirection direction, RigidAxis3 axis) =>
 20203106        DotCylinderCapsuleDirection(direction, axis.X, axis.Y, axis.Z);
 107
 108    private static Signed576 DotCylinderCapsuleDirection(
 109        CylinderCapsuleDirection direction, Signed192 x, Signed192 y, Signed192 z) =>
 26975110        WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(
 26975111            WideArithmetic.MultiplySigned576(direction.X, x), WideArithmetic.MultiplySigned576(direction.Y, y)),
 26975112            WideArithmetic.MultiplySigned576(direction.Z, z));
 113
 114    private static CylinderCapsuleDirection ProjectCylinderCapsulePerpendicular(Axis3 offset, RigidAxis3 axis)
 115    {
 2725116        Signed320 squared = GetAxisSquared(axis);
 2725117        Signed576 dot = DotCylinderCapsuleDirection(new CylinderCapsuleDirection(offset), axis);
 2725118        return new CylinderCapsuleDirection(
 2725119            WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(offset.X, squared),
 2725120                WideArithmetic.MultiplySigned576(dot, axis.X)),
 2725121            WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(offset.Y, squared),
 2725122                WideArithmetic.MultiplySigned576(dot, axis.Y)),
 2725123            WideArithmetic.SubtractSigned576(WideArithmetic.MultiplySigned320(offset.Z, squared),
 2725124                WideArithmetic.MultiplySigned576(dot, axis.Z)));
 125    }
 126
 127    private static CylinderCapsuleDirection GetCylinderCapsulePerpendicular(
 128        FixedQuaternion rotation, Vector3d localAxis)
 129    {
 130        // A continuous radial tie follows the authored frame, so applying a
 131        // common rigid rotation also rotates the selected representative.
 200132        Vector3d localPerpendicular = localAxis.X != Fixed64.Zero || localAxis.Y != Fixed64.Zero
 200133            ? new Vector3d(localAxis.Y, -localAxis.X, Fixed64.Zero) : Vector3d.Right;
 200134        WideOrientedBox.GetRotatedLocalAxisNumerators(rotation, localPerpendicular,
 200135            out Signed192 x, out Signed192 y, out Signed192 z, out _);
 200136        return new CylinderCapsuleDirection(new Axis3(Signed320.ExtendValue(x),
 200137            Signed320.ExtendValue(y), Signed320.ExtendValue(z)));
 138    }
 139
 140    private static void ImportCylinderCapsuleFeature(Signed192 value, Span<ulong> destination)
 141    {
 6396142        destination.Clear();
 6396143        WideArithmetic.GetMagnitude(value, out destination[2], out destination[1], out destination[0]);
 6396144    }
 145
 146    private static void ImportCylinderCapsuleFeature(Signed320 value, Span<ulong> destination)
 147    {
 14911148        destination.Clear();
 14911149        WideArithmetic.GetMagnitude(value, out destination[4], out destination[3], out destination[2],
 14911150            out destination[1], out destination[0]);
 14911151    }
 152
 153    private static void ImportCylinderCapsuleFeature(Signed576 value, Span<ulong> destination)
 154    {
 26241155        destination.Clear();
 26241156        WideArithmetic.GetMagnitude(value, destination[..9]);
 26241157    }
 158
 159    private static void ImportCylinderCapsuleFeature(Signed832 value, Span<ulong> destination)
 160    {
 10448161        destination.Clear();
 10448162        WideArithmetic.GetMagnitude(value, destination[..13]);
 10448163    }
 164
 165    private static Span<ulong> CylinderCapsuleFeatureSlot(Span<ulong> values, int slot) =>
 144700166        values.Slice(slot * ConvexContactCandidate.Words, ConvexContactCandidate.Words);
 167
 168    private static bool BuildCylinderCapsuleAxisCandidate(
 169        in CylinderCapsuleFeatureGeometry geometry, CylinderCapsuleDirection direction,
 170        Span<ulong> values, Span<int> signs, out int gapSign)
 171    {
 5951172        gapSign = 0;
 5951173        if (direction.IsZero)
 4174            return false;
 5947175        values.Clear();
 5947176        signs.Clear();
 5947177        Signed576 cylinderAlignment = DotCylinderCapsuleDirection(direction, geometry.Cylinder);
 5947178        Signed576 capsuleAlignment = DotCylinderCapsuleDirection(direction, geometry.Capsule);
 5947179        Signed576 centerAlignment = DotCylinderCapsuleDirection(direction,
 5947180            geometry.DifferenceX, geometry.DifferenceY, geometry.DifferenceZ);
 5947181        int orientation = centerAlignment.Sign < 0 ? -1 : 1;
 5947182        ImportCylinderCapsuleFeature(direction.X, CylinderCapsuleFeatureSlot(values, 0));
 5947183        ImportCylinderCapsuleFeature(direction.Y, CylinderCapsuleFeatureSlot(values, 1));
 5947184        ImportCylinderCapsuleFeature(direction.Z, CylinderCapsuleFeatureSlot(values, 2));
 5947185        signs[0] = direction.X.Sign * orientation;
 5947186        signs[1] = direction.Y.Sign * orientation;
 5947187        signs[2] = direction.Z.Sign * orientation;
 188
 189        // Every generated rational direction is below 2^440. Dot products are
 190        // below 2^542, the common rational support below 2^674. The final
 191        // squared-gap coefficients are below 2^1780 (40 words give 2560 bits).
 5947192        Signed832 rational = WideArithmetic.SubtractSigned832(WideArithmetic.AddSigned832(
 5947193            WideArithmetic.MultiplySigned576ToSigned832(GetMagnitude576(cylinderAlignment),
 5947194                WideArithmetic.MultiplySigned192(geometry.CylinderLength, geometry.Capsule.RotationDenominator)),
 5947195            WideArithmetic.MultiplySigned576ToSigned832(GetMagnitude576(capsuleAlignment),
 5947196                WideArithmetic.MultiplySigned192(geometry.CapsuleLength, geometry.Cylinder.RotationDenominator))),
 5947197            WideArithmetic.MultiplySigned576ToSigned832(GetMagnitude576(centerAlignment), Signed320.ExtendValue(geometry
 198
 199        const int words = ConvexContactCandidate.Words;
 5947200        Span<ulong> work = stackalloc ulong[10 * words];
 5947201        Span<ulong> u = CylinderCapsuleFeatureSlot(work, 0);
 5947202        Span<ulong> axisSquared = CylinderCapsuleFeatureSlot(work, 1);
 5947203        Span<ulong> product = CylinderCapsuleFeatureSlot(work, 2);
 5947204        Span<ulong> temporary = CylinderCapsuleFeatureSlot(work, 3);
 5947205        Span<ulong> plane = CylinderCapsuleFeatureSlot(work, 4);
 5947206        Span<ulong> p = CylinderCapsuleFeatureSlot(work, 5);
 5947207        Span<ulong> q = CylinderCapsuleFeatureSlot(work, 6);
 5947208        Span<ulong> pSquared = CylinderCapsuleFeatureSlot(work, 7);
 5947209        Span<ulong> qSquaredK = CylinderCapsuleFeatureSlot(work, 8);
 5947210        Span<ulong> f = CylinderCapsuleFeatureSlot(work, 9);
 5947211        axisSquared.Clear();
 47576212        for (int component = 0; component < 3; component++)
 213        {
 17841214            Span<ulong> coordinate = CylinderCapsuleFeatureSlot(values, component);
 17841215            WideArithmetic.MultiplyMagnitudes(coordinate, coordinate, product);
 17841216            WideArithmetic.AddMagnitudeInto(product, axisSquared);
 217        }
 5947218        ImportCylinderCapsuleFeature(geometry.CylinderSquared, u);
 5947219        WideArithmetic.MultiplyMagnitudes(axisSquared, u, product);
 5947220        ImportCylinderCapsuleFeature(cylinderAlignment, temporary);
 5947221        WideArithmetic.MultiplyMagnitudes(temporary, temporary, plane);
 5947222        WideArithmetic.SubtractEqualMagnitudes(product, plane, temporary);
 5947223        temporary.CopyTo(plane);
 5947224        ImportCylinderCapsuleFeature(rational, temporary);
 5947225        WideArithmetic.MultiplyMagnitudes(temporary, u, p);
 5947226        ImportCylinderCapsuleFeature(WideArithmetic.MultiplySigned192(
 5947227            Signed192.Raw(geometry.Radius), geometry.Scale), q);
 5947228        Span<ulong> k = CylinderCapsuleFeatureSlot(values, 9);
 5947229        WideArithmetic.MultiplyMagnitudes(u, plane, k);
 5947230        WideArithmetic.MultiplyMagnitudes(p, p, pSquared);
 5947231        WideArithmetic.MultiplyMagnitudes(q, q, temporary);
 5947232        WideArithmetic.MultiplyMagnitudes(temporary, k, qSquaredK);
 5947233        WideArithmetic.AddEqualMagnitudes(pSquared, qSquaredK, CylinderCapsuleFeatureSlot(values, 7));
 5947234        WideArithmetic.MultiplyMagnitudes(p, q, temporary);
 5947235        WideArithmetic.AddEqualMagnitudes(temporary, temporary, CylinderCapsuleFeatureSlot(values, 8));
 5947236        signs[7] = WideArithmetic.GetActiveMagnitudeLength(CylinderCapsuleFeatureSlot(values, 7)) == 0 ? 0 : 1;
 5947237        signs[8] = WideArithmetic.GetActiveMagnitudeLength(CylinderCapsuleFeatureSlot(values, 8)) == 0 ? 0 : rational.Si
 5947238        signs[9] = WideArithmetic.GetActiveMagnitudeLength(k) == 0 ? 0 : 1;
 5947239        ImportCylinderCapsuleFeature(geometry.Scale, f);
 5947240        WideArithmetic.MultiplyMagnitudes(f, u, temporary);
 5947241        WideArithmetic.MultiplyMagnitudes(temporary, temporary, product);
 5947242        WideArithmetic.MultiplyMagnitudes(product, axisSquared, CylinderCapsuleFeatureSlot(values, 10));
 5947243        signs[10] = 1;
 5947244        gapSign = rational.Sign < 0
 5947245            ? WideArithmetic.CompareMagnitudeEqualLength(qSquaredK, pSquared)
 5947246            : rational.Sign > 0 || WideArithmetic.GetActiveMagnitudeLength(qSquaredK) != 0 ? 1 : 0;
 5947247        return true;
 248    }
 249
 250    private static bool BuildCylinderCapsuleEndpointRimCandidate(
 251        in CylinderCapsuleFeatureGeometry geometry, int cylinderSign, int capsuleSign,
 252        Span<ulong> values, Span<int> signs, out int gapSign)
 253    {
 2958254        gapSign = -1;
 2958255        Axis3 offset = geometry.EndpointOffset(cylinderSign, capsuleSign);
 2958256        var direction = new CylinderCapsuleDirection(offset);
 2958257        Signed576 axial = DotCylinderCapsuleDirection(direction, geometry.Cylinder);
 2958258        if (axial.Sign * cylinderSign < 0)
 2252259            return false;
 706260        Signed576 g = GetAxisSquared(offset);
 706261        Signed832 q = WideArithmetic.SubtractSigned832(
 706262            WideArithmetic.MultiplySigned576ToSigned832(g, geometry.CylinderSquared),
 706263            WideArithmetic.MultiplySigned576ToSigned832(axial, axial));
 264        // On the radial axis the closest feature is a cap, not a rim. Its
 265        // normal is already present in the analytic boundary candidates.
 706266        if (q.IsZero)
 2267            return false;
 704268        Signed320 rf = WideArithmetic.MultiplySigned192(Signed192.Raw(geometry.Radius), geometry.Scale);
 704269        Signed576 rfSquared = WideArithmetic.MultiplySigned320(rf, rf);
 704270        if (WideArithmetic.SubtractSigned832(q,
 704271                WideArithmetic.MultiplySigned576ToSigned832(rfSquared, geometry.CylinderSquared)).Sign < 0)
 97272            return false;
 273
 607274        values.Clear();
 607275        signs.Clear();
 276        const int words = ConvexContactCandidate.Words;
 607277        Span<ulong> work = stackalloc ulong[3 * words];
 607278        Span<ulong> first = CylinderCapsuleFeatureSlot(work, 0);
 607279        Span<ulong> second = CylinderCapsuleFeatureSlot(work, 1);
 607280        Span<ulong> third = CylinderCapsuleFeatureSlot(work, 2);
 607281        ImportCylinderCapsuleFeature(q, first);
 607282        ImportCylinderCapsuleFeature(geometry.CylinderSquared, second);
 607283        Span<ulong> root = CylinderCapsuleFeatureSlot(values, 6);
 607284        WideArithmetic.MultiplyMagnitudes(first, second, root);
 607285        root.CopyTo(CylinderCapsuleFeatureSlot(values, 9));
 607286        signs[6] = signs[9] = 1;
 287
 607288        CylinderCapsuleDirection radial = ProjectCylinderCapsulePerpendicular(offset, geometry.Cylinder);
 607289        Signed832 nx = WideArithmetic.MultiplySigned576ToSigned832(radial.X, rf);
 607290        Signed832 ny = WideArithmetic.MultiplySigned576ToSigned832(radial.Y, rf);
 607291        Signed832 nz = WideArithmetic.MultiplySigned576ToSigned832(radial.Z, rf);
 607292        ImportCylinderCapsuleFeature(nx, CylinderCapsuleFeatureSlot(values, 0));
 607293        ImportCylinderCapsuleFeature(ny, CylinderCapsuleFeatureSlot(values, 1));
 607294        ImportCylinderCapsuleFeature(nz, CylinderCapsuleFeatureSlot(values, 2));
 607295        signs[0] = -nx.Sign;
 607296        signs[1] = -ny.Sign;
 607297        signs[2] = -nz.Sign;
 607298        ImportCylinderCapsuleFeature(offset.X, CylinderCapsuleFeatureSlot(values, 3));
 607299        ImportCylinderCapsuleFeature(offset.Y, CylinderCapsuleFeatureSlot(values, 4));
 607300        ImportCylinderCapsuleFeature(offset.Z, CylinderCapsuleFeatureSlot(values, 5));
 607301        signs[3] = offset.X.Sign;
 607302        signs[4] = offset.Y.Sign;
 607303        signs[5] = offset.Z.Sign;
 304
 607305        if (!geometry.CapsuleLength.IsZero)
 306        {
 599307            Signed576 projection = DotCylinderCapsuleDirection(direction, geometry.Capsule);
 599308            Signed576 radialProjection = DotCylinderCapsuleDirection(radial, geometry.Capsule);
 599309            Signed832 rational = WideArithmetic.MultiplySigned576ToSigned832(radialProjection, rf);
 599310            ImportCylinderCapsuleFeature(rational, first);
 599311            ImportCylinderCapsuleFeature(projection, second);
 599312            if (GetConvexContactCandidateQuadraticSign(first, -rational.Sign, second, projection.Sign, root) * capsuleSi
 467313                return false;
 314        }
 315
 140316        Signed832 gapRational = WideArithmetic.MultiplySigned576ToSigned832(
 140317            WideArithmetic.AddSigned576(g, rfSquared), geometry.CylinderSquared);
 140318        ImportCylinderCapsuleFeature(gapRational, CylinderCapsuleFeatureSlot(values, 7));
 140319        ImportCylinderCapsuleFeature(WideArithmetic.AddSigned320(rf, rf), CylinderCapsuleFeatureSlot(values, 8));
 140320        signs[7] = gapRational.Sign;
 140321        signs[8] = -rf.Sign;
 140322        ImportCylinderCapsuleFeature(geometry.Scale, first);
 140323        WideArithmetic.MultiplyMagnitudes(first, first, third);
 140324        ImportCylinderCapsuleFeature(geometry.CylinderSquared, second);
 140325        WideArithmetic.MultiplyMagnitudes(third, second, CylinderCapsuleFeatureSlot(values, 10));
 140326        signs[10] = 1;
 327        // A zero residual has no normal. Shared cap/side boundaries represent
 328        // this exact-touch case, including the continuous tie at the rim.
 140329        return GetConvexContactCandidateQuadraticSign(CylinderCapsuleFeatureSlot(values, 7), signs[7],
 140330            CylinderCapsuleFeatureSlot(values, 8), signs[8], root) > 0;
 331    }
 332
 333    private static bool BuildCylinderCapsuleProjectedCornerCandidate(
 334        in CylinderCapsuleFeatureGeometry geometry, Axis3 major, int cylinderSign,
 335        Span<ulong> values, Span<int> signs, out int gapSign)
 336    {
 1428337        gapSign = -1;
 1428338        Axis3 offset = geometry.EndpointOffset(cylinderSign, 0);
 1428339        Signed576 axial = DotCylinderCapsuleDirection(new CylinderCapsuleDirection(offset), geometry.Cylinder);
 1428340        if (axial.Sign * cylinderSign <= 0)
 1040341            return false;
 388342        Signed576 e = GetAxisSquared(major);
 388343        Signed576 majorProjection = DotCylinderCapsuleDirection(new CylinderCapsuleDirection(major),
 388344            geometry.DifferenceX, geometry.DifferenceY, geometry.DifferenceZ);
 388345        Signed320 radiusSquared = WideArithmetic.MultiplySigned192(
 388346            Signed192.Raw(geometry.Radius), Signed192.Raw(geometry.Radius));
 388347        if (WideArithmetic.SubtractSigned832(
 388348                WideArithmetic.MultiplySigned576ToSigned832(majorProjection, majorProjection),
 388349                WideArithmetic.MultiplySigned576ToSigned832(e, radiusSquared)).Sign <= 0)
 79350            return false;
 351
 352        // With perpendicular axes the projected cylinder is a rectangle.
 353        // Both coordinate excesses are positive, so its nearest corner gives
 354        // the sole non-boundary minimum on the capsule-interior normal plane.
 309355        values.Clear();
 309356        signs.Clear();
 309357        CylinderCapsuleDirection projected = ProjectCylinderCapsulePerpendicular(offset, geometry.Capsule);
 309358        Signed320 b = GetAxisSquared(geometry.Capsule);
 309359        Signed320 rf = WideArithmetic.MultiplySigned192(Signed192.Raw(geometry.Radius), geometry.Scale);
 309360        Signed576 rfb = WideArithmetic.MultiplySigned320(rf, b);
 309361        Signed832 nx = WideArithmetic.MultiplySigned576ToSigned832(rfb, major.X);
 309362        Signed832 ny = WideArithmetic.MultiplySigned576ToSigned832(rfb, major.Y);
 309363        Signed832 nz = WideArithmetic.MultiplySigned576ToSigned832(rfb, major.Z);
 309364        ImportCylinderCapsuleFeature(nx, CylinderCapsuleFeatureSlot(values, 0));
 309365        ImportCylinderCapsuleFeature(ny, CylinderCapsuleFeatureSlot(values, 1));
 309366        ImportCylinderCapsuleFeature(nz, CylinderCapsuleFeatureSlot(values, 2));
 309367        signs[0] = -nx.Sign * majorProjection.Sign;
 309368        signs[1] = -ny.Sign * majorProjection.Sign;
 309369        signs[2] = -nz.Sign * majorProjection.Sign;
 309370        ImportCylinderCapsuleFeature(projected.X, CylinderCapsuleFeatureSlot(values, 3));
 309371        ImportCylinderCapsuleFeature(projected.Y, CylinderCapsuleFeatureSlot(values, 4));
 309372        ImportCylinderCapsuleFeature(projected.Z, CylinderCapsuleFeatureSlot(values, 5));
 309373        signs[3] = projected.X.Sign;
 309374        signs[4] = projected.Y.Sign;
 309375        signs[5] = projected.Z.Sign;
 309376        ImportCylinderCapsuleFeature(e, CylinderCapsuleFeatureSlot(values, 6));
 309377        ImportCylinderCapsuleFeature(e, CylinderCapsuleFeatureSlot(values, 9));
 309378        signs[6] = signs[9] = 1;
 379
 380        const int words = ConvexContactCandidate.Words;
 309381        Span<ulong> work = stackalloc ulong[5 * words];
 309382        Span<ulong> squared = CylinderCapsuleFeatureSlot(work, 0);
 309383        Span<ulong> product = CylinderCapsuleFeatureSlot(work, 1);
 309384        Span<ulong> first = CylinderCapsuleFeatureSlot(work, 2);
 309385        Span<ulong> second = CylinderCapsuleFeatureSlot(work, 3);
 309386        Span<ulong> third = CylinderCapsuleFeatureSlot(work, 4);
 309387        squared.Clear();
 2472388        for (int component = 3; component < 6; component++)
 389        {
 927390            Span<ulong> coordinate = CylinderCapsuleFeatureSlot(values, component);
 927391            WideArithmetic.MultiplyMagnitudes(coordinate, coordinate, product);
 927392            WideArithmetic.AddMagnitudeInto(product, squared);
 393        }
 309394        ImportCylinderCapsuleFeature(rfb, first);
 309395        WideArithmetic.MultiplyMagnitudes(first, first, product);
 309396        WideArithmetic.AddMagnitudeInto(product, squared);
 309397        WideArithmetic.MultiplyMagnitudes(squared, CylinderCapsuleFeatureSlot(values, 9),
 309398            CylinderCapsuleFeatureSlot(values, 7));
 309399        Signed832 dot = WideArithmetic.MultiplySigned576ToSigned832(
 309400            WideArithmetic.MultiplySigned576(GetMagnitude576(majorProjection), geometry.Scale), b);
 309401        ImportCylinderCapsuleFeature(dot, second);
 309402        WideArithmetic.MultiplyMagnitudes(first, second, product);
 309403        WideArithmetic.AddEqualMagnitudes(product, product, CylinderCapsuleFeatureSlot(values, 8));
 309404        signs[7] = 1;
 405        // Zero-radius cylinders use the segment feature family instead.
 309406        signs[8] = -1;
 309407        ImportCylinderCapsuleFeature(geometry.Scale, first);
 309408        ImportCylinderCapsuleFeature(b, second);
 309409        WideArithmetic.MultiplyMagnitudes(first, second, third);
 309410        WideArithmetic.MultiplyMagnitudes(third, third, product);
 309411        WideArithmetic.MultiplyMagnitudes(product, CylinderCapsuleFeatureSlot(values, 9),
 309412            CylinderCapsuleFeatureSlot(values, 10));
 309413        signs[10] = 1;
 309414        return true;
 415    }
 416
 417    private static void KeepCylinderCapsuleCandidate(
 418        ReadOnlySpan<ulong> values, ReadOnlySpan<int> signs, int gapSign,
 419        Span<ulong> bestValues, Span<int> bestSigns, ref int bestGapSign, ref bool hasBest)
 420    {
 6543421        var candidate = new ConvexContactCandidate(values, signs, gapSign);
 6543422        if (hasBest && CompareConvexContactCandidates(candidate,
 6543423                new ConvexContactCandidate(bestValues, bestSigns, bestGapSign)) >= 0)
 4096424            return;
 2447425        values.CopyTo(bestValues);
 2447426        signs.CopyTo(bestSigns);
 2447427        bestGapSign = gapSign;
 2447428        hasBest = true;
 2447429    }
 430
 431    private static void KeepCylinderCapsuleDirection(
 432        in CylinderCapsuleFeatureGeometry geometry, CylinderCapsuleDirection direction,
 433        Span<ulong> values, Span<int> signs, Span<ulong> bestValues, Span<int> bestSigns,
 434        ref int bestGapSign, ref bool hasBest)
 435    {
 2956436        if (BuildCylinderCapsuleAxisCandidate(geometry, direction, values, signs, out int gapSign))
 2952437            KeepCylinderCapsuleCandidate(values, signs, gapSign, bestValues, bestSigns, ref bestGapSign, ref hasBest);
 2956438    }
 439
 440    internal static bool TryGetCenteredFiniteCylinderCapsuleContact(
 441        Vector3d cylinderCenter, FixedQuaternion cylinderRotation, Vector3d cylinderLocalAxis,
 442        Fixed64 cylinderLength, Fixed64 cylinderRadius,
 443        Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Vector3d capsuleLocalAxis,
 444        Fixed64 capsuleLength, Fixed64 capsuleRadius, out FixedContactAnchors contact)
 445    {
 908446        if (!TryGetCenteredFiniteCylinderCapsulePenetration(
 908447                cylinderCenter, cylinderRotation, cylinderLocalAxis, Signed192.Raw(cylinderLength), cylinderRadius,
 908448                capsuleCenter, capsuleRotation, capsuleLocalAxis, capsuleLength, capsuleRadius,
 908449                out Vector3d normal, out Fixed64 depth, out bool depthIsClamped))
 450        {
 353451            contact = default;
 353452            return false;
 453        }
 555454        contact = CreateCylinderCapsuleFeatureContact(normal, depth, depthIsClamped,
 555455            cylinderCenter, cylinderRotation, cylinderLocalAxis, cylinderLength, cylinderRadius,
 555456            capsuleCenter, capsuleRotation, capsuleLocalAxis, capsuleLength, capsuleRadius);
 555457        return true;
 458    }
 459
 460    /// <summary>
 461    /// Exact closed relation for an admitted finite cylinder and capsule. The
 462    /// positive cylinder full length may occupy 64 unsigned raw bits, including
 463    /// twice a slab half-thickness; all other authored inputs retain their
 464    /// ordinary admission bounds. The cylinder-to-capsule normal and complete
 465    /// nearest-even depth are rounded only after exact feature selection.
 466    /// Separation returns default outputs; clamping reports conceptual overflow.
 467    /// </summary>
 468    internal static bool TryGetCenteredFiniteCylinderCapsulePenetration(
 469        Vector3d cylinderCenter, FixedQuaternion cylinderRotation, Vector3d cylinderLocalAxis,
 470        Signed192 cylinderLength, Fixed64 cylinderRadius,
 471        Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Vector3d capsuleLocalAxis,
 472        Fixed64 capsuleLength, Fixed64 capsuleRadius,
 473        out Vector3d normal, out Fixed64 depth, out bool depthIsClamped)
 474    {
 942475        WideOrientedBox.GetRotatedLocalAxisNumerators(cylinderRotation, cylinderLocalAxis,
 942476            out Signed192 ax, out Signed192 ay, out Signed192 az, out Signed192 ad);
 942477        WideOrientedBox.GetRotatedLocalAxisNumerators(capsuleRotation, capsuleLocalAxis,
 942478            out Signed192 bx, out Signed192 by, out Signed192 bz, out Signed192 bd);
 942479        var cylinder = new RigidAxis3(ax, ay, az, ad);
 942480        var capsule = new RigidAxis3(bx, by, bz, bd);
 481        // Full length < 2^64 keeps scaled half-axes < 2^229 and endpoint
 482        // offsets < 2^231; the existing direction/candidate widths still hold.
 942483        var geometry = new CylinderCapsuleFeatureGeometry(cylinderCenter, cylinder, cylinderLength,
 942484            cylinderRadius, capsuleCenter, capsule, Signed192.Raw(capsuleLength));
 485        const int valueCount = ConvexContactCandidate.Words * ConvexContactCandidate.Slots;
 942486        Span<ulong> values = stackalloc ulong[valueCount];
 942487        Span<int> signs = stackalloc int[ConvexContactCandidate.Slots];
 942488        Span<ulong> bestValues = stackalloc ulong[valueCount];
 942489        Span<int> bestSigns = stackalloc int[ConvexContactCandidate.Slots];
 942490        int bestGapSign = 0;
 942491        bool hasBest = false;
 492
 493        // Stable ties: cap poles, capsule-interior principal boundaries, side
 494        // endpoint directions, degenerate segment endpoints, then ellipse.
 942495        KeepCylinderCapsuleDirection(geometry, new CylinderCapsuleDirection(cylinder.ToWide()),
 942496            values, signs, bestValues, bestSigns, ref bestGapSign, ref hasBest);
 942497        int capGapSign = bestGapSign;
 942498        Axis3 major = Cross(cylinder.ToWide(), capsule.ToWide());
 942499        if (capsuleLength != Fixed64.Zero && !major.IsZero)
 500        {
 752501            Axis3 minor = Cross(capsule.ToWide(), major);
 752502            KeepCylinderCapsuleDirection(geometry, new CylinderCapsuleDirection(major),
 752503                values, signs, bestValues, bestSigns, ref bestGapSign, ref hasBest);
 752504            KeepCylinderCapsuleDirection(geometry, new CylinderCapsuleDirection(minor),
 752505                values, signs, bestValues, bestSigns, ref bestGapSign, ref hasBest);
 506        }
 507
 4646508        for (int capsuleSign = -1; capsuleSign <= 1; capsuleSign += 2)
 509        {
 1683510            CylinderCapsuleDirection side = ProjectCylinderCapsulePerpendicular(
 1683511                geometry.EndpointOffset(0, capsuleSign), cylinder);
 1683512            _ = BuildCylinderCapsuleAxisCandidate(geometry,
 1683513                side.IsZero ? GetCylinderCapsulePerpendicular(cylinderRotation, cylinderLocalAxis) : side,
 1683514                values, signs, out int sideGapSign);
 1683515            KeepCylinderCapsuleCandidate(values, signs, sideGapSign,
 1683516                bestValues, bestSigns, ref bestGapSign, ref hasBest);
 1683517            if ((major.IsZero || capsuleLength == Fixed64.Zero)
 1683518                && (capGapSign >= 0 || sideGapSign >= 0))
 519            {
 520                // Parallel cores sum to one finite cylinder. If the query is
 521                // inside either its axial slab or radial cylinder, the nearest
 522                // boundary is a cap or side; a rim cannot improve either gap.
 163523                return MaterializeCylinderCapsulePenetration(
 163524                    new ConvexContactCandidate(bestValues, bestSigns, bestGapSign), capsuleRadius,
 163525                    out normal, out depth, out depthIsClamped);
 526            }
 8678527            for (int cylinderSign = -1; cylinderSign <= 1; cylinderSign += 2)
 528            {
 2958529                if (BuildCylinderCapsuleEndpointRimCandidate(geometry, cylinderSign, capsuleSign,
 2958530                        values, signs, out int rimGapSign))
 531                {
 532                    // The support point plus its outward residual proves a
 533                    // closest point of the whole convex Minkowski sum. Unlike
 534                    // an arbitrary separating direction, this is a global MTD.
 139535                    return MaterializeCylinderCapsulePenetration(
 139536                        new ConvexContactCandidate(values, signs, rimGapSign), capsuleRadius,
 139537                        out normal, out depth, out depthIsClamped);
 538                }
 2819539                if (cylinderRadius == Fixed64.Zero)
 540                {
 384541                    KeepCylinderCapsuleDirection(geometry,
 384542                        new CylinderCapsuleDirection(geometry.EndpointOffset(cylinderSign, capsuleSign)),
 384543                        values, signs, bestValues, bestSigns, ref bestGapSign, ref hasBest);
 544                }
 545            }
 546            // A zero-length core already returned through the cap/side
 547            // certificate or its unique outward endpoint/rim residual.
 548        }
 549
 640550        if (capsuleLength != Fixed64.Zero && !major.IsZero)
 551        {
 640552            if (cylinderRadius == Fixed64.Zero)
 553            {
 378554                for (int cylinderSign = -1; cylinderSign <= 1; cylinderSign += 2)
 126555                    KeepCylinderCapsuleDirection(geometry,
 126556                        ProjectCylinderCapsulePerpendicular(geometry.EndpointOffset(cylinderSign, 0), capsule),
 126557                        values, signs, bestValues, bestSigns, ref bestGapSign, ref hasBest);
 558            }
 577559            else if (GetAxisProjection(cylinder.ToWide(), capsule).IsZero)
 560            {
 1452561                for (int cylinderSign = -1; cylinderSign <= 1; cylinderSign += 2)
 484562                    if (BuildCylinderCapsuleProjectedCornerCandidate(geometry, major, cylinderSign,
 484563                            values, signs, out int cornerGapSign))
 12564                        KeepCylinderCapsuleCandidate(values, signs, cornerGapSign,
 12565                            bestValues, bestSigns, ref bestGapSign, ref hasBest);
 566            }
 335567            else if (TryPrepareCylinderCapsuleEllipse(cylinderCenter, cylinder, cylinderLength,
 335568                    cylinderRadius, capsuleCenter, capsule, out CylinderCapsuleEllipse ellipse)
 335569                && TryImproveCylinderCapsuleEllipse(ellipse,
 335570                    new ConvexContactCandidate(bestValues, bestSigns, bestGapSign), cylinderRadius, capsuleRadius,
 335571                    out bool intersects, out normal, out depth, out depthIsClamped))
 572            {
 149573                return intersects;
 574            }
 575        }
 491576        return MaterializeCylinderCapsulePenetration(
 491577            new ConvexContactCandidate(bestValues, bestSigns, bestGapSign), capsuleRadius,
 491578            out normal, out depth, out depthIsClamped);
 579    }
 580
 581    private static bool MaterializeCylinderCapsulePenetration(
 582        in ConvexContactCandidate candidate, Fixed64 capsuleRadius,
 583        out Vector3d normal, out Fixed64 depth, out bool depthIsClamped)
 584    {
 840585        normal = default;
 840586        depth = default;
 840587        depthIsClamped = false;
 840588        int depthSign = CompareConvexContactCandidateDepthToTwiceRaw(candidate, capsuleRadius, default);
 840589        if (depthSign < 0)
 282590            return false;
 558591        if (depthSign > 0)
 519592            GetRoundedConvexContactCandidateDepth(candidate, capsuleRadius, out depth, out depthIsClamped);
 558593        normal = GetConvexContactCandidateNormal(candidate);
 558594        return true;
 595    }
 596
 597    private static FixedContactAnchors CreateCylinderCapsuleFeatureContact(
 598        Vector3d normal, Fixed64 depth, bool depthIsClamped,
 599        Vector3d cylinderCenter, FixedQuaternion cylinderRotation, Vector3d cylinderLocalAxis,
 600        Fixed64 cylinderLength, Fixed64 cylinderRadius,
 601        Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Vector3d capsuleLocalAxis,
 555602        Fixed64 capsuleLength, Fixed64 capsuleRadius) => new(
 555603            WideGeometry.GetCenteredCylinderSupportAnchor(cylinderCenter, cylinderRotation, cylinderLocalAxis,
 555604                cylinderLength, cylinderRadius, normal),
 555605            WideGeometry.GetCenteredCapsuleSupportAnchor(capsuleCenter, capsuleRotation, capsuleLocalAxis,
 555606                capsuleLength, capsuleRadius, -normal), normal, depth, depthIsClamped);
 607}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderCylinderPenetration.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CylinderCylinderPenetration.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.ContactQuadratic;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>CylinderCylinderPenetration declaration for WideConvexPrismRelations.</content>
 14internal static partial class WideConvexPrismRelations
 15{
 16    internal readonly struct CylinderCylinderPenetration
 17    {
 18        internal readonly Axis3 Axis;
 19        internal readonly bool Negate;
 20        internal readonly CylinderPairDepth Depth;
 21
 22        internal CylinderCylinderPenetration(
 23            Axis3 axis,
 24            bool negate,
 25            CylinderPairDepth depth)
 26        {
 30227            Axis = axis;
 30228            Negate = negate;
 30229            Depth = depth;
 30230            HasValue = true;
 30231        }
 32
 33        internal bool HasValue { get; }
 34    }
 35}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderPairContact.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CylinderPairContact.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 System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>Complete nonparallel cylinder support-feature selection.</content>
 14internal static partial class WideConvexPrismRelations
 15{
 16    private const int CylinderPairValueWords = CylinderPairRimFeatures.CoefficientWords;
 17    private const int CylinderPairCellWords = (16 * (CylinderPairValueWords * 64 + 11) + 255) / 64;
 18
 19    private static bool TryGetNonparallelCylinderPairPenetration(
 20        Vector3d firstCenter, FixedQuaternion firstRotation, Vector3d firstLocalAxis,
 21        Signed192 firstLength, Fixed64 firstRadius,
 22        Vector3d secondCenter, FixedQuaternion secondRotation, Vector3d secondLocalAxis,
 23        Signed192 secondLength, Fixed64 secondRadius,
 24        out Vector3d normal, out Fixed64 depth, out bool depthIsClamped)
 25    {
 40926        normal = default;
 40927        depth = default;
 40928        depthIsClamped = false;
 40929        var geometry = new CylinderPairGeometry(firstCenter, firstRotation, firstLocalAxis,
 40930            firstLength, firstRadius, secondCenter, secondRotation, secondLocalAxis, secondLength, secondRadius);
 40931        Span<ulong> bestAnalyticValues = stackalloc ulong[ConvexContactCandidate.Words * ConvexContactCandidate.Slots];
 40932        Span<int> bestAnalyticSigns = stackalloc int[ConvexContactCandidate.Slots];
 40933        Span<ulong> bestValues = stackalloc ulong[9 * CylinderPairValueWords];
 40934        Span<sbyte> bestSigns = stackalloc sbyte[9];
 40935        Span<ulong> bestCell = stackalloc ulong[CylinderPairCellWords];
 40936        var best = new CylinderPairSelection(bestValues, bestSigns, bestCell, false);
 40937        if (!TrySelectCylinderPairFeature(firstCenter, firstRotation, firstLocalAxis, firstLength, firstRadius,
 40938                secondCenter, secondRotation, secondLocalAxis, secondLength, secondRadius, geometry,
 40939                bestAnalyticValues, bestAnalyticSigns, out int analyticGapSign, ref best))
 9740            return false;
 31241        var analytic = new ConvexContactCandidate(bestAnalyticValues, bestAnalyticSigns, analyticGapSign);
 31242        if (!best.HasRoot && !best.NormalReady)
 43        {
 14044            normal = GetConvexContactCandidateNormal(analytic);
 14045            GetRoundedConvexContactCandidateDepth(analytic, Fixed64.Zero, out depth, out depthIsClamped);
 46        }
 47        else
 48        {
 17249            normal = best.NormalReady ? best.Normal : CylinderPairRimFeatures.GetSimpleNormal(
 17250                geometry, best.FirstSign, best.SecondSign, ref best.Root, best.SlopeSign, 1);
 17251            depth = Fixed64.Zero;
 17252            if (!best.IsZero)
 16953                ConvexContactValueRoot.GetRoundedDepth(geometry.RawScale, geometry.ValueShift, ref best.Root, out depth,
 54        }
 31255        return true;
 56    }
 57
 58    // Release all transient candidate buffers before the selected algebraic
 59    // normal is rounded. Its certified sign queries own a separate large arena.
 60    [MethodImpl(MethodImplOptions.NoInlining)]
 61    private static bool TrySelectCylinderPairFeature(
 62        Vector3d firstCenter, FixedQuaternion firstRotation, Vector3d firstLocalAxis,
 63        Signed192 firstLength, Fixed64 firstRadius,
 64        Vector3d secondCenter, FixedQuaternion secondRotation, Vector3d secondLocalAxis,
 65        Signed192 secondLength, Fixed64 secondRadius, in CylinderPairGeometry geometry,
 66        Span<ulong> bestAnalyticValues, Span<int> bestAnalyticSigns, out int analyticGapSign,
 67        ref CylinderPairSelection best)
 68    {
 40969        Span<ulong> analyticValues = stackalloc ulong[ConvexContactCandidate.Words * ConvexContactCandidate.Slots];
 40970        Span<int> analyticSigns = stackalloc int[ConvexContactCandidate.Slots];
 40971        analyticGapSign = 1;
 291672        for (int axis = 0; axis < 3; axis++)
 73        {
 113474            CylinderPairAnalyticFeatures.Build(geometry, axis, analyticValues, analyticSigns, out int gapSign);
 113475            if (gapSign < 0)
 8576                return false;
 104977            var candidate = new ConvexContactCandidate(analyticValues, analyticSigns, gapSign);
 104978            if (axis != 0 && CompareConvexContactCandidates(candidate,
 104979                    new ConvexContactCandidate(bestAnalyticValues, bestAnalyticSigns, analyticGapSign)) >= 0)
 80                continue;
 51181            analyticValues.CopyTo(bestAnalyticValues);
 51182            analyticSigns.CopyTo(bestAnalyticSigns);
 51183            analyticGapSign = gapSign;
 84        }
 32485        var analytic = new ConvexContactCandidate(bestAnalyticValues, bestAnalyticSigns, analyticGapSign);
 32486        best.IsZero = analyticGapSign == 0;
 32487        Span<ulong> values = stackalloc ulong[9 * CylinderPairValueWords];
 32488        Span<sbyte> signs = stackalloc sbyte[9];
 32489        Span<ulong> cell = stackalloc ulong[CylinderPairCellWords];
 90
 91        // Stable exact ties: cap axes, their cross, first/second side planes,
 92        // then cap pairs in ascending sign order and their root/branch order.
 191493        for (int side = 0; side < 2; side++)
 94        {
 64395            if (!TryGetCylinderPairSideFeature(firstCenter, firstRotation, firstLocalAxis, firstLength, firstRadius,
 64396                    secondCenter, secondRotation, secondLocalAxis, secondLength, secondRadius,
 64397                    geometry, side == 0, values, signs, cell, out FiniteAxisValueRoot sideRoot,
 64398                    out Vector3d sideNormal, out int sideGapSign))
 99                continue;
 478100            if (sideGapSign < 0)
 10101                return false;
 468102            if (sideGapSign == 0)
 2103                best.KeepTouch(sideNormal);
 466104            else if (ShouldKeepCylinderPairValue(geometry, ref sideRoot, analytic, best))
 169105                best.KeepRoot(sideRoot, sideNormal);
 106        }
 1872107        for (int firstSign = -1; firstSign <= 1; firstSign += 2)
 108        {
 3750109            for (int secondSign = -1; secondSign <= 1; secondSign += 2)
 110            {
 1253111                CylinderPairRimFeatures.BuildValues(geometry, firstSign, secondSign, values, signs);
 1253112                if (signs[0] == 0)
 106113                    for (int branch = 0; branch < 2; branch++)
 37114                        if (CylinderPairRepeatedRim.TryGetZeroNormal(geometry, firstSign, secondSign, branch, out Vector
 115                        {
 3116                            best.KeepTouch(zeroNormal);
 117                            // The admitted common rim point proves intersection;
 118                            // its supporting normal proves the global gap is zero.
 3119                            return true;
 120                        }
 1250121                if (!TryKeepCylinderPairCapValues(geometry, firstSign, secondSign, values, signs, cell, analytic, ref be
 2122                    return false;
 123            }
 124        }
 309125        return true;
 126    }
 127
 128    // Four admission polynomials (33,408 bytes plus signs) survive only one
 129    // cap pair. Their construction frames return before sign queries, and this
 130    // whole frame returns before final normal materialization. A ready bit
 131    // avoids constructing cap derivatives when earlier predicates reject.
 132    [MethodImpl(MethodImplOptions.NoInlining)]
 133    private static bool TryKeepCylinderPairCapValues(in CylinderPairGeometry geometry,
 134        int firstSign, int secondSign, scoped Span<ulong> values, scoped Span<sbyte> signs,
 135        scoped Span<ulong> cell, scoped ConvexContactCandidate analytic, ref CylinderPairSelection best)
 136    {
 1250137        Span<ulong> derivatives = stackalloc ulong[CylinderPairRimFeatures.AdmissionCoefficientCount * CylinderPairValue
 1250138        Span<sbyte> derivativeSigns = stackalloc sbyte[CylinderPairRimFeatures.AdmissionCoefficientCount];
 1250139        int readyMask = 0;
 1250140        Span<ulong> factor = stackalloc ulong[5 * CylinderPairValueWords];
 1250141        Span<sbyte> factorSigns = stackalloc sbyte[5];
 142        // Deeply clustered roots retain the original exact fallback instead
 143        // of carrying eight full-size cells into admission and sign queries.
 1250144        Span<ulong> batchCells = stackalloc ulong[8 * 8];
 1250145        Span<int> batchShifts = stackalloc int[8];
 1250146        FiniteAxisValueRoots roots = WideFiniteAxisIntersection.GetFiniteValueRoots(
 1250147            values, signs, batchCells, batchShifts);
 1250148        if (roots.HasRepeatedRoots)
 487149            WideFiniteAxisIntersection.GetFiniteValueRepeatedRootFactor(values, signs, factor, factorSigns);
 12684150        for (int ordinal = 0; ordinal < roots.Count; ordinal++)
 151        {
 5094152            scoped FiniteAxisValueRoot root = FiniteAxisValueRoots.GetRoot(roots, ordinal, values, signs, cell);
 5094153            if (roots.HasRepeatedRoots && TryHandleRepeatedCylinderPairValue(
 5094154                    geometry, firstSign, secondSign, root, factor, factorSigns, analytic, ref best))
 155                continue;
 4343156            int slope = CylinderPairRimFeatures.GetSimpleValueSlopeSign(root);
 4343157            if (!CylinderPairRimFeatures.TryAdmitSimple(geometry, firstSign, secondSign, ref root, slope,
 4343158                    derivatives, derivativeSigns, ref readyMask, out int gapSign))
 159                continue;
 823160            if (gapSign < 0)
 2161                return false;
 821162            if (ShouldKeepCylinderPairValue(geometry, ref root, analytic, best))
 139163                best.KeepSimpleRoot(root, firstSign, secondSign, slope);
 164        }
 1248165        return true;
 166    }
 167
 168    private static bool ShouldKeepCylinderPairValue(in CylinderPairGeometry geometry, scoped ref FiniteAxisValueRoot roo
 2038169        ConvexContactCandidate analytic, CylinderPairSelection best) => !best.IsZero && (best.HasRoot
 2038170            ? WideFiniteAxisIntersection.CompareFiniteValueRoots(root, best.Root) < 0
 2038171            : ConvexContactValueRoot.CompareRootSquared(geometry.RawScale, geometry.ValueShift, ref root, analytic) < 0)
 172
 173    [MethodImpl(MethodImplOptions.NoInlining)]
 174    private static bool TryHandleRepeatedCylinderPairValue(in CylinderPairGeometry geometry,
 175        int firstSign, int secondSign, scoped FiniteAxisValueRoot source,
 176        scoped ReadOnlySpan<ulong> factor, scoped ReadOnlySpan<sbyte> factorSigns, scoped ConvexContactCandidate analyti
 177        ref CylinderPairSelection best)
 178    {
 179        // Each recovery family proves that admitted nonzero radial signs are
 180        // positive: negative signs require a unique, nondegenerate closest
 181        // disk pair, excluded by a rank-one pencil.
 182        // True classifies source as repeated even if no branch is admitted.
 1614183        Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords(factor, factorSigns)]
 1614184        Span<ulong> upper = stackalloc ulong[source.LowerNumerator.Length];
 1614185        source.LowerNumerator.CopyTo(upper);
 1614186        WideArithmetic.AddWord(upper, 0, 1);
 4621187        for (int ordinal = 0; WideFiniteAxisIntersection.TryGetFiniteValueRoot(factor, factorSigns,
 4400188            ordinal, cell, out FiniteAxisValueRoot root); ordinal++)
 189        {
 2144190            int lowerComparison = WideFiniteAxisIntersection.CompareFiniteValueRootToDyadic(
 2144191                root, source.LowerNumerator, source.DenominatorShift);
 2144192            if (source.IsRational ? lowerComparison != 0 : lowerComparison <= 0
 2144193                || WideFiniteAxisIntersection.CompareFiniteValueRootToDyadic(root, upper, source.DenominatorShift) >= 0)
 194                continue;
 195            // Repeated families cannot separate. Once this value cannot
 196            // improve, neither admission nor another equal-depth branch matters.
 751197            if (!ShouldKeepCylinderPairValue(geometry, ref root, analytic, best))
 607198                return true;
 2562199            for (int branch = 0; branch < 8; branch++)
 200            {
 1141201                if (!CylinderPairRepeatedRim.TryGetRankOneNormal(geometry, firstSign, secondSign, root,
 1141202                        branch, out Vector3d normal))
 203                    continue;
 4204                best.KeepRoot(root, normal);
 4205                return true;
 206            }
 207            // H=0 stationary normals cannot improve: an interior minimum
 208            // requires orthogonal rim tangents, for which a cap-axis gap is
 209            // strictly smaller. Boundary normals belong to the side planes.
 210            // A rank-two triple pencil crosses its stationary distance with
 211            // odd (cubic) order along the first rim. Its admitted point cannot
 212            // be an interior support minimum; cap boundaries are side cases.
 140213            return true; // The repeated factor has exactly one root in source's cell.
 214        }
 863215        return false;
 216    }
 217
 218    private ref struct CylinderPairSelection
 219    {
 220        private readonly Span<ulong> _values;
 221        private readonly Span<sbyte> _signs;
 222        private readonly Span<ulong> _cell;
 223        internal FiniteAxisValueRoot Root;
 224        internal Vector3d Normal;
 225        internal bool HasRoot;
 226        internal bool NormalReady;
 227        internal bool IsZero;
 228        internal int FirstSign;
 229        internal int SecondSign;
 230        internal int SlopeSign;
 231
 232        internal CylinderPairSelection(Span<ulong> values, Span<sbyte> signs, Span<ulong> cell, bool zero)
 233        {
 409234            this = default;
 1636235            _values = values; _signs = signs; _cell = cell; IsZero = zero;
 409236        }
 237
 238        internal void KeepTouch(Vector3d normal)
 239        {
 5240            if (IsZero)
 2241                return;
 3242            IsZero = true;
 3243            HasRoot = false;
 3244            NormalReady = true;
 3245            Normal = normal;
 3246        }
 247
 248        internal void KeepRoot(scoped FiniteAxisValueRoot root, Vector3d normal)
 249        {
 312250            Root = FiniteAxisValueRoot.CopyTo(root, _values, _signs, _cell);
 312251            HasRoot = NormalReady = true;
 312252            Normal = normal;
 312253        }
 254
 255        internal void KeepSimpleRoot(scoped FiniteAxisValueRoot root, int firstSign, int secondSign, int slope)
 256        {
 139257            KeepRoot(root, default);
 139258            NormalReady = false;
 417259            FirstSign = firstSign; SecondSign = secondSign; SlopeSign = slope;
 139260        }
 261    }
 262}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderPairDepth.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CylinderPairDepth.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.ContactQuadratic;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>CylinderPairDepth declaration for WideConvexPrismRelations.</content>
 14internal static partial class WideConvexPrismRelations
 15{
 16    /// <summary>
 17    /// Parallel cap/perpendicular depth R / (C sqrt(A)) + radiusRaw.
 18    /// Authored axes need not have an exact unit squared length.
 19    /// </summary>
 20    internal readonly struct CylinderPairDepth
 21    {
 22        internal readonly Signed704 Rational;
 23        internal readonly Signed192 Common;
 24        internal readonly Signed576 AxisSquared;
 25        internal readonly ulong RadiusRaw;
 26
 27        internal CylinderPairDepth(
 28            Signed704 rational,
 29            Signed192 common,
 30            Signed576 axisSquared,
 31            ulong radiusRaw)
 32        {
 44333            Rational = rational;
 44334            Common = common;
 44335            AxisSquared = axisSquared;
 44336            RadiusRaw = radiusRaw;
 44337        }
 38
 102139        internal Signed320 RadialCoefficient => WideArithmetic.MultiplySigned192(
 102140            Common, new Signed192(0UL, 0UL, RadiusRaw));
 41    }
 42}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.CylinderPairSideFeatures.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.CylinderPairSideFeatures.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 System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>
 14/// Exact projected side-feature selection for cylinder-pair contacts.
 15/// </content>
 16internal static partial class WideConvexPrismRelations
 17{
 18    /// <summary>
 19    /// Selects the existing projected-cylinder minimum on one side-normal
 20    /// plane. A common-perpendicular winner remains with the earlier global
 21    /// analytic family. Positive full gaps return their exact value root;
 22    /// negative gaps and exact touches return their sign and normal directly.
 23    /// </summary>
 24    [MethodImpl(MethodImplOptions.NoInlining)]
 25    internal static bool TryGetCylinderPairSideFeature(
 26        Vector3d firstCenter, FixedQuaternion firstRotation, Vector3d firstLocalAxis,
 27        Signed192 firstLength, Fixed64 firstRadius,
 28        Vector3d secondCenter, FixedQuaternion secondRotation, Vector3d secondLocalAxis,
 29        Signed192 secondLength, Fixed64 secondRadius,
 30        in CylinderPairGeometry pairGeometry, bool firstSide,
 31        Span<ulong> valueCoefficients, Span<sbyte> valueSigns, Span<ulong> valueCell,
 32        out FiniteAxisValueRoot valueRoot, out Vector3d normal, out int gapSign)
 33    {
 65634        valueRoot = default;
 65635        normal = default;
 65636        gapSign = 0;
 65637        Vector3d cylinderCenter = firstSide ? secondCenter : firstCenter;
 65638        FixedQuaternion cylinderRotation = firstSide ? secondRotation : firstRotation;
 65639        Vector3d cylinderLocalAxis = firstSide ? secondLocalAxis : firstLocalAxis;
 65640        Signed192 cylinderLength = firstSide ? secondLength : firstLength;
 65641        Fixed64 cylinderRadius = firstSide ? secondRadius : firstRadius;
 65642        Vector3d capsuleCenter = firstSide ? firstCenter : secondCenter;
 65643        FixedQuaternion capsuleRotation = firstSide ? firstRotation : secondRotation;
 65644        Vector3d capsuleLocalAxis = firstSide ? firstLocalAxis : secondLocalAxis;
 65645        Signed192 capsuleLength = firstSide ? firstLength : secondLength;
 65646        Fixed64 radiusOffset = firstSide ? firstRadius : secondRadius;
 65647        WideOrientedBox.GetRotatedLocalAxisNumerators(cylinderRotation, cylinderLocalAxis,
 65648            out Signed192 ax, out Signed192 ay, out Signed192 az, out Signed192 ad);
 65649        WideOrientedBox.GetRotatedLocalAxisNumerators(capsuleRotation, capsuleLocalAxis,
 65650            out Signed192 bx, out Signed192 by, out Signed192 bz, out Signed192 bd);
 65651        var cylinder = new RigidAxis3(ax, ay, az, ad);
 65652        var capsule = new RigidAxis3(bx, by, bz, bd);
 65653        Axis3 major = Cross(cylinder.ToWide(), capsule.ToWide());
 54        // The pair owner already proved these exact axes are nonparallel.
 65655        var geometry = new CylinderCapsuleFeatureGeometry(cylinderCenter, cylinder, cylinderLength,
 65656            cylinderRadius, capsuleCenter, capsule, capsuleLength);
 57        const int candidateWords = ConvexContactCandidate.Words;
 65658        Span<ulong> values = stackalloc ulong[ConvexContactCandidate.Slots * candidateWords];
 65659        Span<int> candidateSigns = stackalloc int[ConvexContactCandidate.Slots];
 65660        Span<ulong> best = stackalloc ulong[ConvexContactCandidate.Slots * candidateWords];
 65661        Span<int> bestSigns = stackalloc int[ConvexContactCandidate.Slots];
 65662        BuildCylinderCapsuleAxisCandidate(geometry, new CylinderCapsuleDirection(major),
 65663            best, bestSigns, out int bestGapSign);
 65664        bool majorWins = true;
 65665        int capSign = 1;
 65666        Axis3 minor = Cross(capsule.ToWide(), major);
 65667        BuildCylinderCapsuleAxisCandidate(geometry, new CylinderCapsuleDirection(minor),
 65668            values, candidateSigns, out int candidateGapSign);
 65669        if (CompareConvexContactCandidates(new ConvexContactCandidate(values, candidateSigns, candidateGapSign),
 65670                new ConvexContactCandidate(best, bestSigns, bestGapSign)) < 0)
 71        {
 87472            values.CopyTo(best); candidateSigns.CopyTo(bestSigns);
 43773            bestGapSign = candidateGapSign;
 43774            majorWins = false;
 43775            int orientation = DotCylinderCapsuleDirection(new CylinderCapsuleDirection(minor),
 43776                geometry.DifferenceX, geometry.DifferenceY, geometry.DifferenceZ).Sign < 0 ? -1 : 1;
 43777            capSign = GetAxisProjection(minor, cylinder).Sign * orientation;
 78        }
 79
 65680        bool perpendicular = GetAxisProjection(cylinder.ToWide(), capsule).IsZero;
 65681        if (perpendicular)
 82        {
 283283            for (int sign = -1; sign <= 1; sign += 2)
 84            {
 94485                if (!BuildCylinderCapsuleProjectedCornerCandidate(geometry, major, sign,
 94486                        values, candidateSigns, out candidateGapSign))
 87                    continue;
 88                // Admission proves both rectangle coordinate excesses >0.
 89                // The unique outward corner gap -sqrt(x²+y²) strictly beats
 90                // both principal gaps -x and -y; the other cap cannot admit.
 59491                values.CopyTo(best); candidateSigns.CopyTo(bestSigns);
 29792                bestGapSign = candidateGapSign;
 29793                majorWins = false;
 29794                capSign = sign;
 95            }
 96        }
 97
 98        const int ellipseWords = CylinderCapsuleEllipseWords;
 65699        Span<ulong> stationary = stackalloc ulong[5 * ellipseWords];
 656100        Span<sbyte> stationarySigns = stackalloc sbyte[5];
 656101        Span<ulong> signedGap = stackalloc ulong[3 * ellipseWords];
 656102        Span<sbyte> signedGapSigns = stackalloc sbyte[3];
 656103        Span<ulong> squaredGap = stackalloc ulong[10 * ellipseWords];
 656104        Span<sbyte> squaredGapSigns = stackalloc sbyte[10];
 656105        Span<ulong> numerator = squaredGap[..(5 * ellipseWords)];
 656106        Span<ulong> denominator = squaredGap[(5 * ellipseWords)..];
 1312107        Span<sbyte> numeratorSigns = squaredGapSigns[..5], denominatorSigns = squaredGapSigns[5..];
 108        // The old quartic's proven maximum coefficient height is 1818 bits.
 656109        Span<ulong> stationaryCell = stackalloc ulong[(9 * 1818 + 255) / 64];
 656110        scoped FiniteAxisPolynomialRoot stationaryRoot = default;
 656111        CylinderCapsuleEllipse ellipse = default;
 656112        bool ellipseWins = false;
 656113        if (!perpendicular && TryPrepareCylinderCapsuleEllipse(cylinderCenter, cylinder, cylinderLength,
 656114            cylinderRadius, capsuleCenter, capsule, out ellipse))
 115        {
 169116            BuildCylinderCapsuleEllipsePolynomials(ellipse, stationary, stationarySigns,
 169117                signedGap, signedGapSigns, numerator, numeratorSigns, denominator, denominatorSigns);
 169118            WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(stationary, stationarySigns);
 169119            WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(signedGap, signedGapSigns);
 169120            WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(squaredGap, squaredGapSigns);
 169121            if (WideFiniteAxisIntersection.TryGetLargestPositiveFiniteAxisRoot(stationary, stationarySigns,
 169122                stationaryCell, out stationaryRoot))
 123            {
 108124                int ellipseGapSign = WideFiniteAxisIntersection.GetSignAtFiniteAxisRoot(
 108125                    ref stationaryRoot, signedGap, signedGapSigns);
 108126                int comparison = ellipseGapSign.CompareTo(bestGapSign);
 127                // A zero smooth gap has one non-principal supporting normal;
 128                // neither principal-axis competitor can tie it at zero.
 108129                if (comparison == 0)
 103130                    comparison = ellipseGapSign * CompareCylinderCapsuleEllipseSquaredGap(ref stationaryRoot,
 103131                        numerator, numeratorSigns, denominator, denominatorSigns,
 103132                        new ConvexContactCandidate(best, bestSigns, bestGapSign));
 108133                if (comparison < 0)
 134                {
 103135                    ellipseWins = true;
 103136                    majorWins = false;
 103137                    bestGapSign = ellipseGapSign;
 103138                    capSign = GetAxisProjection(ellipse.Minor, cylinder).Sign;
 139                }
 140            }
 141        }
 656142        if (majorWins)
 167143            return false;
 144
 489145        var analytic = new ConvexContactCandidate(best, bestSigns, bestGapSign);
 489146        if (ellipseWins)
 147        {
 103148            gapSign = CompareCylinderCapsuleEllipseDepthToTwiceRaw(ref stationaryRoot,
 103149                numerator, numeratorSigns, denominator, denominatorSigns, bestGapSign, radiusOffset, 0UL);
 103150            normal = new Vector3d(GetRoundedCylinderCapsuleEllipseNormalComponent(ref stationaryRoot, ellipse, 0),
 103151                GetRoundedCylinderCapsuleEllipseNormalComponent(ref stationaryRoot, ellipse, 1),
 103152                GetRoundedCylinderCapsuleEllipseNormalComponent(ref stationaryRoot, ellipse, 2));
 153        }
 154        else
 155        {
 386156            gapSign = CompareConvexContactCandidateDepthToTwiceRaw(analytic, radiusOffset, default);
 386157            normal = GetConvexContactCandidateNormal(analytic);
 158        }
 489159        if (firstSide)
 160        {
 240161            normal = -normal;
 240162            capSign = -capSign;
 163        }
 489164        if (gapSign <= 0)
 16165            return true;
 166
 473167        int words = valueCoefficients.Length / 9;
 473168        Signed320 sideRadius = firstSide ? pairGeometry.FirstRadius : pairGeometry.SecondRadius;
 473169        Span<ulong> radiusSquared = stackalloc ulong[9];
 473170        WideArithmetic.GetMagnitude(WideArithmetic.MultiplySigned320(sideRadius, sideRadius), radiusSquared);
 473171        if (bestGapSign == 0)
 172        {
 16173            valueCoefficients.Clear(); valueSigns.Clear();
 8174            radiusSquared.CopyTo(valueCoefficients);
 8175            valueSigns[0] = -1;
 8176            valueCoefficients[words] = 1;
 8177            valueSigns[1] = 1;
 8178            WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(valueCoefficients, 9, pairGeometry.ValueShift);
 179            // The positive radius square is the unique root of this linear
 180            // polynomial, and the geometry scale places it in (0,1].
 8181            valueRoot = WideFiniteAxisIntersection.GetKnownFiniteValueRoot(
 8182                valueCoefficients[..(2 * words)], valueSigns[..2], 0, 1, valueCell);
 8183            return true;
 184        }
 185
 465186        Span<ulong> unshifted = stackalloc ulong[5 * words];
 465187        Span<sbyte> unshiftedSigns = stackalloc sbyte[5];
 465188        CylinderPairSideValuePolynomial.Build(pairGeometry, firstSide, capSign,
 465189            valueCoefficients, valueSigns, unshifted, unshiftedSigns);
 465190        WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(unshifted, 5, pairGeometry.ValueShift);
 465191        WideFiniteAxisIntersection.ScaleFiniteAxisPolynomialVariable(valueCoefficients, 9, pairGeometry.ValueShift);
 465192        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(unshifted, unshiftedSigns);
 465193        WideFiniteAxisIntersection.NormalizeFiniteAxisPolynomialPowerOfTwo(valueCoefficients, valueSigns);
 465194        Span<ulong> unshiftedCell = stackalloc ulong[
 465195            WideFiniteAxisIntersection.GetFiniteValueRootCellWords(unshifted, unshiftedSigns)];
 465196        scoped FiniteAxisValueRoot unshiftedRoot = GetKnownCylinderPairSideUnshiftedRoot(
 465197                pairGeometry, unshifted, unshiftedSigns, unshiftedCell,
 465198                ellipseWins, ref stationaryRoot, numerator, numeratorSigns, denominator, denominatorSigns,
 465199                analytic);
 200        // G(S)=F((sqrt(S)-r)^2)F((sqrt(S)+r)^2). The selected nonzero
 201        // h satisfies F(h²)=0, hence the admitted positive (r+h)² is a known
 202        // G-root. This is membership construction, not a candidate rejection.
 465203        valueRoot = WideFiniteAxisIntersection.MapKnownFiniteValueRootRadicalOffset(unshiftedRoot, bestGapSign,
 465204                radiusSquared, pairGeometry.ValueShift, valueCoefficients, valueSigns, valueCell);
 465205        return true;
 206    }
 207
 208    [MethodImpl(MethodImplOptions.NoInlining)]
 209    private static FiniteAxisValueRoot GetKnownCylinderPairSideUnshiftedRoot(in CylinderPairGeometry geometry,
 210        ReadOnlySpan<ulong> coefficients, ReadOnlySpan<sbyte> signs, Span<ulong> cell,
 211        bool ellipse, scoped ref FiniteAxisPolynomialRoot stationaryRoot,
 212        scoped ReadOnlySpan<ulong> numerator, scoped ReadOnlySpan<sbyte> numeratorSigns,
 213        scoped ReadOnlySpan<ulong> denominator, scoped ReadOnlySpan<sbyte> denominatorSigns,
 214        scoped ConvexContactCandidate analytic)
 215    {
 216        // Smooth stationary ellipse values satisfy F(h²)=0. A winning minor
 217        // axis is stationary (its major projection is zero); when the axes are
 218        // perpendicular, F=m²(l²+4H*rho*m) also includes both line endpoints
 219        // and the minor-axis value. The earlier major-axis family was excluded.
 465220        Span<ulong> batchCells = stackalloc ulong[64];
 465221        Span<int> batchShifts = stackalloc int[8];
 465222        FiniteAxisValueRoots roots = WideFiniteAxisIntersection.GetFiniteValueRoots(
 465223            coefficients, signs, batchCells, batchShifts);
 465224        Span<ulong> upper = stackalloc ulong[cell.Length];
 225        // A winning smooth support minimum also locally minimizes ellipse
 226        // distance: D'=2*rho*h', with positive curvature radius rho. The
 227        // noncircular ellipse has a strictly farther stationary point. A
 228        // winning principal minor or finite-segment endpoint likewise has a
 229        // farther endpoint. That distance is another physical F-root, so the
 230        // greatest root cannot be the selected h². It must not be confused
 231        // with the smallest root: an inadmissible line root may precede it.
 232        System.Diagnostics.Debug.Assert(roots.Count >= 2);
 465233        int lastEligibleOrdinal = roots.Count - 2;
 1522234        for (int ordinal = 0; ordinal < lastEligibleOrdinal; ordinal++)
 235        {
 394236            FiniteAxisValueRoot candidate = FiniteAxisValueRoots.GetRoot(roots, ordinal, coefficients, signs, cell);
 394237            if (candidate.IsRational)
 238            {
 285239                if (CompareCylinderPairSideUnshiftedEndpoint(geometry, ellipse, ref stationaryRoot,
 285240                    numerator, numeratorSigns, denominator, denominatorSigns, analytic,
 561241                    cell, candidate.DenominatorShift) != 0) continue;
 242            }
 243            else
 244            {
 245                // Earlier ordinals have already been rejected, so this root
 246                // is at most the selected h² and its open lower endpoint is
 247                // strictly smaller. Only the upper endpoint can reject it.
 248#if DEBUG
 249                System.Diagnostics.Debug.Assert(CompareCylinderPairSideUnshiftedEndpoint(geometry, ellipse,
 250                    ref stationaryRoot, numerator, numeratorSigns, denominator, denominatorSigns, analytic,
 251                    cell, candidate.DenominatorShift) > 0);
 252#endif
 109253                cell.CopyTo(upper);
 109254                WideArithmetic.AddWord(upper, 0, 1);
 109255                if (CompareCylinderPairSideUnshiftedEndpoint(geometry, ellipse, ref stationaryRoot,
 109256                    numerator, numeratorSigns, denominator, denominatorSigns, analytic,
 109257                    upper, candidate.DenominatorShift) >= 0) continue;
 258            }
 98259            return candidate;
 260        }
 261        // Rejecting the earlier eligible roots identifies the last one by
 262        // exclusion; no full quartic comparison is needed again.
 367263        return FiniteAxisValueRoots.GetRoot(roots, lastEligibleOrdinal, coefficients, signs, cell);
 264    }
 265
 266    /// <summary>
 267    /// Compares the retained raw h² with endpoint*2^ValueShift/RawScale².
 268    /// The F-cell has at most 55065 denominator bits. Thus ellipse queries
 269    /// have fewer than 58500 bits; the existing quartic Hermite fallback,
 270    /// including its caller query/remainder and determinant scratch, stays
 271    /// below 700 KiB. It runs after the separate F-isolation arena has returned.
 272    /// </summary>
 273    [MethodImpl(MethodImplOptions.NoInlining)]
 274    private static int CompareCylinderPairSideUnshiftedEndpoint(in CylinderPairGeometry geometry,
 275        bool ellipse, scoped ref FiniteAxisPolynomialRoot root,
 276        scoped ReadOnlySpan<ulong> numerator, scoped ReadOnlySpan<sbyte> numeratorSigns,
 277        scoped ReadOnlySpan<ulong> denominator, scoped ReadOnlySpan<sbyte> denominatorSigns,
 278        scoped ConvexContactCandidate analytic, scoped ReadOnlySpan<ulong> endpoint, int shift)
 279    {
 394280        int words = (shift + geometry.ValueShift + 2560 + 384 + 127) / 64;
 394281        Span<ulong> rawScale = stackalloc ulong[3];
 394282        Span<ulong> scaleSquared = stackalloc ulong[6];
 394283        WideArithmetic.GetMagnitude(geometry.RawScale, out rawScale[2], out rawScale[1], out rawScale[0]);
 394284        WideArithmetic.MultiplyMagnitudes(rawScale, rawScale, scaleSquared);
 394285        Span<ulong> term = stackalloc ulong[words];
 394286        if (ellipse)
 287        {
 29288            Span<ulong> query = stackalloc ulong[5 * words];
 29289            Span<sbyte> querySigns = stackalloc sbyte[5];
 58290            query.Clear(); querySigns.Clear();
 348291            for (int index = 0; index < 5; index++)
 292            {
 145293                Span<ulong> coefficient = query.Slice(index * words, words);
 145294                int sign = 0;
 145295                WideArithmetic.MultiplyMagnitudes(numerator.Slice(index * CylinderCapsuleEllipseWords,
 145296                    CylinderCapsuleEllipseWords), scaleSquared, term);
 145297                WideArithmetic.AddShiftedSignedMagnitude(term, numeratorSigns[index], shift, coefficient, ref sign);
 145298                WideArithmetic.MultiplyMagnitudes(denominator.Slice(index * CylinderCapsuleEllipseWords,
 145299                    CylinderCapsuleEllipseWords), endpoint, term);
 145300                WideArithmetic.AddShiftedSignedMagnitude(term, -denominatorSigns[index], geometry.ValueShift, coefficien
 145301                querySigns[index] = (sbyte)sign;
 302            }
 29303            return WideFiniteAxisIntersection.GetSignAtFiniteAxisRoot(ref root, query, querySigns);
 304        }
 365305        Span<ulong> rational = stackalloc ulong[words];
 365306        Span<ulong> radical = stackalloc ulong[words];
 730307        rational.Clear(); radical.Clear();
 730308        int rationalSign = 0, radicalSign = 0;
 365309        WideArithmetic.MultiplyMagnitudes(analytic.GapRational, scaleSquared, term);
 365310        WideArithmetic.AddShiftedSignedMagnitude(term, analytic.GapRationalSign, shift, rational, ref rationalSign);
 365311        WideArithmetic.MultiplyMagnitudes(analytic.GapDenominator, endpoint, term);
 365312        WideArithmetic.AddShiftedSignedMagnitude(term, -1, geometry.ValueShift, rational, ref rationalSign);
 365313        WideArithmetic.MultiplyMagnitudes(analytic.GapRadical, scaleSquared, term);
 365314        WideArithmetic.AddShiftedSignedMagnitude(term, analytic.GapRadicalSign, shift, radical, ref radicalSign);
 365315        return GetConvexContactCandidateQuadraticSign(rational, rationalSign, radical, radicalSign, analytic.GapRadicand
 316    }
 317}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.Projection.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.Projection.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Contains projection helpers for computing wide-precision separating-axis
 14/// projection depths of capsule/prism contacts and shared radial depth rounding.
 15/// </content>
 16internal static partial class WideConvexPrismRelations
 17{
 18    private static void GetCapsuleProjectionDepths(
 19        Axis3 axis,
 20        RigidAxis3 shapeAxis,
 21        Fixed64 axisLength,
 22        Fixed64 radius,
 23        Signed576 prismMinimum,
 24        Signed576 prismMaximum,
 25        out ProjectionDepth positive,
 26        out ProjectionDepth negative)
 27    {
 2056528        Signed576 alignment = GetAxisProjection(axis, shapeAxis);
 2056529        Signed704 axial = WideArithmetic.MultiplySigned576ToSigned704(
 2056530            alignment,
 2056531            Signed320.ExtendValue(Signed192.Raw(axisLength)));
 2056532        Signed192 commonWithoutScale = WideArithmetic.AddSigned192(
 2056533            shapeAxis.RotationDenominator,
 2056534            shapeAxis.RotationDenominator);
 2056535        Signed320 commonWide = WideArithmetic.MultiplySigned192(
 2056536            commonWithoutScale,
 2056537            Scale);
 2056538        _ = Signed192.TryNarrowSigned(
 2056539            Signed576.ExtendValue(commonWide),
 2056540            out Signed192 common);
 2056541        Signed704 scaledMinimum =
 2056542            WideArithmetic.MultiplySigned576ToSigned704(
 2056543                prismMinimum,
 2056544                Signed320.ExtendValue(commonWithoutScale));
 2056545        Signed704 scaledMaximum =
 2056546            WideArithmetic.MultiplySigned576ToSigned704(
 2056547                prismMaximum,
 2056548                Signed320.ExtendValue(commonWithoutScale));
 2056549        Signed576 axisSquared = GetAxisSquared(axis);
 2056550        Signed320 shapeAxisSquared = GetAxisSquared(shapeAxis);
 51
 2056552        Signed704 axialMagnitude = GetMagnitude(axial);
 2056553        positive = new ProjectionDepth(
 2056554            WideArithmetic.SubtractSigned704(
 2056555                axialMagnitude,
 2056556                scaledMinimum),
 2056557            common,
 2056558            radius,
 2056559            RadialKind.Capsule,
 2056560            axisSquared,
 2056561            shapeAxisSquared);
 2056562        negative = new ProjectionDepth(
 2056563            WideArithmetic.AddSigned704(
 2056564                axialMagnitude,
 2056565                scaledMaximum),
 2056566            common,
 2056567            radius,
 2056568            RadialKind.Capsule,
 2056569            axisSquared,
 2056570            shapeAxisSquared);
 2056571    }
 72
 73    private static bool IsProjectionNonNegative(in ProjectionDepth depth)
 74    {
 4116675        if (depth.Rational.Sign >= 0)
 3104676            return true;
 1012077        Span<ulong> left = stackalloc ulong[40];
 1012078        Span<ulong> right = stackalloc ulong[40];
 1012079        BuildLocalCapsuleRadicand(depth, left);
 1012080        BuildRationalSquare(depth.Rational, right);
 1012081        return WideArithmetic.CompareMagnitudeEqualLength(left, right) >= 0;
 82    }
 83
 84    /// <summary>Tests the selected sphere support plane's exact center distance against its radius.</summary>
 85    internal static bool IsSphereProjectionWithinRadius(in ProjectionDepth depth) =>
 586        IsProjectionNonNegative(new ProjectionDepth(WideArithmetic.SubtractSigned704(default, depth.Rational), depth.Com
 587            depth.Radius, depth.RadialKind, depth.AxisSquared, depth.ShapeAxisSquared));
 88
 89    internal static int CompareProjectionDepths(
 90        in ProjectionDepth left,
 91        in ProjectionDepth right)
 92    {
 2310693        Signed576 unit = Signed576.ExtendValue(
 2310694            Signed320.ExtendValue(
 2310695                Signed192.Signed(1L)));
 2310696        Signed320 leftCoefficient = WideArithmetic.MultiplySigned192(left.Common, Signed192.Raw(left.Radius));
 2310697        Signed320 rightCoefficient = WideArithmetic.MultiplySigned192(right.Common, Signed192.Raw(right.Radius));
 98        // Equal radial terms and axis norms cancel exactly; widening the
 99        // rational subtraction preserves the full signed input range.
 23106100        if (left.AxisSquared.Equals(right.AxisSquared) && leftCoefficient.Equals(rightCoefficient))
 8509101            return WideArithmetic.SubtractSigned832(Signed832.ExtendValue(left.Rational), Signed832.ExtendValue(right.Ra
 14597102        if (leftCoefficient.Equals(rightCoefficient))
 103        {
 104            // The same radial term cancels across different axis scales too.
 105            // Compare the remaining signed rational projections with the existing exact owner.
 14594106            Span<ulong> values = stackalloc ulong[40];
 29188107            var leftN = values[..11]; var rightN = values.Slice(11, 11);
 29188108            var leftAxis = values.Slice(22, 9); var rightAxis = values.Slice(31, 9);
 29188109            WideArithmetic.GetMagnitude(left.Rational, leftN); WideArithmetic.GetMagnitude(right.Rational, rightN);
 29188110            WideArithmetic.GetMagnitude(left.AxisSquared, leftAxis); WideArithmetic.GetMagnitude(right.AxisSquared, righ
 14594111            return WideArithmetic.CompareSignedNormalizedMagnitudes(leftN, left.Rational.Sign, leftAxis,
 14594112                rightN, right.Rational.Sign, rightAxis);
 113        }
 3114        return WideArithmetic.CompareRadialProjectionDepths(
 3115            left.Rational,
 3116            leftCoefficient,
 3117            Signed832.ExtendValue(left.AxisSquared),
 3118            unit,
 3119            left.AxisSquared,
 3120            right.Rational,
 3121            rightCoefficient,
 3122            Signed832.ExtendValue(right.AxisSquared),
 3123            unit,
 3124            right.AxisSquared);
 125    }
 126
 127    private static Fixed64 GetRoundedDepth(
 128        in ProjectionDepth depth,
 129        out bool isClamped)
 130    {
 559131        if (CompareProjectionDepthToTwiceRaw(
 559132                depth,
 559133                new Signed192(0UL, 0UL, unchecked((ulong)long.MaxValue << 1))) > 0)
 134        {
 3135            isClamped = true;
 3136            return Fixed64.MaxValue;
 137        }
 138
 556139        Fixed64 approximation = GetDepthApproximation(depth);
 140        // The estimate is a lower bound at every axis scale. Ordinary scaled
 141        // axes need at most five corrections; a reduced rational basis can
 142        // make the integer-root enclosure wider, requiring a bounded search.
 1344143        for (int correction = 0; correction < 5; correction++)
 144        {
 671145            Signed192 upperMidpoint = new(
 671146                0UL,
 671147                0UL,
 671148                unchecked((ulong)approximation.m_rawValue << 1) | 1UL);
 671149            int upperComparison =
 671150                CompareProjectionDepthToTwiceRaw(
 671151                    depth,
 671152                    upperMidpoint);
 671153            if ((approximation < Fixed64.MaxValue)
 671154                & (upperComparison
 671155                    + (approximation.m_rawValue & 1L) > 0))
 156            {
 116157                approximation = Fixed64.FromRaw(
 116158                    approximation.m_rawValue + 1L);
 116159                continue;
 160            }
 161
 555162            isClamped = false;
 555163            return approximation;
 164        }
 165
 2166        long low = approximation.m_rawValue, high = long.MaxValue;
 63167        while (low < high)
 168        {
 62169            long middle = low + ((high - low) >> 1);
 62170            Signed192 upperMidpoint = new(0UL, 0UL, unchecked((ulong)middle << 1) | 1UL);
 62171            int comparison = CompareProjectionDepthToTwiceRaw(depth, upperMidpoint);
 172            // Crossing an odd cell's upper half rounds to the next even value.
 90173            if (comparison + (middle & 1L) > 0) low = middle + 1;
 34174            else high = middle;
 175        }
 1176        isClamped = false;
 1177        return Fixed64.FromRaw(low);
 178    }
 179
 180    private static Fixed64 GetDepthApproximation(
 181        in ProjectionDepth depth)
 182    {
 556183        Signed704 axisSquared =
 556184            Signed704.ExtendValue(depth.AxisSquared);
 556185        Signed576 axisFloor =
 556186            WideArithmetic.GetFloorSquareRoot(axisSquared);
 556187        Signed576 axisCeiling =
 556188            GetCeilingSquareRoot(axisSquared, axisFloor);
 556189        Signed576 rationalAxisLength = depth.Rational.Sign < 0
 556190            ? axisFloor
 556191            : axisCeiling;
 556192        Signed576 rationalDenominator = WideArithmetic.MultiplySigned576(
 556193            rationalAxisLength,
 556194            depth.Common);
 556195        Fixed64 radial = depth.RadialKind == RadialKind.Capsule ? depth.Radius : Fixed64.Zero;
 556196        Signed704 combinedNumerator = WideArithmetic.AddSigned704(
 556197            depth.Rational,
 556198            WideArithmetic.MultiplySigned576ToSigned704(
 556199                rationalDenominator,
 556200                Signed320.ExtendValue(Signed192.Raw(radial))));
 201        // Cancel rational and radial support before narrowing so individually
 202        // out-of-domain terms cannot discard a representable shallow depth.
 556203        return Fixed64.GetNonNegativeRawRatioFloor(
 556204            combinedNumerator,
 556205            Signed704.ExtendValue(rationalDenominator));
 206    }
 207
 208    private static Signed576 GetCeilingSquareRoot(
 209        Signed704 squared,
 210        Signed576 floor)
 211    {
 556212        _ = Signed320.TryNarrowSigned(
 556213            floor,
 556214            out Signed320 narrowFloor);
 556215        Signed704 floorSquared =
 556216            WideArithmetic.MultiplySigned576ToSigned704(
 556217                floor,
 556218                narrowFloor);
 556219        return WideArithmetic.SubtractSigned704(
 556220                squared,
 556221                floorSquared).IsZero
 556222            ? floor
 556223            : WideArithmetic.AddSigned576(
 556224                floor,
 556225                Signed576.ExtendValue(
 556226                    Signed320.ExtendValue(
 556227                        Signed192.Signed(1L))));
 228    }
 229
 230    private static ulong GetNearestEvenIncrement(
 231        int midpointComparison,
 232        ulong floor) =>
 8283233        unchecked((ulong)(
 8283234            (midpointComparison
 8283235                + (int)(floor & 1UL)
 8283236                + 1) >> 1));
 237
 238    private static int CompareProjectionDepthToTwiceRaw(
 239        in ProjectionDepth depth,
 240        Signed192 twiceRaw)
 241    {
 1292242        Span<ulong> leftFirst = stackalloc ulong[40];
 1292243        Span<ulong> leftSecond = stackalloc ulong[40];
 1292244        Span<ulong> rightFirst = stackalloc ulong[40];
 1292245        Span<ulong> rightSecond = stackalloc ulong[40];
 1292246        leftFirst.Clear();
 1292247        leftSecond.Clear();
 1292248        rightFirst.Clear();
 1292249        rightSecond.Clear();
 250
 1292251        Span<ulong> rationalTerm = depth.Rational.Sign >= 0
 1292252            ? leftFirst
 1292253            : rightFirst;
 1292254        BuildProduct(
 1292255            depth.Rational,
 1292256            depth.Rational,
 1292257            depth.ShapeAxisSquared,
 1292258            rationalTerm);
 1292259        ShiftLeft(rationalTerm, 2);
 1292260        if (depth.RadialKind != RadialKind.None)
 261        {
 1118262            Span<ulong> radialTerm = IsZero(leftFirst)
 1118263                ? leftFirst
 1118264                : leftSecond;
 1118265            BuildThresholdCapsuleRadicand(depth, radialTerm);
 1118266            ShiftLeft(radialTerm, 2);
 267        }
 268
 1292269        Signed320 thresholdCoefficient =
 1292270            WideArithmetic.MultiplySigned192(depth.Common, twiceRaw);
 1292271        BuildThresholdRadicand(
 1292272            thresholdCoefficient,
 1292273            depth.ShapeAxisSquared,
 1292274            depth.AxisSquared,
 1292275            rightSecond);
 1292276        return CompareRadicalPairs(
 1292277            leftFirst,
 1292278            leftSecond,
 1292279            rightFirst,
 1292280            rightSecond);
 281    }
 282
 283    private static Signed576 GetAxisProjection(
 284        Axis3 axis,
 285        RigidAxis3 direction) =>
 34380286        WideArithmetic.AddSigned576(
 34380287            WideArithmetic.AddSigned576(
 34380288                WideArithmetic.MultiplySigned320(
 34380289                    axis.X,
 34380290                    Signed320.ExtendValue(direction.X)),
 34380291                WideArithmetic.MultiplySigned320(
 34380292                    axis.Y,
 34380293                    Signed320.ExtendValue(direction.Y))),
 34380294            WideArithmetic.MultiplySigned320(
 34380295                axis.Z,
 34380296                Signed320.ExtendValue(direction.Z)));
 297
 298    private static Signed576 GetAxisSquared(Axis3 axis) =>
 26236299        WideArithmetic.AddSigned576(
 26236300            WideArithmetic.AddSigned576(
 26236301                WideArithmetic.MultiplySigned320(axis.X, axis.X),
 26236302                WideArithmetic.MultiplySigned320(axis.Y, axis.Y)),
 26236303            WideArithmetic.MultiplySigned320(axis.Z, axis.Z));
 304
 305    private static Signed320 GetAxisSquared(RigidAxis3 axis) =>
 26913306        WideArithmetic.AddSigned320(
 26913307            WideArithmetic.AddSigned320(
 26913308                WideArithmetic.MultiplySigned192(axis.X, axis.X),
 26913309                WideArithmetic.MultiplySigned192(axis.Y, axis.Y)),
 26913310            WideArithmetic.MultiplySigned192(axis.Z, axis.Z));
 311
 312    private static Signed704 GetMagnitude(Signed704 value) =>
 20767313        value.Sign >= 0
 20767314            ? value
 20767315            : WideArithmetic.SubtractSigned704(default, value);
 316
 317    private static void BuildLocalCapsuleRadicand(
 318        in ProjectionDepth depth,
 319        Span<ulong> result)
 320    {
 10120321        Signed320 coefficient = WideArithmetic.MultiplySigned192(
 10120322            depth.Common,
 10120323            Signed192.Raw(depth.Radius));
 10120324        BuildProduct(
 10120325            coefficient,
 10120326            coefficient,
 10120327            depth.AxisSquared,
 10120328            result);
 10120329    }
 330
 331    private static void BuildThresholdCapsuleRadicand(
 332        in ProjectionDepth depth,
 333        Span<ulong> result)
 334    {
 1118335        Signed320 coefficient = WideArithmetic.MultiplySigned192(
 1118336            depth.Common,
 1118337            Signed192.Raw(depth.Radius));
 1118338        BuildProduct(
 1118339            coefficient,
 1118340            coefficient,
 1118341            depth.ShapeAxisSquared,
 1118342            depth.AxisSquared,
 1118343            result);
 1118344    }
 345
 346    private static void BuildRationalSquare(
 347        Signed704 rational,
 348        Span<ulong> result) =>
 10120349        BuildProduct(rational, rational, result);
 350
 351    private static void BuildThresholdRadicand(
 352        Signed320 coefficient,
 353        Signed320 shapeAxisSquared,
 354        Signed576 axisSquared,
 355        Span<ulong> result) =>
 1292356        BuildProduct(
 1292357            coefficient,
 1292358            coefficient,
 1292359            shapeAxisSquared,
 1292360            axisSquared,
 1292361            result);
 362
 363    // Both callers pack at most three nonzero signed radical terms into
 364    // these two pairs. A fourth term would invalidate the reduction below.
 365    private static int CompareRadicalPairs(
 366        ReadOnlySpan<ulong> leftFirst,
 367        ReadOnlySpan<ulong> leftSecond,
 368        ReadOnlySpan<ulong> rightFirst,
 369        ReadOnlySpan<ulong> rightSecond)
 370    {
 4854371        int termWords = leftFirst.Length;
 4854372        int productWords = termWords * 2;
 4854373        Span<ulong> leftBase = stackalloc ulong[termWords];
 4854374        Span<ulong> rightBase = stackalloc ulong[termWords];
 4854375        WideArithmetic.AddEqualMagnitudes(leftFirst, leftSecond, leftBase);
 4854376        WideArithmetic.AddEqualMagnitudes(rightFirst, rightSecond, rightBase);
 4854377        int baseComparison = WideArithmetic.CompareMagnitudeEqualLength(leftBase, rightBase);
 4854378        Span<ulong> leftProduct = stackalloc ulong[productWords];
 4854379        Span<ulong> rightProduct = stackalloc ulong[productWords];
 4854380        WideArithmetic.MultiplyMagnitudes(leftFirst, leftSecond, leftProduct);
 4854381        WideArithmetic.MultiplyMagnitudes(rightFirst, rightSecond, rightProduct);
 382        // Squared radical sums differ by (a+b-c-d)+2*(sqrt(ab)-sqrt(cd)).
 383        // Agreeing signs decide this exactly; only opposing signs need the
 384        // larger squared comparison. Zero differences preserve exact ties.
 4854385        int productComparison = WideArithmetic.CompareMagnitudeEqualLength(leftProduct, rightProduct);
 4854386        if (baseComparison == 0)
 2982387            return productComparison;
 1872388        if (productComparison == 0 || productComparison == baseComparison)
 747389            return baseComparison;
 390        // At least one pair product is zero. Opposing comparisons force
 391        // the product on the larger-base side to be zero, leaving B-2*sqrt(P).
 392        // Compare B² with 4P exactly; the opposite product is local scratch.
 1125393        Span<ulong> baseMagnitude = stackalloc ulong[termWords];
 1125394        if (baseComparison > 0)
 524395            WideArithmetic.SubtractEqualMagnitudes(leftBase, rightBase, baseMagnitude);
 396        else
 601397            WideArithmetic.SubtractEqualMagnitudes(rightBase, leftBase, baseMagnitude);
 1125398        Span<ulong> baseSquared = stackalloc ulong[productWords];
 1125399        WideArithmetic.MultiplyMagnitudes(baseMagnitude, baseMagnitude, baseSquared);
 1125400        Span<ulong> oppositeProduct = baseComparison > 0 ? rightProduct : leftProduct;
 1125401        ShiftLeft(oppositeProduct, 2);
 1125402        return baseComparison * WideArithmetic.CompareMagnitudeEqualLength(baseSquared, oppositeProduct);
 403    }
 404}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.ProjectionArithmetic.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.ProjectionArithmetic.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Wide-precision (multi-word) arithmetic helpers used to build and manipulate
 14/// magnitude spans for high-precision projection products, including
 15/// multiplication, addition, subtraction, shifting, and comparison of
 16/// arbitrary-length unsigned word arrays and combining signed magnitudes.
 17/// </content>
 18internal static partial class WideConvexPrismRelations
 19{
 20    private static Signed576 GetMagnitude576(Signed576 value) =>
 2042521        value.Sign >= 0
 2042522            ? value
 2042523            : WideArithmetic.SubtractSigned576(default, value);
 24
 25    private static void BuildProduct(
 26        Signed704 first,
 27        Signed704 second,
 28        Span<ulong> result)
 29    {
 1146830        Span<ulong> firstWords = stackalloc ulong[11];
 1146831        Span<ulong> secondWords = stackalloc ulong[11];
 1146832        WideArithmetic.GetMagnitude(first, firstWords);
 1146833        WideArithmetic.GetMagnitude(second, secondWords);
 1146834        WideArithmetic.MultiplyMagnitudes(firstWords, secondWords, result);
 1146835    }
 36
 37    private static void BuildProduct(
 38        Signed704 first,
 39        Signed704 second,
 40        Signed320 third,
 41        Span<ulong> result)
 42    {
 129243        BuildProduct(first, second, result);
 129244        MultiplyBy(result, third);
 129245    }
 46
 47    private static void BuildProduct(
 48        Signed320 first,
 49        Signed320 second,
 50        Signed576 third,
 51        Span<ulong> result)
 52    {
 1017653        Span<ulong> firstWords = stackalloc ulong[5];
 1017654        Span<ulong> secondWords = stackalloc ulong[5];
 1017655        GetMagnitude(first, firstWords);
 1017656        GetMagnitude(second, secondWords);
 1017657        WideArithmetic.MultiplyMagnitudes(firstWords, secondWords, result);
 1017658        MultiplyBy(result, third);
 1017659    }
 60
 61    private static void BuildProduct(
 62        Signed320 first,
 63        Signed320 second,
 64        Signed320 third,
 65        Signed576 fourth,
 66        Span<ulong> result)
 67    {
 241068        Span<ulong> firstWords = stackalloc ulong[5];
 241069        Span<ulong> secondWords = stackalloc ulong[5];
 241070        GetMagnitude(first, firstWords);
 241071        GetMagnitude(second, secondWords);
 241072        WideArithmetic.MultiplyMagnitudes(firstWords, secondWords, result);
 241073        MultiplyBy(result, third);
 241074        MultiplyBy(result, fourth);
 241075    }
 76
 77    private static void MultiplyBy(
 78        Span<ulong> value,
 79        Signed320 factor)
 80    {
 370281        Span<ulong> words = stackalloc ulong[5];
 370282        GetMagnitude(factor, words);
 370283        MultiplyBy(value, words);
 370284    }
 85
 86    private static void MultiplyBy(
 87        Span<ulong> value,
 88        Signed576 factor)
 89    {
 1258690        Span<ulong> words = stackalloc ulong[9];
 1258691        WideArithmetic.GetMagnitude(factor, words);
 1258692        MultiplyBy(value, words);
 1258693    }
 94
 95    private static void MultiplyBy(
 96        Span<ulong> value,
 97        ReadOnlySpan<ulong> factor)
 98    {
 2651699        Span<ulong> product = stackalloc ulong[value.Length];
 26516100        WideArithmetic.MultiplyMagnitudes(value, factor, product);
 26516101        product.CopyTo(value);
 26516102    }
 103
 104    private static void GetMagnitude(
 105        Signed320 value,
 106        Span<ulong> result) =>
 32853107        WideArithmetic.GetMagnitude(
 32853108            value,
 32853109            out result[4],
 32853110            out result[3],
 32853111            out result[2],
 32853112            out result[1],
 32853113            out result[0]);
 114
 115    private static void CopyPositiveRadicand(
 116        ReadOnlySpan<ulong> radicand,
 117        Span<ulong> first,
 118        Span<ulong> second)
 119    {
 7708120        if (IsZero(first))
 7124121            radicand.CopyTo(first);
 122        else
 584123            radicand.CopyTo(second);
 584124    }
 125
 126    private static void CombineWideSignedMagnitudes(
 127        ReadOnlySpan<ulong> left,
 128        int leftSign,
 129        ReadOnlySpan<ulong> right,
 130        int rightSign,
 131        Span<ulong> result,
 132        out int resultSign)
 133    {
 189511134        if (leftSign == 0 || IsZero(left))
 135        {
 40834136            right.CopyTo(result);
 40834137            resultSign = IsZero(right) ? 0 : rightSign;
 40834138            return;
 139        }
 148677140        if (rightSign == 0 || IsZero(right))
 141        {
 3182142            left.CopyTo(result);
 3182143            resultSign = leftSign;
 3182144            return;
 145        }
 145495146        if (leftSign == rightSign)
 147        {
 2179148            WideArithmetic.AddEqualMagnitudes(left, right, result);
 2179149            resultSign = leftSign;
 2179150            return;
 151        }
 152
 143316153        int comparison = WideArithmetic.CompareMagnitudeEqualLength(left, right);
 143316154        if (comparison == 0)
 155        {
 11094156            result.Clear();
 11094157            resultSign = 0;
 11094158            return;
 159        }
 132222160        if (comparison > 0)
 161        {
 105806162            WideArithmetic.SubtractEqualMagnitudes(left, right, result);
 105806163            resultSign = leftSign;
 164        }
 165        else
 166        {
 26416167            WideArithmetic.SubtractEqualMagnitudes(right, left, result);
 26416168            resultSign = rightSign;
 169        }
 26416170    }
 171
 172    private static void ShiftLeft(Span<ulong> value, int bits)
 173    {
 460786174        for (int iteration = 0; iteration < bits; iteration++)
 175        {
 182987176            ulong carry = 0UL;
 12364486177            for (int index = 0; index < value.Length; index++)
 178            {
 5999256179                ulong nextCarry = value[index] >> 63;
 5999256180                value[index] = (value[index] << 1) | carry;
 5999256181                carry = nextCarry;
 182            }
 183        }
 47406184    }
 185
 186    private static bool IsZero(ReadOnlySpan<ulong> value) =>
 2614353187        WideArithmetic.GetActiveMagnitudeLength(value) == 0;
 188}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.ProjectionDepth.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.ProjectionDepth.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.ContactQuadratic;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>ProjectionDepth declaration for WideConvexPrismRelations.</content>
 14internal static partial class WideConvexPrismRelations
 15{
 16    internal readonly struct ProjectionDepth
 17    {
 18        internal readonly Signed704 Rational;
 19        internal readonly Signed192 Common;
 20        internal readonly Fixed64 Radius;
 21        internal readonly RadialKind RadialKind;
 22        internal readonly Signed576 AxisSquared;
 23        internal readonly Signed320 ShapeAxisSquared;
 24
 25        internal ProjectionDepth(
 26            Signed704 rational,
 27            Signed192 common,
 28            Fixed64 radius,
 29            RadialKind radialKind,
 30            Signed576 axisSquared,
 31            Signed320 shapeAxisSquared)
 32        {
 4146433            Rational = rational;
 4146434            Common = common;
 4146435            Radius = radius;
 4146436            RadialKind = radialKind;
 4146437            AxisSquared = axisSquared;
 4146438            ShapeAxisSquared = shapeAxisSquared;
 4146439        }
 40    }
 41}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.RigidCylinderPairs.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.RigidCylinderPairs.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
 8namespace FixedMathSharp.Geometry;
 9
 10/// <content>
 11/// Contains routines for computing contact information between pairs of rigid, finite cylinders.
 12/// </content>
 13internal static partial class WideConvexPrismRelations
 14{
 15    internal static bool TryGetCenteredFiniteCylindersContact(
 16        Vector3d firstCenter,
 17        FixedQuaternion firstRotation,
 18        Vector3d firstLocalAxis,
 19        Fixed64 firstLength,
 20        Fixed64 firstRadius,
 21        Vector3d secondCenter,
 22        FixedQuaternion secondRotation,
 23        Vector3d secondLocalAxis,
 24        Fixed64 secondLength,
 25        Fixed64 secondRadius,
 26        out FixedContactAnchors contact)
 27    {
 28        // A zero-radius cylinder is exactly its finite core segment. Keep
 29        // that family with the complete cylinder/capsule authority, including
 30        // oblique side/rim minima and zero-depth segment/segment contacts.
 78831        if (secondRadius == Fixed64.Zero)
 8532            return TryGetCenteredFiniteCylinderCapsuleContact(
 8533                firstCenter, firstRotation, firstLocalAxis, firstLength, firstRadius,
 8534                secondCenter, secondRotation, secondLocalAxis, secondLength, Fixed64.Zero,
 8535                out contact);
 70336        if (firstRadius == Fixed64.Zero)
 37        {
 12738            if (!TryGetCenteredFiniteCylinderCapsuleContact(
 12739                    secondCenter, secondRotation, secondLocalAxis, secondLength, secondRadius,
 12740                    firstCenter, firstRotation, firstLocalAxis, firstLength, Fixed64.Zero,
 12741                    out FixedContactAnchors reversed))
 42            {
 9543                contact = default;
 9544                return false;
 45            }
 3246            contact = new FixedContactAnchors(reversed.SecondAnchor, reversed.FirstAnchor,
 3247                -reversed.Normal, reversed.Depth, reversed.DepthIsClamped);
 3248            return true;
 49        }
 57650        if (!TryGetPositiveRadiusCylinderPairPenetration(
 57651                firstCenter, firstRotation, firstLocalAxis, Signed192.Raw(firstLength), firstRadius,
 57652                secondCenter, secondRotation, secondLocalAxis, Signed192.Raw(secondLength), secondRadius,
 57653                out Vector3d normal, out Fixed64 depth, out bool depthIsClamped))
 54        {
 11655            contact = default;
 11656            return false;
 57        }
 46058        contact = new FixedContactAnchors(
 46059            WideGeometry.GetCenteredCylinderSupportAnchor(firstCenter, firstRotation,
 46060                firstLocalAxis, firstLength, firstRadius, normal),
 46061            WideGeometry.GetCenteredCylinderSupportAnchor(secondCenter, secondRotation,
 46062                secondLocalAxis, secondLength, secondRadius, -normal),
 46063            normal, depth, depthIsClamped);
 46064        return true;
 65    }
 66
 67    /// <summary>
 68    /// Exact relation for admitted rigid cylinders with positive radii. Raw full
 69    /// lengths may occupy 64 unsigned bits, including twice a slab half-thickness.
 70    /// This does not widen authored positions, radii, or quaternion admission.
 71    /// </summary>
 72    internal static bool TryGetPositiveRadiusCylinderPairPenetration(
 73        Vector3d firstCenter, FixedQuaternion firstRotation, Vector3d firstLocalAxis,
 74        Signed192 firstLength, Fixed64 firstRadius,
 75        Vector3d secondCenter, FixedQuaternion secondRotation, Vector3d secondLocalAxis,
 76        Signed192 secondLength, Fixed64 secondRadius,
 77        out Vector3d normal, out Fixed64 depth, out bool depthIsClamped)
 78    {
 57679        normal = default;
 57680        depth = default;
 57681        depthIsClamped = false;
 57682        WideOrientedBox.GetRotatedLocalAxisNumerators(
 57683            firstRotation,
 57684            firstLocalAxis,
 57685            out Signed192 firstAxisX,
 57686            out Signed192 firstAxisY,
 57687            out Signed192 firstAxisZ,
 57688            out Signed192 firstDenominator);
 57689        WideOrientedBox.GetRotatedLocalAxisNumerators(
 57690            secondRotation,
 57691            secondLocalAxis,
 57692            out Signed192 secondAxisX,
 57693            out Signed192 secondAxisY,
 57694            out Signed192 secondAxisZ,
 57695            out Signed192 secondDenominator);
 57696        var firstAxis = new RigidAxis3(
 57697            firstAxisX,
 57698            firstAxisY,
 57699            firstAxisZ,
 576100            firstDenominator);
 576101        var secondAxis = new RigidAxis3(
 576102            secondAxisX,
 576103            secondAxisY,
 576104            secondAxisZ,
 576105            secondDenominator);
 576106        Axis3 firstCandidate = firstAxis.ToWide();
 576107        Axis3 secondCandidate = secondAxis.ToWide();
 576108        if (!Cross(firstCandidate, secondCandidate).IsZero)
 109        {
 409110            return TryGetNonparallelCylinderPairPenetration(firstCenter, firstRotation, firstLocalAxis,
 409111                firstLength, firstRadius, secondCenter, secondRotation, secondLocalAxis,
 409112                secondLength, secondRadius, out normal, out depth, out depthIsClamped);
 113        }
 167114        var best = default(CylinderCylinderPenetration);
 167115        if (!TryKeepCylinderCylinderAxis(
 167116                firstCandidate,
 167117                firstCenter,
 167118                firstAxis,
 167119                firstLength,
 167120                firstRadius,
 167121                secondCenter,
 167122                secondAxis,
 167123                secondLength,
 167124                secondRadius,
 167125                ref best))
 126        {
 4127            return false;
 128        }
 129        // Coincident or overlapping coaxial cores have a zero closest-core
 130        // direction. Their radial minimum still needs a representative;
 131        // reuse the cylinder/capsule authored-frame tie direction. For an
 132        // off-axis pair the closest-core radial candidate remains minimal.
 163133        CylinderCapsuleDirection perpendicular = GetCylinderCapsulePerpendicular(firstRotation, firstLocalAxis);
 163134        if (!TryKeepCylinderCylinderAxis(
 163135                new Axis3(Signed320.NarrowValue(perpendicular.X),
 163136                    Signed320.NarrowValue(perpendicular.Y), Signed320.NarrowValue(perpendicular.Z)),
 163137                firstCenter, firstAxis, firstLength, firstRadius,
 163138                secondCenter, secondAxis, secondLength, secondRadius, ref best))
 139        {
 10140            return false;
 141        }
 142        // Parallel finite cylinders need their cap axis and the perpendicular
 143        // center difference, not clamped closest points on their core segments.
 144        // |A_i|<2^100 and |d_i|<2^64 bound the two cross products by
 145        // 2^167 and 2^269 per component, including subtraction headroom.
 146        // The squared norm is <2^540, alignment <2^371, and radial plane
 147        // product <2^742, within the existing 576/832-bit slots.
 153148        var difference = new Axis3(
 153149            Signed320.ExtendValue(WideArithmetic.Difference(secondCenter.X, firstCenter.X)),
 153150            Signed320.ExtendValue(WideArithmetic.Difference(secondCenter.Y, firstCenter.Y)),
 153151            Signed320.ExtendValue(WideArithmetic.Difference(secondCenter.Z, firstCenter.Z)));
 153152        Axis3 radial = Cross(firstCandidate, Cross(difference, firstCandidate));
 153153        if (!TryKeepCylinderCylinderAxis(radial,
 153154                firstCenter, firstAxis, firstLength, firstRadius,
 153155                secondCenter, secondAxis, secondLength, secondRadius, ref best))
 156        {
 5157            return false;
 158        }
 148159        Axis3 orientedAxis = best.Negate
 148160            ? new Axis3(WideArithmetic.Negate(best.Axis.X),
 148161                WideArithmetic.Negate(best.Axis.Y), WideArithmetic.Negate(best.Axis.Z))
 148162            : best.Axis;
 148163        normal = WideNormalization.GetNormalized(
 148164            Signed576.ExtendValue(orientedAxis.X),
 148165            Signed576.ExtendValue(orientedAxis.Y),
 148166            Signed576.ExtendValue(orientedAxis.Z));
 148167        GetRoundedCylinderCylinderDepth(best, out depth, out depthIsClamped);
 148168        return true;
 169    }
 170
 171
 172    private static bool TryKeepCylinderCylinderAxis(
 173        Axis3 axis,
 174        Vector3d firstCenter,
 175        RigidAxis3 firstAxis,
 176        Signed192 firstLength,
 177        Fixed64 firstRadius,
 178        Vector3d secondCenter,
 179        RigidAxis3 secondAxis,
 180        Signed192 secondLength,
 181        Fixed64 secondRadius,
 182        ref CylinderCylinderPenetration best)
 183    {
 483184        if (axis.IsZero)
 40185            return true;
 186
 443187        Signed576 firstAlignment =
 443188            GetAxisProjection(axis, firstAxis);
 443189        Signed576 secondAlignment =
 443190            GetAxisProjection(axis, secondAxis);
 443191        Signed576 centerProjection = GetDifferenceProjection(
 443192            secondCenter,
 443193            firstCenter,
 443194            axis);
 443195        Signed576 firstAxial = WideArithmetic.MultiplySigned576(
 443196            WideArithmetic.MultiplySigned576(
 443197                GetMagnitude576(firstAlignment),
 443198                firstLength),
 443199            secondAxis.RotationDenominator);
 443200        Signed576 secondAxial = WideArithmetic.MultiplySigned576(
 443201            WideArithmetic.MultiplySigned576(
 443202                GetMagnitude576(secondAlignment),
 443203                secondLength),
 443204            firstAxis.RotationDenominator);
 443205        Signed576 scaledCenter = WideArithmetic.MultiplySigned576(
 443206            WideArithmetic.MultiplySigned576(
 443207                WideArithmetic.MultiplySigned576(
 443208                    GetMagnitude576(centerProjection),
 443209                    firstAxis.RotationDenominator),
 443210                secondAxis.RotationDenominator),
 443211            Signed192.Raw(Fixed64.Two));
 443212        Signed704 rational = Signed704.ExtendValue(
 443213            WideArithmetic.SubtractSigned576(
 443214                WideArithmetic.AddSigned576(
 443215                    firstAxial,
 443216                    secondAxial),
 443217                scaledCenter));
 443218        Signed576 axisSquared = GetAxisSquared(axis);
 443219        Signed576 commonWide = WideArithmetic.MultiplySigned576(
 443220            Signed576.ExtendValue(
 443221                WideArithmetic.MultiplySigned192(
 443222                    firstAxis.RotationDenominator,
 443223                    secondAxis.RotationDenominator)),
 443224            Signed192.Raw(Fixed64.Two));
 443225        _ = Signed192.TryNarrowSigned(
 443226            commonWide,
 443227            out Signed192 common);
 228        // Only cap normals and perpendiculars reach this parallel selector.
 229        // The radial support is respectively zero or the sum of both radii.
 230        // Each admitted radius raw value is <= 2^63-1, so their exact sum
 231        // is <= 2^64-2 and must not be narrowed through Fixed64.
 443232        ulong radiusRaw = firstAlignment.IsZero
 443233            ? unchecked((ulong)firstRadius.m_rawValue + (ulong)secondRadius.m_rawValue)
 443234            : 0UL;
 443235        var depth = new CylinderPairDepth(rational, common, axisSquared, radiusRaw);
 443236        if (GetCylinderPairDepthSign(depth) < 0)
 19237            return false;
 424238        if (!best.HasValue
 424239            || CompareCylinderPairDepths(
 424240                depth,
 424241                best.Depth) < 0)
 242        {
 302243            best = new CylinderCylinderPenetration(
 302244                axis,
 302245                centerProjection.Sign < 0,
 302246                depth);
 247        }
 424248        return true;
 249    }
 250
 251    private static int GetCylinderPairDepthSign(in CylinderPairDepth depth) =>
 471252        WideArithmetic.CompareSignedLinearRadicalToZero(
 471253            Signed832.ExtendValue(depth.Rational),
 471254            Signed704.ExtendValue(Signed576.ExtendValue(depth.RadialCoefficient)),
 471255            depth.AxisSquared,
 471256            Signed320.ExtendValue(Signed192.Signed(1L)));
 257
 258    private static int CompareCylinderPairDepths(
 259        in CylinderPairDepth left,
 260        in CylinderPairDepth right)
 261    {
 262        // All candidates in this query share the same positive Common.
 261263        Signed576 unit = Signed576.ExtendValue(Signed320.ExtendValue(Signed192.Signed(1L)));
 261264        return WideArithmetic.CompareRadialProjectionDepths(
 261265            left.Rational, left.RadialCoefficient,
 261266            Signed832.ExtendValue(left.AxisSquared), unit, left.AxisSquared,
 261267            right.Rational, right.RadialCoefficient,
 261268            Signed832.ExtendValue(right.AxisSquared), unit, right.AxisSquared);
 269    }
 270}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.RigidCylinderPairs.Rounding.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.RigidCylinderPairs.Rounding.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Parallel cylinder depth materialization through the shared projection and
 14/// quadratic-contact rounding authorities.
 15/// </content>
 16internal static partial class WideConvexPrismRelations
 17{
 18    private static void GetRoundedCylinderCylinderDepth(
 19        in CylinderCylinderPenetration penetration,
 20        out Fixed64 result,
 21        out bool isClamped)
 22    {
 14823        CylinderPairDepth depth = penetration.Depth;
 14824        if (depth.RadiusRaw <= (ulong)long.MaxValue)
 25        {
 12026            var projection = new ProjectionDepth(
 12027                depth.Rational, depth.Common, Fixed64.FromRaw((long)depth.RadiusRaw),
 12028                depth.RadiusRaw == 0UL ? RadialKind.None : RadialKind.Capsule,
 12029                depth.AxisSquared, Signed320.ExtendValue(Signed192.Signed(1L)));
 12030            result = GetRoundedDepth(projection, out isClamped);
 12031            return;
 32        }
 33
 34        // Only an unsigned radius sum above Fixed64.MaxValue needs the shared
 35        // quadratic path. For g=R/(C sqrt(A))+r, its squared raw gap is
 36        // (R²+(rC)²A+2RrC sqrt(A))/(C²A).
 37        // The admitted axes give |R|<2^502, C<2^163, A<2^540, r<2^64:
 38        // gap slots A/B/C/D use <1005/730/540/866 bits respectively.
 39        // Thus every construction product fits the existing forty-word slots.
 40        const int words = ConvexContactCandidate.Words;
 2841        Span<ulong> values = stackalloc ulong[ConvexContactCandidate.Slots * words];
 2842        Span<int> signs = stackalloc int[ConvexContactCandidate.Slots];
 2843        values.Clear();
 2844        signs.Clear();
 2845        GetMagnitude(penetration.Axis.X, values[..words]);
 2846        GetMagnitude(penetration.Axis.Y, values.Slice(words, words));
 2847        GetMagnitude(penetration.Axis.Z, values.Slice(2 * words, words));
 2848        signs[0] = penetration.Axis.X.Sign;
 2849        signs[1] = penetration.Axis.Y.Sign;
 2850        signs[2] = penetration.Axis.Z.Sign;
 51
 2852        Signed320 coefficient = depth.RadialCoefficient;
 2853        Span<ulong> rational = values.Slice(7 * words, words);
 2854        Span<ulong> radical = values.Slice(8 * words, words);
 2855        Span<ulong> radicand = values.Slice(9 * words, words);
 2856        Span<ulong> denominator = values.Slice(10 * words, words);
 2857        Span<ulong> rationalSquared = stackalloc ulong[words];
 2858        BuildProduct(depth.Rational, depth.Rational, rationalSquared);
 2859        BuildProduct(coefficient, coefficient, depth.AxisSquared, rational);
 2860        WideArithmetic.AddEqualMagnitudes(rational, rationalSquared, rational);
 2861        BuildProduct(depth.Rational,
 2862            Signed704.ExtendValue(Signed576.ExtendValue(coefficient)), radical);
 2863        ShiftLeft(radical, 1);
 2864        WideArithmetic.GetMagnitude(depth.AxisSquared, radicand[..9]);
 2865        Signed320 common = Signed320.ExtendValue(depth.Common);
 2866        BuildProduct(common, common, depth.AxisSquared, denominator);
 2867        signs[7] = 1;
 2868        signs[8] = depth.Rational.Sign;
 2869        GetRoundedConvexContactCandidateDepth(
 2870            new ConvexContactCandidate(values, signs, GetCylinderPairDepthSign(depth)),
 2871            Fixed64.Zero, out result, out isClamped);
 2872    }
 73}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.SphereBoundary.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.SphereBoundary.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12
 13
 14/// <content>Finite spherical face peaks retain exact native-yaw span cuts through ray evaluation.</content>
 15internal static partial class WideConvexPrismRelations
 16{
 17    /// <summary>Finds the sphere's inward ray at the closest point of a retained vertical face rectangle.</summary>
 18    /// <remarks>
 19    /// Geometry is admitted within 4096 units. The inside point chooses the face's inward normal;
 20    /// its finite rectangle must actually meet the sphere. The caller supplies fresh selection banks
 21    /// with the ordinary dimensions, or wide dimensions when the clip has wide bounds. Selection
 22    /// retains the exact ray parameter; physical depth squared is NormalSquared*N^2/(4*D^2).
 23    /// </remarks>
 24    internal static bool TryGetSphereBoundaryFaceContact(Vector3d center, Fixed64 radius,
 25        Vector3d origin, Fixed64 yaw, Vector2d start, Vector2d end, Vector2d inside,
 26        in RationalSegmentInterval clip, Fixed64 lowerY, Fixed64 upperY,
 27        ref CapsulePlaneRaySelection selection, out CapsulePlaneRayFrame ray, out FixedContactAnchors contact,
 28        out RationalSegmentInterval selectedParameter, out Fixed64 selectedHeight, int verticalNormal = 0,
 29        PrismSphereBoundaryAdmission? admission = null)
 30    {
 9031        ray = default; contact = default; selectedParameter = default; selectedHeight = default; selection.Restore(defau
 1932        if (lowerY > upperY) return false;
 1733        var rotation = new WideConvex2dRelations.RotationFrame2d(yaw);
 1734        WideConvex2dRelations.GetRotatedOffset(rotation, start, out Signed192 ax, out Signed192 az);
 1735        WideConvex2dRelations.GetRotatedOffset(rotation, end, out Signed192 bx, out Signed192 bz);
 3436        Signed192 ex = WideArithmetic.SubtractSigned192(bx, ax), ez = WideArithmetic.SubtractSigned192(bz, az);
 1737        Signed320 edgeSquared = WideArithmetic.AddSigned320(WideArithmetic.MultiplySigned192(ex, ex),
 1738            WideArithmetic.MultiplySigned192(ez, ez));
 1839        if (edgeSquared.IsZero) return false;
 1640        Signed320 orientation = WideArithmetic.MultiplySubtract(WideArithmetic.Difference(end.X, start.X),
 1641            WideArithmetic.Difference(inside.Y, start.Y), WideArithmetic.Difference(end.Y, start.Y),
 1642            WideArithmetic.Difference(inside.X, start.X));
 1743        if (orientation.IsZero) return false;
 44
 1545        Span<Signed576> normal = stackalloc Signed576[3]
 1546            { Signed576.ExtendValue(WideArithmetic.Negate(ez)), default, Signed576.ExtendValue(ex) };
 1547        if (orientation.Sign < 0)
 12148            for (int axis = 0; axis < normal.Length; axis++) normal[axis] = WideArithmetic.SubtractSigned576(default, no
 1549        if (verticalNormal != 0)
 450        { normal[0] = normal[2] = default; normal[1] = Signed576.ExtendValue(Signed192.Signed(verticalNormal)); }
 1551        WideArithmetic.ReduceCommonScale(normal);
 52
 1553        ax = WideArithmetic.AddSigned192(ax, Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 1554            WideArithmetic.Difference(origin.X, center.X), Scale)));
 1555        az = WideArithmetic.AddSigned192(az, Signed192.NarrowProven(WideArithmetic.MultiplySigned192(
 1556            WideArithmetic.Difference(origin.Z, center.Z), Scale)));
 1557        Signed320 projection = WideArithmetic.Negate(WideArithmetic.AddSigned320(
 1558            WideArithmetic.MultiplySigned192(ax, ex), WideArithmetic.MultiplySigned192(az, ez)));
 3059        Signed832 n = Signed832.ExtendValue(projection), d = Signed832.ExtendValue(edgeSquared);
 6060        Signed832 lowerN = default, lowerD = Signed832.ExtendValue(Signed192.Signed(1)), upperN = lowerD, upperD = lower
 1561        if (clip.IsClipped)
 62        {
 1663            lowerN = clip.WideLowerNumerator; lowerD = clip.WideLowerDenominator;
 1664            upperN = clip.WideUpperNumerator; upperD = clip.WideUpperDenominator;
 65        }
 2566        if (n.Sign < 0 || RationalSegmentInterval.CompareParameters(n, d, lowerN, lowerD) < 0) { n = lowerN; d = lowerD;
 1467        else if (RationalSegmentInterval.CompareParameters(n, d, upperN, upperD) > 0) { n = upperN; d = upperD; }
 68
 4569        Signed192 y = WideArithmetic.Difference(center.Y, origin.Y), lower = Signed192.Raw(lowerY), upper = Signed192.Ra
 1770        if (WideArithmetic.SubtractSigned192(y, lower).Sign < 0) y = lower;
 1571        else if (WideArithmetic.SubtractSigned192(y, upper).Sign > 0) y = upper;
 1572        selectedParameter = new(n, d, n, d);
 73        // Clamping to a bounded admitted height proves this local coordinate fits.
 1574        selectedHeight = Fixed64.FromRaw(unchecked((long)y.Low));
 1575        y = WideArithmetic.AddSigned192(y, WideArithmetic.Difference(origin.Y, center.Y));
 76
 77        // Native X/Z coordinates carry Q; the sphere ray consumes doubled raw
 78        // coordinates. Keep the rational span parameter instead of rounding a cut.
 79        const int words = 32;
 3080        Span<ulong> values = stackalloc ulong[14 * words]; Span<int> signs = stackalloc int[14];
 1581        var xPoint = ContactQuadratic.At(values, signs, 0, words);
 1582        var yPoint = ContactQuadratic.At(values, signs, 1, words);
 1583        var zPoint = ContactQuadratic.At(values, signs, 2, words);
 1584        var pointD = ContactQuadratic.At(values, signs, 3, words);
 3085        var parameter = ContactQuadratic.At(values, signs, 4, words); parameter.Set(n);
 3086        var divisor = ContactQuadratic.At(values, signs, 5, words); divisor.Set(d);
 1587        var term = ContactQuadratic.At(values, signs, 6, words);
 1588        ContactQuadratic.Scale(divisor, Signed576.ExtendValue(ax), xPoint);
 3089        ContactQuadratic.Scale(parameter, Signed576.ExtendValue(ex), term); xPoint.Add(term);
 1590        ContactQuadratic.Scale(divisor, Signed576.ExtendValue(az), zPoint);
 3091        ContactQuadratic.Scale(parameter, Signed576.ExtendValue(ez), term); zPoint.Add(term);
 1592        ContactQuadratic.Scale(divisor, Signed576.ExtendValue(Scale), pointD);
 1593        ContactQuadratic.Scale(pointD, Signed576.ExtendValue(y), yPoint);
 1594        Signed832 two = Signed832.ExtendValue(Signed192.Signed(2));
 3095        ContactQuadratic.Scale(xPoint, two, term); term.CopyTo(xPoint);
 3096        ContactQuadratic.Scale(yPoint, two, term); term.CopyTo(yPoint);
 3097        ContactQuadratic.Scale(zPoint, two, term); term.CopyTo(zPoint);
 98
 99        Signed832 plane;
 15100        if (verticalNormal == 0)
 101        {
 13102            plane = WideArithmetic.AddSigned832(WideArithmetic.MultiplySigned576ToSigned832(normal[0], Signed320.ExtendV
 13103                WideArithmetic.MultiplySigned576ToSigned832(normal[2], Signed320.ExtendValue(az)));
 13104            plane = WideArithmetic.AddSigned832(plane, plane);
 143105            for (int axis = 0; axis < normal.Length; axis++) normal[axis] = WideArithmetic.MultiplySigned576(normal[axis
 106        }
 2107        else plane = WideArithmetic.MultiplySigned576ToSigned832(normal[1], Signed320.ExtendValue(WideArithmetic.AddSign
 15108        ray = CapsulePlaneRayFrame.ForSpherePointRay(radius, new WideDirection3d(normal[0], normal[1], normal[2]), plane
 109
 16110        if (admission != null && !admission(selectedParameter, selectedHeight, ray.Normal)) return false;
 15111        if (!CapsulePlaneRayEvents.AccumulateRationalPoint(ray, xPoint, yPoint, zPoint, pointD, 1, ref selection)) retur
 13112        bool sphereRepresented = selection.TryGetExitAnchor(ray, 1, center, FixedQuaternion.Identity,
 13113            out FixedPointAnchor sphere, out Fixed64 depth, out bool clamped);
 13114        bool targetRepresented = selection.TryGetPointInRigidFrame(ray, origin, FixedQuaternion.Identity,
 13115            center, FixedQuaternion.Identity, out Vector3d source);
 116        // Actual finite intersection keeps both witnesses in their admitted
 117        // bounded physical charts, independently of absolute world placement.
 118        System.Diagnostics.Debug.Assert(sphereRepresented && targetRepresented);
 13119        contact = new FixedContactAnchors(sphere, new FixedPointAnchor(origin, FixedQuaternion.Identity, source),
 13120            ray.Normal.GetNormal(false), depth, clamped);
 13121        return true;
 122    }
 123}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.SphereDomain.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.SphereDomain.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>Admits exact native-prism sphere supports before depth ranking.</content>
 13internal static partial class WideConvexPrismRelations
 14{
 15    private static void KeepSphereProjection(Axis3 axis, bool negate, Vector3d offset, ProjectionDepth depth,
 16        RigidAxis3 shapeAxis, Vector3d center, Vector3d origin, Fixed64 yaw, ReadOnlySpan<Vector2d> offsets,
 17        Fixed64 halfThickness, PrismPointAdmission admission, ref PenetrationCandidate best, Fixed64 length,
 18        IPrismCapsuleContactDomain? capsuleDomain, in FixedQuaternion shapeRotation)
 19    {
 1148620        if (best.HasValue && CompareProjectionDepths(depth, best.ExactDepth) >= 0) return;
 979421        var candidate = new PenetrationCandidate(axis, negate, offset, depth);
 979422        FixedPointAnchor unused = default;
 979423        if (TryGetMatchedPrismAnchor(candidate, negate ? Negate(axis) : axis, shapeAxis, center,
 979424            shapeRotation, Vector3d.Up, length, default, GetAxisProjection(negate ? Negate(axis) : axis, shapeAxis).Sign
 979425            FixedQuaternion.Identity, offsets, halfThickness, ref unused, out _, admission, materialize: false,
 979426            capsuleDomain: capsuleDomain))
 3027            best = candidate;
 979428    }
 29
 30    private static bool AdmitPrismSpherePoint(Axis3 normal, in ProjectionDepth depth, ReadOnlySpan<Signed832> projected,
 31        Signed832 pointD, Fixed64 yaw, PrismPointAdmission admission, Signed832 coreN = default, Signed832 coreD = defau
 32    {
 33        const int words = 32;
 53634        Span<ulong> values = stackalloc ulong[20 * words]; Span<int> signs = stackalloc int[20];
 26835        var x = ContactQuadratic.At(values, signs, 0, words);
 26836        var y = ContactQuadratic.At(values, signs, 1, words);
 26837        var z = ContactQuadratic.At(values, signs, 2, words);
 26838        var d = ContactQuadratic.At(values, signs, 3, words);
 26839        var first = ContactQuadratic.At(values, signs, 4, words);
 26840        var second = ContactQuadratic.At(values, signs, 5, words);
 26841        var term = ContactQuadratic.At(values, signs, 6, words);
 26842        var frame = new WideConvex2dRelations.RotationFrame2d(yaw);
 26843        Signed320 metric = WideArithmetic.AddSigned320(WideArithmetic.MultiplySigned192(frame.Cosine, frame.Cosine),
 26844            WideArithmetic.MultiplySigned192(frame.Sine, frame.Sine));
 26845        if (coreD.IsZero)
 66446        { x.Set(projected[0]); y.Set(projected[1]); z.Set(projected[2]); d.Set(pointD); }
 47        else
 48        {
 20449            var parameter = ContactQuadratic.At(values, signs, 7, words); parameter.Set(coreN);
 20450            var remaining = ContactQuadratic.At(values, signs, 8, words); remaining.Set(WideArithmetic.SubtractSigned832
 81651            for (int component = 0; component < 3; component++)
 52            {
 30653                var coordinate = component == 0 ? x : component == 1 ? y : z;
 61254                first.Set(projected[component]); ContactQuadratic.Multiply(first, remaining, ReadOnlySpan<ulong>.Empty, 
 91855                first.Set(projected[component + 3]); ContactQuadratic.Multiply(first, parameter, ReadOnlySpan<ulong>.Emp
 56            }
 30657            first.Set(pointD); second.Set(coreD); ContactQuadratic.Multiply(first, second, ReadOnlySpan<ulong>.Empty, d)
 58        }
 53659        x.CopyTo(first); z.CopyTo(second);
 26860        ContactQuadratic.Scale(first, Signed576.ExtendValue(frame.Cosine), x);
 53661        ContactQuadratic.Scale(second, Signed576.ExtendValue(frame.Sine), term); x.Add(term);
 26862        ContactQuadratic.Scale(first, Signed576.ExtendValue(WideArithmetic.Negate(frame.Sine)), z);
 53663        ContactQuadratic.Scale(second, Signed576.ExtendValue(frame.Cosine), term); z.Add(term);
 53664        x.CopyTo(first); ContactQuadratic.Scale(first, Signed576.ExtendValue(Scale), x);
 53665        z.CopyTo(first); ContactQuadratic.Scale(first, Signed576.ExtendValue(Scale), z);
 53666        y.CopyTo(first); ContactQuadratic.Scale(first, Signed576.ExtendValue(metric), y);
 53667        d.CopyTo(first); ContactQuadratic.Scale(first, Signed576.ExtendValue(metric), d);
 68        // Native scalar Sin/Cos need not have unit squared length. Divide by
 69        // their actual metric; never substitute a rounded quaternion inverse.
 26870        return admission(new WideDirection3d(normal.X, normal.Y, normal.Z), depth, x, y, z, d);
 71    }
 72
 73    private static bool KeepSphereEdgeAxes(Vector3d center, Fixed64 radius, Vector3d origin, Fixed64 yaw,
 74        ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness, RigidAxis3 shapeAxis,
 75        PrismPointAdmission admission, ref PenetrationCandidate best)
 76    {
 1477        var frame = new WideConvex2dRelations.RotationFrame2d(yaw);
 4278        Span<Signed192> a = stackalloc Signed192[3], b = stackalloc Signed192[3], edge = stackalloc Signed192[3];
 1479        Span<Signed320> normal = stackalloc Signed320[3];
 13280        for (int index = 0; index < offsets.Length; index++)
 41881          for (int kind = 0; kind < 3; kind++)
 82          {
 15783            GetPrismEdgeVertex(frame, offsets[index], kind == 1 ? halfThickness : -halfThickness, Signed192.Signed(1), a
 15784            GetPrismEdgeVertex(frame, kind == 2 ? offsets[index] : offsets[(index + 1) % offsets.Length],
 15785                kind == 0 ? -halfThickness : halfThickness, Signed192.Signed(1), b);
 125686            for (int coordinate = 0; coordinate < 3; coordinate++)
 87            {
 47188                edge[coordinate] = WideArithmetic.SubtractSigned192(b[coordinate], a[coordinate]);
 47189                a[coordinate] = WideArithmetic.AddSigned192(a[coordinate], Signed192.NarrowProven(
 47190                    WideArithmetic.MultiplySigned192(WideArithmetic.Difference(origin[coordinate], center[coordinate]), 
 91            }
 15792            Signed320 denominator = Signed320.NarrowValue(PrismEdgeDot(edge, edge));
 15793            if (denominator.IsZero) continue;
 14594            Signed320 numerator = WideArithmetic.Negate(Signed320.NarrowValue(PrismEdgeDot(a, edge)));
 15795            if (numerator.Sign < 0) numerator = default;
 14996            else if (WideArithmetic.SubtractSigned320(numerator, denominator).Sign > 0) numerator = denominator;
 116097            for (int coordinate = 0; coordinate < 3; coordinate++)
 43598                normal[coordinate] = Signed320.NarrowValue(WideArithmetic.AddSigned576(
 43599                    WideArithmetic.MultiplySigned320(denominator, a[coordinate]),
 435100                    WideArithmetic.MultiplySigned320(numerator, edge[coordinate])));
 101            // This internal domain entry is for admitted 4,096-reach shapes
 102            // with overlapping bounds. Native coordinates need <80 bits, Gram
 103            // terms <162 and directions <244; their squares fit Signed576.
 104            // The zero core fixes t=1/2 in the shared witness projection, so
 105            // its <740-bit half-planes fit Signed832 without the free-core bank.
 145106            if (!TryKeepAxis(new(normal[0], normal[1], normal[2]), shapeAxis, center, Fixed64.Zero, radius,
 146107                origin, yaw, offsets, halfThickness, ref best, admission, null, FixedQuaternion.Identity)) return false;
 108          }
 13109        return true;
 110    }
 111}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.Support.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.Support.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.ContactQuadratic;
 10
 11namespace FixedMathSharp.Geometry;
 12
 13/// <content>
 14/// Support types and helpers for convex prism relations, including finite shape kinds,
 15/// radial classifications, and high-precision axis representations used in narrow-phase math.
 16/// </content>
 17internal static partial class WideConvexPrismRelations
 18{
 119    private static readonly Signed192 Scale = Signed192.One;
 20
 21    #region Nested Types
 22
 23    private enum FiniteShapeKind
 24    {
 25        Cylinder,
 26        Cone,
 27    }
 28
 29
 30
 31
 32
 33    private readonly struct RigidAxis3
 34    {
 35        internal readonly Signed192 X;
 36        internal readonly Signed192 Y;
 37        internal readonly Signed192 Z;
 38        internal readonly Signed192 RotationDenominator;
 39
 40        internal RigidAxis3(
 41            Signed192 x,
 42            Signed192 y,
 43            Signed192 z,
 44            Signed192 rotationDenominator)
 45        {
 733146            X = x;
 733147            Y = y;
 733148            Z = z;
 733149            RotationDenominator = rotationDenominator;
 733150        }
 51
 52        internal Axis3 ToWide() =>
 1067753            new(
 1067754                Signed320.ExtendValue(X),
 1067755                Signed320.ExtendValue(Y),
 1067756                Signed320.ExtendValue(Z));
 57    }
 58
 59    private readonly struct PenetrationCandidate
 60    {
 61        internal readonly Axis3 Axis;
 62        internal readonly bool Negate;
 63        internal readonly Vector3d PrismOffset;
 64        internal readonly ProjectionDepth ExactDepth;
 65
 66        internal PenetrationCandidate(
 67            Axis3 axis,
 68            bool negate,
 69            Vector3d prismOffset,
 70            ProjectionDepth exactDepth)
 71        {
 1171372            Axis = axis;
 1171373            Negate = negate;
 1171374            PrismOffset = prismOffset;
 1171375            ExactDepth = exactDepth;
 1171376            HasValue = true;
 1171377        }
 78
 79        internal bool HasValue { get; }
 80    }
 81
 82
 83
 84
 85
 86
 87
 88    #endregion
 89
 90    private static bool TryMaterializeContact(
 91        in PenetrationCandidate candidate,
 92        RigidAxis3 shapeAxis,
 93        Vector3d center,
 94        FixedQuaternion shapeRotation,
 95        Vector3d localAxisDirection,
 96        Fixed64 axisLength,
 97        Fixed64 radius,
 98        Vector3d prismOrigin,
 99        Fixed64 prismYaw,
 100        FixedQuaternion prismOrientation,
 101        ReadOnlySpan<Vector2d> prismOffsets,
 102        Fixed64 prismHalfThickness,
 103        out FixedContactAnchors contact)
 104    {
 460105        Axis3 orientedAxis = candidate.Negate
 460106            ? Negate(candidate.Axis)
 460107            : candidate.Axis;
 460108        Vector3d normal = WideNormalization.GetNormalized(
 460109            Signed576.ExtendValue(orientedAxis.X),
 460110            Signed576.ExtendValue(orientedAxis.Y),
 460111            Signed576.ExtendValue(orientedAxis.Z));
 460112        int axialSign = GetAxisProjection(
 460113            orientedAxis,
 460114            shapeAxis).Sign;
 460115        FixedPointAnchor shapeAnchor = WideGeometry.GetCenteredCapsuleSupportAnchor(
 460116            center,
 460117            shapeRotation,
 460118            localAxisDirection,
 460119            axisLength,
 460120            Fixed64.Zero,
 460121            normal,
 460122            axialSign);
 460123        if (!TryGetMatchedPrismAnchor(candidate, orientedAxis, shapeAxis,
 460124                center, shapeRotation, localAxisDirection, axisLength, normal, axialSign,
 460125                prismOrigin, prismYaw, prismOrientation, prismOffsets, prismHalfThickness,
 460126                ref shapeAnchor, out FixedPointAnchor prismAnchor))
 38127            return TryGetPrismEdgeContact(shapeAxis, center, shapeRotation, axisLength,
 38128                radius, prismOrigin, prismYaw, prismOrientation, prismOffsets, prismHalfThickness, out contact);
 422129        shapeAnchor = new FixedPointAnchor(center, shapeRotation, shapeAnchor.LocalPoint,
 422130            GetPrismCapsuleRadialOffset(orientedAxis, shapeRotation, radius), shapeAnchor.ExactLocalTerm);
 422131        Fixed64 depth = GetRoundedDepth(candidate.ExactDepth, out bool depthIsClamped);
 422132        contact = new FixedContactAnchors(shapeAnchor, prismAnchor, normal, depth, depthIsClamped);
 422133        return true;
 134    }
 135
 136    private static bool TryGetMatchedPrismAnchor(in PenetrationCandidate candidate, Axis3 normal,
 137        RigidAxis3 shapeAxis, Vector3d center, FixedQuaternion rotation, Vector3d localAxis,
 138        Fixed64 length, Vector3d direction, int axialSign, Vector3d origin, Fixed64 yaw,
 139        FixedQuaternion orientation, ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness,
 140        ref FixedPointAnchor shapeAnchor, out FixedPointAnchor prismAnchor,
 141        PrismPointAdmission? admission = null, bool materialize = true,
 142        IPrismCapsuleContactDomain? capsuleDomain = null)
 143    {
 10430144        prismAnchor = default;
 10430145        if (capsuleDomain != null && !capsuleDomain.IncludeRecovery)
 146        {
 147            // A physical supporting plane must intersect the capsule's complete
 148            // projected width. Subtracting its exit depth leaves one radial term,
 149            // which the existing exact projection-sign owner can classify.
 202150            Signed704 axial = GetMagnitude(WideArithmetic.MultiplySigned576ToSigned704(
 202151                GetAxisProjection(normal, shapeAxis), Signed320.ExtendValue(Signed192.Raw(length))));
 202152            var depth = candidate.ExactDepth;
 202153            var remaining = new ProjectionDepth(WideArithmetic.SubtractSigned704(
 202154                WideArithmetic.AddSigned704(axial, axial), depth.Rational), depth.Common,
 202155                depth.Radius, depth.RadialKind, depth.AxisSquared, depth.ShapeAxisSquared);
 353156            if (!IsProjectionNonNegative(remaining)) return false;
 157        }
 10279158        Span<Signed832> projected = stackalloc Signed832[6];
 10279159        if (!TryGetPrismSupportInterval(candidate, normal, shapeAxis, center, length, axialSign, origin, yaw,
 18916160                offsets, halfThickness, projected, out Signed832 pointD, out var solidClip)) return false;
 3284161        Signed832 tN = Signed832.ExtendValue(Signed192.Signed(1)), tD = Signed832.ExtendValue(Signed192.Signed(2));
 1642162        if (capsuleDomain != null)
 163        {
 164            // A fixed tip projects to one point. A free core is parallel to
 165            // the plane, so radial displacement is constant over the whole family.
 1054166            if (!capsuleDomain.IncludeRecovery && !IsProjectedPrismPointWithinCapsule(projected, pointD,
 1057167                    tN, tD, center - origin, rotation, length, candidate.ExactDepth.Radius)) return false;
 1051168            var wideNormal = new WideDirection3d(normal.X, normal.Y, normal.Z);
 1051169            ReadOnlySpan<RationalSegmentInterval> intervals = capsuleDomain.GetSupportIntervals(wideNormal, projected, p
 2162170            foreach (RationalSegmentInterval retained in intervals)
 40171                if (TryGetPrismDomainParameter(normal, candidate.ExactDepth, projected, pointD, yaw,
 60172                        retained, admission!, out tN, out tD)) return true;
 1031173            return false;
 174        }
 730175        if (admission != null && !AdmitPrismSpherePoint(normal, candidate.ExactDepth, projected, pointD, yaw, admission)
 470176        if (!materialize) return true;
 422177        if (WideArithmetic.SubtractSigned832(WideArithmetic.AddSigned832(
 422178                solidClip.WideLowerNumerator, solidClip.WideLowerNumerator), solidClip.WideLowerDenominator).Sign > 0)
 84179        { tN = solidClip.WideLowerNumerator; tD = solidClip.WideLowerDenominator; }
 380180        else if (WideArithmetic.SubtractSigned832(WideArithmetic.AddSigned832(
 380181                solidClip.WideUpperNumerator, solidClip.WideUpperNumerator), solidClip.WideUpperDenominator).Sign < 0)
 74182        { tN = solidClip.WideUpperNumerator; tD = solidClip.WideUpperDenominator; }
 422183        if (axialSign == 0 && length > Fixed64.Zero)
 187184            shapeAnchor = GetPrismCoreAnchor(shapeAnchor, localAxis, length, direction, tN, tD);
 422185        prismAnchor = MaterializePrismProjection(projected, pointD, tN, tD, origin, orientation);
 422186        return true;
 187    }
 188
 189    private static bool TryGetPrismDomainParameter(Axis3 normal, in ProjectionDepth depth,
 190        ReadOnlySpan<Signed832> projected, Signed832 pointD, Fixed64 yaw,
 191        in RationalSegmentInterval retained, PrismPointAdmission admission, out Signed832 tN, out Signed832 tD)
 192    {
 118193        tN = Signed832.ExtendValue(Signed192.Signed(1)); tD = Signed832.ExtendValue(Signed192.Signed(2));
 59194        if (retained.IsClipped)
 195        {
 21196            if (RationalSegmentInterval.CompareParameters(tN, tD, retained.WideLowerNumerator, retained.WideLowerDenomin
 14197            { tN = retained.WideLowerNumerator; tD = retained.WideLowerDenominator; }
 14198            else if (RationalSegmentInterval.CompareParameters(tN, tD, retained.WideUpperNumerator, retained.WideUpperDe
 12199            { tN = retained.WideUpperNumerator; tD = retained.WideUpperDenominator; }
 200        }
 95201        if (AdmitPrismSpherePoint(normal, depth, projected, pointD, yaw, admission, tN, tD)) return true;
 23202        GetPrismInteriorParameter(retained, out tN, out tD);
 23203        return AdmitPrismSpherePoint(normal, depth, projected, pointD, yaw, admission, tN, tD);
 204    }
 205
 206    private static FixedPointAnchor GetPrismCoreAnchor(in FixedPointAnchor anchor, Vector3d localAxis,
 207        Fixed64 length, Vector3d direction, Signed832 tN, Signed832 tD)
 208    {
 216209        Signed832 axialN = WideArithmetic.MultiplySigned832(WideArithmetic.SubtractSigned832(
 216210            WideArithmetic.AddSigned832(tN, tN), tD), Signed192.Raw(length));
 216211        Signed832 axialD = WideArithmetic.AddSigned832(tD, tD);
 216212        Signed832 magnitude = axialN.Sign < 0 ? WideArithmetic.SubtractSigned832(default, axialN) : axialN;
 216213        bool endpoint = WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned832(axialD, Signed192.Raw(length))
 216214            WideArithmetic.AddSigned832(magnitude, magnitude)).IsZero;
 216215        return endpoint ? WideGeometry.GetCenteredCapsuleSupportAnchor(anchor.Origin, anchor.Rotation, localAxis,
 216216            length, Fixed64.Zero, direction, axialN.Sign)
 216217            : new FixedPointAnchor(anchor.Origin, anchor.Rotation, WideTriangleRelations.MaterializeCorePoint(localAxis,
 216218                anchor.LocalDisplacement, anchor.ExactLocalTerm);
 219    }
 220
 221    private static bool TryGetPrismSupportInterval(in PenetrationCandidate candidate, Axis3 normal,
 222        RigidAxis3 shapeAxis, Vector3d center, Fixed64 length, int axialSign, Vector3d origin, Fixed64 yaw,
 223        ReadOnlySpan<Vector2d> offsets, Fixed64 halfThickness, Span<Signed832> projected,
 224        out Signed832 pointD, out RationalSegmentInterval solidClip)
 225    {
 10296226        solidClip = default;
 227        // Ordinary capsule axes fit 201 bits: projection N/D 571/503,
 228        // half-planes 704 and free-core parameters 803. Bounded zero-core sphere
 229        // axes fit 244 bits and keep fixed-core projections within Signed832.
 10296230        var frame = new WideConvex2dRelations.RotationFrame2d(yaw);
 10296231        GetPrismSupportProjection(candidate.PrismOffset, normal, shapeAxis, center, length, axialSign, origin,
 10296232            frame, projected, out pointD, out Signed192 vx, out Signed192 vz);
 233        // Projection removes the radial term. Clip the finite core against the
 234        // actual native-yaw prism, never its rounded quaternion approximation.
 20592235        Signed832 lowerN = default, lowerD = Signed832.ExtendValue(Signed192.Signed(1));
 20592236        Signed832 upperN = lowerD, upperD = lowerD;
 10296237        int winding = WideConvex2dRelations.GetPlanarSweepWinding(offsets);
 41184238        Signed192 minX = vx, maxX = vx, minZ = vz, maxZ = vz;
 62212239        for (int index = 0; index < offsets.Length; index++)
 240        {
 28501241            WideConvex2dRelations.GetRotatedOffset(frame, offsets[index], out Signed192 x, out Signed192 z);
 28501242            WideConvex2dRelations.GetRotatedOffset(frame, offsets[index + 1 == offsets.Length ? 0 : index + 1],
 28501243                out Signed192 nextX, out Signed192 nextZ);
 33246244            if (WideArithmetic.SubtractSigned192(x, minX).Sign < 0) minX = x;
 32739245            if (WideArithmetic.SubtractSigned192(x, maxX).Sign > 0) maxX = x;
 32507246            if (WideArithmetic.SubtractSigned192(z, minZ).Sign < 0) minZ = z;
 31644247            if (WideArithmetic.SubtractSigned192(z, maxZ).Sign > 0) maxZ = z;
 57002248            Signed192 dx = WideArithmetic.SubtractSigned192(nextX, x), dz = WideArithmetic.SubtractSigned192(nextZ, z);
 28501249            Signed832 first = GetPrismEdgeConstraint(projected[..3], pointD, x, z, dx, dz);
 28501250            Signed832 second = GetPrismEdgeConstraint(projected[3..], pointD, x, z, dx, dz);
 28645251            if (winding < 0) { first = WideArithmetic.SubtractSigned832(default, first); second = WideArithmetic.Subtrac
 28501252            if (!WideTriangleRelations.ClipScaledHalfPlane(first, second, ref lowerN, ref lowerD, ref upperN, ref upperD
 7691253                return false;
 254        }
 2605255        Signed192 height = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(Signed192.Raw(halfThickness), Scale))
 2605256        ReadOnlySpan<Signed192> minimum = stackalloc Signed192[3] { minX, WideArithmetic.Negate(height), minZ };
 2605257        ReadOnlySpan<Signed192> maximum = stackalloc Signed192[3] { maxX, height, maxZ };
 17048258        for (int axis = 0; axis < 3; axis++)
 259        {
 260            // Real polygon edges bound X/Z already; collapsed footprints also
 261            // need their finite endpoint bounds along the surviving line.
 6865262            if (winding != 0 && axis != 1) continue;
 2645263            Signed832 first = WideArithmetic.MultiplySigned832(projected[axis], Scale);
 2645264            Signed832 second = WideArithmetic.MultiplySigned832(projected[axis + 3], Scale);
 2645265            Signed832 min = WideArithmetic.MultiplySigned832(pointD, minimum[axis]);
 2645266            Signed832 max = WideArithmetic.MultiplySigned832(pointD, maximum[axis]);
 2645267            if (!WideTriangleRelations.ClipScaledHalfPlane(WideArithmetic.SubtractSigned832(first, min),
 2645268                    WideArithmetic.SubtractSigned832(second, min), ref lowerN, ref lowerD, ref upperN, ref upperD)
 2645269                || !WideTriangleRelations.ClipScaledHalfPlane(WideArithmetic.SubtractSigned832(max, first),
 2645270                    WideArithmetic.SubtractSigned832(max, second), ref lowerN, ref lowerD, ref upperN, ref upperD))
 946271                return false;
 272        }
 1659273        solidClip = new RationalSegmentInterval(lowerN, lowerD, upperN, upperD);
 1659274        return true;
 275    }
 276
 277    private static void GetPrismSupportProjection(Vector3d vertexOffset, Axis3 normal, RigidAxis3 shapeAxis,
 278        Vector3d center, Fixed64 length, int axialSign, Vector3d origin, WideConvex2dRelations.RotationFrame2d frame,
 279        Span<Signed832> projected, out Signed832 pointD, out Signed192 vx, out Signed192 vz)
 280    {
 10298281        Signed192 twiceRotation = WideArithmetic.AddSigned192(shapeAxis.RotationDenominator, shapeAxis.RotationDenominat
 10298282        Signed192 coreD = Signed192.NarrowProven(WideArithmetic.MultiplySigned192(twiceRotation, Scale));
 10298283        ReadOnlySpan<Signed320> n = stackalloc Signed320[3] { normal.X, normal.Y, normal.Z };
 10298284        ReadOnlySpan<Signed192> a = stackalloc Signed192[3] { shapeAxis.X, shapeAxis.Y, shapeAxis.Z };
 10298285        Signed576 metric = WideArithmetic.AddSigned576(WideArithmetic.AddSigned576(
 10298286            WideArithmetic.MultiplySigned320(n[0], n[0]), WideArithmetic.MultiplySigned320(n[1], n[1])),
 10298287            WideArithmetic.MultiplySigned320(n[2], n[2]));
 10298288        WideConvex2dRelations.GetRotatedOffset(frame, new Vector2d(vertexOffset.X, vertexOffset.Z),
 10298289            out vx, out vz);
 10298290        ReadOnlySpan<Signed192> vertex = stackalloc Signed192[3]
 10298291            { vx, Signed192.NarrowProven(WideArithmetic.MultiplySigned192(Signed192.Raw(vertexOffset.Y), Scale)), vz };
 20596292        Span<Signed576> core = stackalloc Signed576[6], support = stackalloc Signed576[3];
 10298293        bool freeCore = axialSign == 0 && length > Fixed64.Zero;
 82384294        for (int axis = 0; axis < 3; axis++)
 295        {
 30894296            Signed320 midpoint = WideArithmetic.MultiplySigned192(WideArithmetic.Difference(center[axis], origin[axis]),
 30894297            Signed320 half = WideArithmetic.MultiplySigned192(a[axis], Signed192.Raw(length));
 30894298            core[axis] = Signed576.ExtendValue(WideArithmetic.AddSigned320(midpoint,
 30894299                freeCore ? WideArithmetic.Negate(half) : axialSign < 0 ? WideArithmetic.Negate(half) : axialSign > 0 ? h
 30894300            core[axis + 3] = freeCore ? Signed576.ExtendValue(WideArithmetic.AddSigned320(midpoint, half)) : core[axis];
 30894301            support[axis] = Signed576.ExtendValue(WideArithmetic.MultiplySigned192(vertex[axis], twiceRotation));
 302        }
 10298303        Signed576 plane = DotPrismSupport(support, n);
 61788304        for (int end = 0; end < 2; end++)
 305        {
 20596306            Signed576 distance = WideArithmetic.SubtractSigned576(plane, DotPrismSupport(core.Slice(3 * end, 3), n));
 164768307            for (int axis = 0; axis < 3; axis++)
 61788308                projected[3 * end + axis] = WideArithmetic.AddSigned832(
 61788309                    WideArithmetic.MultiplySigned576ToSigned832(metric, core[3 * end + axis]),
 61788310                    WideArithmetic.MultiplySigned576ToSigned832(distance, n[axis]));
 311        }
 10298312        pointD = WideArithmetic.MultiplySigned576ToSigned832(metric, Signed320.ExtendValue(coreD));
 10298313    }
 314
 315    internal static void GetProjectedCapsuleSupport(Vector3d center, FixedQuaternion rotation, Fixed64 length,
 316        Vector3d origin, Fixed64 yaw, Vector3d pointOnPlane, in WideDirection3d normal,
 317        Span<Signed832> projected, out Signed832 denominator)
 318    {
 2319        WideOrientedBox.GetRotatedLocalAxisNumerators(rotation, Vector3d.Up, out var x, out var y, out var z, out var d)
 2320        var shapeAxis = new RigidAxis3(x, y, z, d);
 2321        var direction = new Axis3(Signed320.NarrowValue(normal.X), Signed320.NarrowValue(normal.Y), Signed320.NarrowValu
 2322        GetPrismSupportProjection(pointOnPlane, direction, shapeAxis, center, length,
 2323            GetAxisProjection(direction, shapeAxis).Sign, origin, new(yaw), projected, out denominator, out _, out _);
 2324    }
 325
 326    private static Vector3d GetPrismCapsuleRadialOffset(Axis3 normal, FixedQuaternion rotation, Fixed64 radius)
 327    {
 439328        WideRigidProjection.GetLocalAxisProjections(new WideAxis3(normal.X, normal.Y, normal.Z),
 439329            new WideRationalBasis3d(rotation), out Signed576 x, out Signed576 y, out Signed576 z);
 330        // Scale the exact local direction before rounding: a rounded unit
 331        // vector's error would otherwise grow with full-domain radii.
 332        const int words = 9;
 878333        Span<ulong> values = stackalloc ulong[7 * words]; Span<int> signs = stackalloc int[7];
 878334        values.Clear(); signs.Clear();
 878335        WideArithmetic.GetMagnitude(x, values[..words]); signs[0] = x.Sign;
 878336        WideArithmetic.GetMagnitude(y, values.Slice(words, words)); signs[1] = y.Sign;
 878337        WideArithmetic.GetMagnitude(z, values.Slice(2 * words, words)); signs[2] = z.Sign;
 439338        return GetConvexContactCandidateScaledNormal(new ConvexContactCandidate(values, signs, 1, words), radius);
 339    }
 340
 341    private static Signed576 DotPrismSupport(ReadOnlySpan<Signed576> point, ReadOnlySpan<Signed320> axis) =>
 30894342        Signed576.NarrowValue(WideArithmetic.AddSigned832(WideArithmetic.AddSigned832(
 30894343            WideArithmetic.MultiplySigned576ToSigned832(point[0], axis[0]),
 30894344            WideArithmetic.MultiplySigned576ToSigned832(point[1], axis[1])),
 30894345            WideArithmetic.MultiplySigned576ToSigned832(point[2], axis[2])));
 346
 347    private static Signed832 GetPrismEdgeConstraint(ReadOnlySpan<Signed832> point, Signed832 denominator,
 348        Signed192 x, Signed192 z, Signed192 dx, Signed192 dz) =>
 57002349        WideArithmetic.SubtractSigned832(WideArithmetic.MultiplySigned832(WideArithmetic.SubtractSigned832(
 57002350                WideArithmetic.MultiplySigned832(point[2], Scale), WideArithmetic.MultiplySigned832(denominator, z)), dx
 57002351            WideArithmetic.MultiplySigned832(WideArithmetic.SubtractSigned832(
 57002352                WideArithmetic.MultiplySigned832(point[0], Scale), WideArithmetic.MultiplySigned832(denominator, x)), dz
 353
 354    private static FixedPointAnchor MaterializePrismProjection(ReadOnlySpan<Signed832> projected, Signed832 pointD,
 355        Signed832 tN, Signed832 tD, Vector3d origin, FixedQuaternion rotation)
 356    {
 357        const int words = 32;
 906358        Span<ulong> values = stackalloc ulong[22 * words]; Span<int> signs = stackalloc int[22];
 906359        ContactQuadratic first = At(values, signs, 3, words), second = At(values, signs, 4, words),
 906360            remaining = At(values, signs, 5, words), parameter = At(values, signs, 6, words),
 906361            denominator = At(values, signs, 7, words), localD = At(values, signs, 8, words),
 906362            numerator = At(values, signs, 9, words), term = At(values, signs, 10, words);
 906363        remaining.Set(WideArithmetic.SubtractSigned832(tD, tN)); parameter.Set(tN);
 3624364        for (int axis = 0; axis < 3; axis++)
 365        {
 2718366            first.Set(projected[axis]); second.Set(projected[axis + 3]);
 1359367            ContactQuadratic coordinate = At(values, signs, axis, words);
 1359368            Multiply(first, remaining, ReadOnlySpan<ulong>.Empty, coordinate);
 2718369            Multiply(second, parameter, ReadOnlySpan<ulong>.Empty, term); coordinate.Add(term);
 370        }
 1359371        first.Set(pointD); second.Set(tD); Multiply(first, second, ReadOnlySpan<ulong>.Empty, denominator);
 453372        WideRationalBasis3d basis = new(rotation);
 453373        ContactQuadratic.Scale(denominator, Signed576.ExtendValue(basis.Denominator), localD);
 453374        ReadOnlySpan<Signed192> coefficients = stackalloc Signed192[9]
 453375            { basis.Xx, basis.Xy, basis.Xz, basis.Yx, basis.Yy, basis.Yz, basis.Zx, basis.Zy, basis.Zz };
 906376        Vector3d point = default, displacement = default;
 3624377        for (int axis = 0; axis < 3; axis++)
 378        {
 1359379            numerator.Clear();
 10872380            for (int component = 0; component < 3; component++)
 381            {
 4077382                ContactQuadratic.Scale(At(values, signs, component, words), Signed576.ExtendValue(coefficients[3 * axis 
 4077383                numerator.Add(term);
 384            }
 1359385            SplitRound(numerator, localD, ReadOnlySpan<ulong>.Empty, out Fixed64 a, out Fixed64 b);
 2718386            point[axis] = a; displacement[axis] = b;
 387        }
 453388        return new FixedPointAnchor(origin, rotation, point, displacement);
 389    }
 390
 391    private static Axis3 FromDirection(Vector3d direction) =>
 1725392        new(
 1725393            Signed320.ExtendValue(Signed192.Raw(direction.X)),
 1725394            Signed320.ExtendValue(Signed192.Raw(direction.Y)),
 1725395            Signed320.ExtendValue(Signed192.Raw(direction.Z)));
 396
 397    private static int Compare(Signed576 left, Signed576 right) =>
 123178398        WideArithmetic.SubtractSigned576(left, right).Sign;
 399
 400    private static Axis3 Negate(Axis3 axis) =>
 9897401        new(
 9897402            WideArithmetic.Negate(axis.X),
 9897403            WideArithmetic.Negate(axis.Y),
 9897404            WideArithmetic.Negate(axis.Z));
 405}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/Convex/WideConvexPrismRelations.SweptUprightCylinder.cs

#LineLine coverage
 1//=======================================================================
 2// WideConvexPrismRelations.SweptUprightCylinder.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;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Exact joint planar and vertical relations for swept upright cylinders.
 14/// </content>
 15internal static partial class WideConvexPrismRelations
 16{
 17    private readonly struct SweepPlanarPath
 18    {
 19        internal readonly Signed192 StartX;
 20        internal readonly Signed192 StartZ;
 21        internal readonly Signed192 VelocityX;
 22        internal readonly Signed192 VelocityZ;
 23
 24        internal SweepPlanarPath(
 25            Vector3d start,
 26            Vector3d end,
 27            Vector3d prismOrigin)
 28        {
 15729            StartX = WideArithmetic.SubtractSigned192(
 15730                WideArithmetic.Scale(start.X),
 15731                WideArithmetic.Scale(prismOrigin.X));
 15732            StartZ = WideArithmetic.SubtractSigned192(
 15733                WideArithmetic.Scale(start.Z),
 15734                WideArithmetic.Scale(prismOrigin.Z));
 15735            Signed192 endX = WideArithmetic.SubtractSigned192(
 15736                WideArithmetic.Scale(end.X),
 15737                WideArithmetic.Scale(prismOrigin.X));
 15738            Signed192 endZ = WideArithmetic.SubtractSigned192(
 15739                WideArithmetic.Scale(end.Z),
 15740                WideArithmetic.Scale(prismOrigin.Z));
 15741            VelocityX = WideArithmetic.SubtractSigned192(endX, StartX);
 15742            VelocityZ = WideArithmetic.SubtractSigned192(endZ, StartZ);
 15743        }
 44    }
 45
 46    private struct SweepParameterDomain
 47    {
 48        internal Signed320 LowerNumerator;
 49        internal Signed320 LowerDenominator;
 50        internal Signed320 UpperNumerator;
 51        internal Signed320 UpperDenominator;
 52        internal bool IsEmpty;
 53
 16054        internal static SweepParameterDomain Unit => new()
 16055        {
 16056            LowerDenominator = Signed320.One,
 16057            UpperNumerator = Signed320.One,
 16058            UpperDenominator = Signed320.One,
 16059        };
 60
 61        internal bool ClipStrictLinear(
 62            Signed320 constant,
 63            Signed320 velocity)
 64        {
 177465            if (velocity.Sign > 0)
 66            {
 41467                KeepLower(
 41468                    WideArithmetic.Negate(constant),
 41469                    velocity);
 70            }
 136071            else if (velocity.Sign < 0)
 72            {
 41373                KeepUpper(
 41374                    constant,
 41375                    WideArithmetic.Negate(velocity));
 76            }
 77            else
 78            {
 94779                IsEmpty = constant.Sign <= 0;
 80            }
 81
 177482            return !IsEmpty;
 83        }
 84
 85        private void KeepLower(
 86            Signed320 numerator,
 87            Signed320 denominator)
 88        {
 41489            int comparison = CompareRatios(
 41490                numerator,
 41491                denominator,
 41492                LowerNumerator,
 41493                LowerDenominator);
 41494            if (comparison > 0)
 95            {
 27496                LowerNumerator = numerator;
 27497                LowerDenominator = denominator;
 98            }
 99
 414100            ValidateOrder();
 414101        }
 102
 103        private void KeepUpper(
 104            Signed320 numerator,
 105            Signed320 denominator)
 106        {
 413107            int comparison = CompareRatios(
 413108                numerator,
 413109                denominator,
 413110                UpperNumerator,
 413111                UpperDenominator);
 413112            if (comparison < 0)
 113            {
 146114                UpperNumerator = numerator;
 146115                UpperDenominator = denominator;
 116            }
 117
 413118            ValidateOrder();
 413119        }
 120
 121        private void ValidateOrder()
 122        {
 827123            int comparison = CompareRatios(
 827124                LowerNumerator,
 827125                LowerDenominator,
 827126                UpperNumerator,
 827127                UpperDenominator);
 827128            IsEmpty = comparison >= 0;
 827129        }
 130    }
 131
 132    internal static bool IntersectsSweptUprightCylinderStrict(
 133        Vector3d bottomStart,
 134        Vector3d bottomEnd,
 135        Fixed64 radius,
 136        Fixed64 height,
 137        Vector3d prismOrigin,
 138        Fixed64 prismRotation,
 139        ReadOnlySpan<Vector2d> prismLocalOffsets,
 140        Fixed64 prismHalfThickness)
 141    {
 160142        SweepParameterDomain verticalDomain = SweepParameterDomain.Unit;
 160143        if (!ClipStrictVerticalOverlap(
 160144                bottomStart.Y,
 160145                bottomEnd.Y,
 160146                height,
 160147                prismOrigin.Y,
 160148                prismHalfThickness,
 160149                ref verticalDomain))
 150        {
 2151            return false;
 152        }
 153
 158154        int winding = GetSweepFootprintWinding(prismLocalOffsets);
 158155        if (winding == 0)
 1156            return false;
 157157        return IntersectsStrictPlanarSweep(
 157158            new SweepPlanarPath(
 157159                bottomStart,
 157160                bottomEnd,
 157161                prismOrigin),
 157162            new WideConvex2dRelations.RotationFrame2d(prismRotation),
 157163            radius,
 157164            prismLocalOffsets,
 157165            winding,
 157166            verticalDomain);
 167    }
 168
 169    private static bool ClipStrictVerticalOverlap(
 170        Fixed64 bottomStart,
 171        Fixed64 bottomEnd,
 172        Fixed64 height,
 173        Fixed64 prismCenter,
 174        Fixed64 prismHalfThickness,
 175        ref SweepParameterDomain domain)
 176    {
 160177        Signed192 velocity = WideArithmetic.Difference(
 160178            bottomEnd,
 160179            bottomStart);
 160180        Signed192 topAboveLower = WideArithmetic.AddSigned192(
 160181            WideArithmetic.SubtractSigned192(
 160182                WideArithmetic.AddSigned192(
 160183                    Signed192.Raw(bottomStart),
 160184                    Signed192.Raw(height)),
 160185                Signed192.Raw(prismCenter)),
 160186            Signed192.Raw(prismHalfThickness));
 160187        if (!domain.ClipStrictLinear(
 160188                Signed320.ExtendValue(topAboveLower),
 160189                Signed320.ExtendValue(velocity)))
 190        {
 1191            return false;
 192        }
 193
 159194        Signed192 upperAboveBottom = WideArithmetic.SubtractSigned192(
 159195            WideArithmetic.AddSigned192(
 159196                Signed192.Raw(prismCenter),
 159197                Signed192.Raw(prismHalfThickness)),
 159198            Signed192.Raw(bottomStart));
 159199        return domain.ClipStrictLinear(
 159200            Signed320.ExtendValue(upperAboveBottom),
 159201            Signed320.ExtendValue(WideArithmetic.Negate(velocity)));
 202    }
 203
 204    private static bool IntersectsStrictPlanarSweep(
 205        SweepPlanarPath path,
 206        WideConvex2dRelations.RotationFrame2d rotation,
 207        Fixed64 radius,
 208        ReadOnlySpan<Vector2d> prismLocalOffsets,
 209        int winding,
 210        SweepParameterDomain verticalDomain)
 211    {
 157212        Signed192 scaledRadius = WideArithmetic.Scale(radius);
 157213        Signed320 radiusSquared = WideArithmetic.MultiplySigned192(
 157214            scaledRadius,
 157215            scaledRadius);
 157216        SweepParameterDomain centerlineDomain = verticalDomain;
 1304217        for (int index = 0; index < prismLocalOffsets.Length; index++)
 218        {
 630219            GetSweepEdge(
 630220                path,
 630221                rotation,
 630222                prismLocalOffsets[index],
 630223                prismLocalOffsets[
 630224                    index + 1 == prismLocalOffsets.Length ? 0 : index + 1],
 630225                out Signed192 pointX,
 630226                out Signed192 pointZ,
 630227                out Signed192 velocityX,
 630228                out Signed192 velocityZ,
 630229                out Signed192 edgeX,
 630230                out Signed192 edgeZ);
 630231            if (radius > Fixed64.Zero
 630232                && (IntersectsOpenCircle(
 630233                        pointX,
 630234                        pointZ,
 630235                        velocityX,
 630236                        velocityZ,
 630237                        radiusSquared,
 630238                        verticalDomain)
 630239                    || (!(edgeX.IsZero && edgeZ.IsZero)
 630240                        && IntersectsOpenEdgeStrip(
 630241                            pointX,
 630242                            pointZ,
 630243                            velocityX,
 630244                            velocityZ,
 630245                            edgeX,
 630246                            edgeZ,
 630247                            radiusSquared,
 630248                            verticalDomain))))
 249            {
 135250                return true;
 251            }
 495252            if (centerlineDomain.IsEmpty
 495253                || (edgeX.IsZero && edgeZ.IsZero))
 254                continue;
 255
 484256            Signed320 constant = Cross(
 484257                edgeX,
 484258                edgeZ,
 484259                pointX,
 484260                pointZ);
 484261            Signed320 velocity = Cross(
 484262                edgeX,
 484263                edgeZ,
 484264                velocityX,
 484265                velocityZ);
 484266            if (winding < 0)
 267            {
 19268                constant = WideArithmetic.Negate(constant);
 19269                velocity = WideArithmetic.Negate(velocity);
 270            }
 484271            _ = centerlineDomain.ClipStrictLinear(
 484272                constant,
 484273                velocity);
 274        }
 275
 22276        return !centerlineDomain.IsEmpty;
 277    }
 278
 279    private static bool IntersectsOpenCircle(
 280        Signed192 pointX,
 281        Signed192 pointZ,
 282        Signed192 velocityX,
 283        Signed192 velocityZ,
 284        Signed320 radiusSquared,
 285        SweepParameterDomain domain)
 286    {
 557287        Signed320 a = Dot(
 557288            velocityX,
 557289            velocityZ,
 557290            velocityX,
 557291            velocityZ);
 557292        Signed320 b = Dot(
 557293            pointX,
 557294            pointZ,
 557295            velocityX,
 557296            velocityZ);
 557297        Signed320 c = WideArithmetic.SubtractSigned320(
 557298            Dot(pointX, pointZ, pointX, pointZ),
 557299            radiusSquared);
 557300        return HasStrictNegativeQuadratic(
 557301            Signed576.ExtendValue(a),
 557302            Signed576.ExtendValue(b),
 557303            Signed576.ExtendValue(c),
 557304            domain);
 305    }
 306
 307    private static bool IntersectsOpenEdgeStrip(
 308        Signed192 pointX,
 309        Signed192 pointZ,
 310        Signed192 velocityX,
 311        Signed192 velocityZ,
 312        Signed192 edgeX,
 313        Signed192 edgeZ,
 314        Signed320 radiusSquared,
 315        SweepParameterDomain domain)
 316    {
 554317        Signed320 edgeSquared = Dot(
 554318            edgeX,
 554319            edgeZ,
 554320            edgeX,
 554321            edgeZ);
 554322        Signed320 projection = Dot(
 554323            pointX,
 554324            pointZ,
 554325            edgeX,
 554326            edgeZ);
 554327        Signed320 projectionVelocity = Dot(
 554328            velocityX,
 554329            velocityZ,
 554330            edgeX,
 554331            edgeZ);
 554332        if (!domain.ClipStrictLinear(projection, projectionVelocity)
 554333            || !domain.ClipStrictLinear(
 554334                WideArithmetic.SubtractSigned320(
 554335                    edgeSquared,
 554336                    projection),
 554337                WideArithmetic.Negate(projectionVelocity)))
 338        {
 276339            return false;
 340        }
 341
 278342        Signed320 cross = Cross(
 278343            edgeX,
 278344            edgeZ,
 278345            pointX,
 278346            pointZ);
 278347        Signed320 crossVelocity = Cross(
 278348            edgeX,
 278349            edgeZ,
 278350            velocityX,
 278351            velocityZ);
 278352        Signed576 a = WideArithmetic.MultiplySigned320(
 278353            crossVelocity,
 278354            crossVelocity);
 278355        Signed576 b = WideArithmetic.MultiplySigned320(
 278356            cross,
 278357            crossVelocity);
 278358        Signed576 c = WideArithmetic.SubtractSigned576(
 278359            WideArithmetic.MultiplySigned320(cross, cross),
 278360            WideArithmetic.MultiplySigned320(
 278361                radiusSquared,
 278362                edgeSquared));
 278363        return HasStrictNegativeQuadratic(a, b, c, domain);
 364    }
 365
 366    private static bool HasStrictNegativeQuadratic(
 367        Signed576 a,
 368        Signed576 b,
 369        Signed576 c,
 370        SweepParameterDomain domain)
 371    {
 835372        if (a.IsZero || b.Sign >= 0)
 373        {
 30374            return EvaluateQuadratic(
 30375                a,
 30376                b,
 30377                c,
 30378                domain.LowerNumerator,
 30379                domain.LowerDenominator).Sign < 0;
 380        }
 381
 805382        Signed576 vertexNumerator = WideArithmetic.SubtractSigned576(
 805383            default,
 805384            b);
 805385        int lowerComparison = CompareRatioToBound(
 805386            vertexNumerator,
 805387            a,
 805388            domain.LowerNumerator,
 805389            domain.LowerDenominator);
 805390        int upperComparison = CompareRatioToBound(
 805391            vertexNumerator,
 805392            a,
 805393            domain.UpperNumerator,
 805394            domain.UpperDenominator);
 805395        if (lowerComparison > 0 && upperComparison < 0)
 396        {
 6397            Signed832 discriminant = WideArithmetic.SubtractSigned832(
 6398                WideArithmetic.MultiplySigned576ToSigned832(b, b),
 6399                WideArithmetic.MultiplySigned576ToSigned832(a, c));
 6400            return discriminant.Sign > 0;
 401        }
 402
 799403        bool useLower = lowerComparison <= 0;
 799404        return EvaluateQuadratic(
 799405            a,
 799406            b,
 799407            c,
 799408            useLower
 799409                ? domain.LowerNumerator
 799410                : domain.UpperNumerator,
 799411            useLower
 799412                ? domain.LowerDenominator
 799413                : domain.UpperDenominator).Sign < 0;
 414    }
 415
 416    private static Signed832 EvaluateQuadratic(
 417        Signed576 a,
 418        Signed576 b,
 419        Signed576 c,
 420        Signed320 numerator,
 421        Signed320 denominator)
 422    {
 423        // Reachable full-domain products and their signed three-term sum stay
 424        // below 793 bits, leaving more than 38 signed headroom bits in Signed832.
 829425        Signed576 numeratorSquared = WideArithmetic.MultiplySigned320(
 829426            numerator,
 829427            numerator);
 829428        Signed576 numeratorDenominator = WideArithmetic.MultiplySigned320(
 829429            numerator,
 829430            denominator);
 829431        Signed576 denominatorSquared = WideArithmetic.MultiplySigned320(
 829432            denominator,
 829433            denominator);
 829434        Signed832 linear = WideArithmetic.MultiplySigned576ToSigned832(
 829435            b,
 829436            numeratorDenominator);
 829437        return WideArithmetic.AddSigned832(
 829438            WideArithmetic.AddSigned832(
 829439                WideArithmetic.MultiplySigned576ToSigned832(
 829440                    a,
 829441                    numeratorSquared),
 829442                WideArithmetic.AddSigned832(linear, linear)),
 829443            WideArithmetic.MultiplySigned576ToSigned832(
 829444                c,
 829445                denominatorSquared));
 446    }
 447
 448    private static int CompareRatioToBound(
 449        Signed576 numerator,
 450        Signed576 denominator,
 451        Signed320 boundNumerator,
 452        Signed320 boundDenominator) =>
 1610453        WideArithmetic.SubtractSigned832(
 1610454            WideArithmetic.MultiplySigned576ToSigned832(
 1610455                numerator,
 1610456                boundDenominator),
 1610457            WideArithmetic.MultiplySigned576ToSigned832(
 1610458                denominator,
 1610459                boundNumerator)).Sign;
 460
 461    private static int CompareRatios(
 462        Signed320 firstNumerator,
 463        Signed320 firstDenominator,
 464        Signed320 secondNumerator,
 465        Signed320 secondDenominator) =>
 1654466        WideArithmetic.SubtractSigned576(
 1654467            WideArithmetic.MultiplySigned320(
 1654468                firstNumerator,
 1654469                secondDenominator),
 1654470            WideArithmetic.MultiplySigned320(
 1654471                secondNumerator,
 1654472                firstDenominator)).Sign;
 473
 474    private static int GetSweepFootprintWinding(
 475        ReadOnlySpan<Vector2d> offsets)
 476    {
 324477        for (int index = 0; index < offsets.Length; index++)
 478        {
 161479            Vector2d first = offsets[index];
 161480            Vector2d second = offsets[
 161481                index + 1 == offsets.Length ? 0 : index + 1];
 161482            Vector2d third = offsets[
 161483                index + 2 >= offsets.Length
 161484                    ? index + 2 - offsets.Length
 161485                    : index + 2];
 161486            Signed320 cross = Cross(
 161487                WideArithmetic.Difference(second.X, first.X),
 161488                WideArithmetic.Difference(second.Y, first.Y),
 161489                WideArithmetic.Difference(third.X, second.X),
 161490                WideArithmetic.Difference(third.Y, second.Y));
 161491            if (!cross.IsZero)
 157492                return cross.Sign;
 493        }
 494
 1495        return 0;
 496    }
 497
 498    private static void GetSweepEdge(
 499        SweepPlanarPath path,
 500        WideConvex2dRelations.RotationFrame2d rotation,
 501        Vector2d edgeStart,
 502        Vector2d edgeEnd,
 503        out Signed192 pointX,
 504        out Signed192 pointZ,
 505        out Signed192 velocityX,
 506        out Signed192 velocityZ,
 507        out Signed192 edgeX,
 508        out Signed192 edgeZ)
 509    {
 630510        WideConvex2dRelations.GetRotatedOffset(
 630511            rotation,
 630512            edgeStart,
 630513            out Signed192 startX,
 630514            out Signed192 startZ);
 630515        WideConvex2dRelations.GetRotatedOffset(
 630516            rotation,
 630517            edgeEnd,
 630518            out Signed192 endX,
 630519            out Signed192 endZ);
 630520        pointX = WideArithmetic.SubtractSigned192(path.StartX, startX);
 630521        pointZ = WideArithmetic.SubtractSigned192(path.StartZ, startZ);
 630522        velocityX = path.VelocityX;
 630523        velocityZ = path.VelocityZ;
 630524        edgeX = WideArithmetic.SubtractSigned192(
 630525            endX,
 630526            startX);
 630527        edgeZ = WideArithmetic.SubtractSigned192(
 630528            endZ,
 630529            startZ);
 630530    }
 531
 532    private static Signed320 Cross(
 533        Signed192 firstX,
 534        Signed192 firstZ,
 535        Signed192 secondX,
 536        Signed192 secondZ) =>
 1685537        WideArithmetic.SubtractSigned320(
 1685538            WideArithmetic.MultiplySigned192(firstX, secondZ),
 1685539            WideArithmetic.MultiplySigned192(firstZ, secondX));
 540
 541    private static Signed320 Dot(
 542        Signed192 firstX,
 543        Signed192 firstZ,
 544        Signed192 secondX,
 545        Signed192 secondZ) =>
 3333546        WideArithmetic.AddSigned320(
 3333547            WideArithmetic.MultiplySigned192(firstX, secondX),
 3333548            WideArithmetic.MultiplySigned192(firstZ, secondZ));
 549}

Methods/Properties

.ctor(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
get_IsZero()
TryClipProjectedPrismFace(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Geometry.RationalSegmentInterval&,System.Boolean)
GetPrismInteriorParameter(FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Signed832&,FixedMathSharp.Signed832&)
IsProjectedPrismPointWithinCapsule(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetPrismEdgeContact(FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.FixedQuaternion,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.IPrismCapsuleContactDomain)
.cctor()
KeepPrismEdgeCandidates(FixedMathSharp.Geometry.WideConvexPrismRelations/PrismEdgeSelection&,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Geometry.RationalSegmentInterval&,System.Boolean)
NarrowPrismEdgeParameter(FixedMathSharp.Signed832)
GetClippedEdgeParameter(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832&,FixedMathSharp.Signed832&)
KeepPrismEdgeEndpoint(FixedMathSharp.Geometry.WideConvexPrismRelations/PrismEdgeSelection&,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
.ctor(System.ReadOnlySpan`1<FixedMathSharp.Signed192>,System.ReadOnlySpan`1<FixedMathSharp.Signed192>,FixedMathSharp.Signed192,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Span`1<FixedMathSharp.Signed832>,FixedMathSharp.Geometry.IPrismCapsuleContactDomain,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvex2dRelations/RotationFrame2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
SetEdge(System.ReadOnlySpan`1<FixedMathSharp.Signed192>,System.ReadOnlySpan`1<FixedMathSharp.Signed192>,System.Int32,System.Int32)
Reset()
Keep(FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
SameTarget(System.ReadOnlySpan`1<FixedMathSharp.Signed832>)
CopyTarget(System.Span`1<FixedMathSharp.Signed832>)
TryGetContact(FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Geometry.FixedContactAnchors&)
WithinRadius(FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic)
Supports(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Geometry.WideDirection3d&)
SupportsVertex(System.ReadOnlySpan`1<FixedMathSharp.Signed192>,System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Geometry.WideDirection3d&,System.Boolean)
TryGetPrismEdgeDirection(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Geometry.WideDirection3d&)
GetPrismEdgeVertex(FixedMathSharp.Geometry.WideConvex2dRelations/RotationFrame2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Signed192,System.Span`1<FixedMathSharp.Signed192>)
PrismEdgeDot(System.ReadOnlySpan`1<FixedMathSharp.Signed192>,System.ReadOnlySpan`1<FixedMathSharp.Signed192>)
ClampPrismEdgeParameter(FixedMathSharp.Signed576,FixedMathSharp.Signed576)
MaterializePrismEdgeContact(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64)
MaterializePrismEdgeAnchor(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,System.ReadOnlySpan`1<System.UInt64>,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean)
.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64)
CapOffset(System.Int32,System.Int32)
EndpointOffset(System.Int32,System.Int32,System.Int32)
Keep(FixedMathSharp.Geometry.WideAxis3)
KeepDirection()
get_Best()
TryGetCenteredCapsuleSlabCapsulePenetration(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
BuildCapsuleSlabDirection(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabGeometry&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>)
HasCapsuleSlabStraightRimCertificate(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabGeometry&,FixedMathSharp.Geometry.WideAxis3,FixedMathSharp.Signed320,System.Int32,System.Int32,System.Int32)
KeepCapsuleSlabHorizontalCorners(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabCandidates&,FixedMathSharp.Geometry.WideAxis3,FixedMathSharp.Geometry.WideAxis3)
BuildCapsuleSlabEndpointRim(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabGeometry&,FixedMathSharp.Geometry.WideAxis3,System.Int32,System.Int32,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.Int32>)
KeepParameter(FixedMathSharp.Geometry.FiniteAxisValueRoot,FixedMathSharp.Geometry.WideAxis3,FixedMathSharp.Geometry.WideAxis3,System.Int32)
TryImproveCapsuleSlabRim(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabGeometry&,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Fixed64,System.Boolean&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
KeepCapsuleSlabRimCharts(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabGeometry&,System.Int32,System.Int32,FixedMathSharp.Geometry.WideAxis3,FixedMathSharp.Geometry.WideAxis3,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabRimSelection&)
KeepCapsuleSlabRimChart(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabGeometry&,System.Int32,System.Int32,FixedMathSharp.Geometry.WideAxis3,FixedMathSharp.Geometry.WideAxis3,FixedMathSharp.Geometry.ConvexContactCandidate,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,FixedMathSharp.Geometry.FiniteAxisValueRoots,FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabRimSelection&)
GetCapsuleSlabRimNormal(FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabGeometry&,FixedMathSharp.Geometry.WideConvexPrismRelations/CapsuleSlabRimSelection&)
CompareConvexContactCandidates(FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Geometry.ConvexContactCandidate)
CompareConvexContactCandidateDepthToTwiceRaw(FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Fixed64,FixedMathSharp.Signed192)
GetRoundedConvexContactCandidateDepth(FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,System.Boolean&)
GetConvexContactCandidateNormal(FixedMathSharp.Geometry.ConvexContactCandidate)
GetConvexContactCandidateScaledNormal(FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Fixed64)
BuildConvexContactCandidateNormalSquare(FixedMathSharp.Geometry.ConvexContactCandidate,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Int32&)
GetRoundedConvexContactCandidateNormalComponent(FixedMathSharp.Geometry.ConvexContactCandidate,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.Int32,System.UInt64,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>)
GetConvexContactCandidateMagnitudeFloorBounds(FixedMathSharp.Geometry.ConvexContactCandidate,System.UInt64,System.UInt64&,System.UInt64&)
GetConvexContactCandidateNormalFloorBounds(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.UInt64,System.UInt64&,System.UInt64&)
GetConvexContactCandidateQuadraticBounds(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Int32&,System.Int32&)
AddConvexContactCandidateRadicalBounds(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>,System.Int32&,System.Int32&)
CompareConvexContactCandidateNormalComponentToTwiceRaw(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.Int32,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.UInt64,System.ReadOnlySpan`1<System.UInt64>)
GetConvexContactCandidateQuadraticSign(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>)
GetConvexContactCandidateThreeTermSign(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>)
GetConvexContactCandidateRankRootBounds(System.ReadOnlySpan`1<System.UInt64>,System.Span`1<System.UInt64>)
CopyConvexContactCandidateSignedRadicand(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>,System.Int32)
ConvexContactCandidateLength(System.ReadOnlySpan`1<System.UInt64>)
ConvexContactCandidateProductWords(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
TryGetSphereContact(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.WideDirection3d&,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,System.Boolean&)
VisitCapsuleContacts(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Geometry.IPrismCapsuleContactDomain,System.Boolean&)
TryGetCenteredCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredCylinderContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredConeContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Geometry.WideConvexPrismRelations/PenetrationCandidate&,System.Boolean&,FixedMathSharp.Geometry.IPrismCapsuleContactDomain,System.Boolean)
TryKeepAxis(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvexPrismRelations/PenetrationCandidate&,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Geometry.IPrismCapsuleContactDomain,FixedMathSharp.FixedQuaternion&)
GetPrismProjection(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Signed576&,FixedMathSharp.Signed576&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
GetEndpointToPrismVertexAxis(FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Fixed64,System.Int32,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,System.Int32)
GetScaledEndpointDifference(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,System.Int32)
GetPlanarEdge(FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d)
GetPlanarVertexAxis(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d)
Cross(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
SubtractProducts(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
GetDifferenceProjection(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
GetRotatedPlanarProjection(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
ScaleProjection(FixedMathSharp.Signed576)
IntersectsCurvedPrism(System.Boolean,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64)
IntersectsCurvedPrism(FixedMathSharp.Geometry.WideConvexPrismRelations/FiniteShapeKind,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64)
PrismContainsPoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,System.Boolean&)
.ctor(FixedMathSharp.Geometry.WideConvexPrismRelations/FiniteShapeKind,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64)
Vertex(System.Int32,System.Int32)
ScaleCoordinate(FixedMathSharp.Signed192)
Contains(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
IntersectsFace(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
IntersectsEdge(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
CurvedDot(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
Relative(FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed576)
CurvedDifferenceProduct(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
FootCoordinate(FixedMathSharp.Signed320,FixedMathSharp.Signed192,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
SetConeCoefficient(FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic,FixedMathSharp.Geometry.ContactQuadratic)
.ctor(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
TryPrepareCylinderCapsuleEllipse(FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3&,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3&,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleEllipse&)
ReduceCylinderCapsuleEllipseAxis(FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3&,FixedMathSharp.Signed192&)
OrientCylinderCapsuleEllipseAxis(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,System.Int32)
GetCylinderCapsuleEllipseProjection(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
TryImproveCylinderCapsuleEllipse(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleEllipse&,FixedMathSharp.Geometry.ConvexContactCandidate&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Boolean&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
CompareCylinderCapsuleEllipseSquaredGap(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,FixedMathSharp.Geometry.ConvexContactCandidate&)
CompareCylinderCapsuleEllipseDepthToTwiceRaw(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,FixedMathSharp.Fixed64,System.UInt64)
CompareCylinderCapsuleEllipseMagnitudeToTwiceRaw(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.UInt64)
GetRoundedCylinderCapsuleEllipseDepth(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Int32,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,System.Boolean&)
GetRoundedCylinderCapsuleEllipseNormalComponent(FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleEllipse&,System.Int32)
BuildCylinderCapsuleEllipsePolynomials(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleEllipse&,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
BuildCylinderCapsuleEllipseNormalPolynomials(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleEllipse&,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Span`1<System.SByte>)
CopyCylinderCapsuleEllipseMagnitude(FixedMathSharp.Signed320,System.Span`1<System.UInt64>)
SquareCylinderCapsuleEllipseMagnitude(System.Span`1<System.UInt64>)
ScaleCylinderCapsuleEllipsePolynomial(System.Span`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.Int32)
AddCylinderCapsuleEllipseCoefficient(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Int32)
.ctor(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576)
.ctor(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
get_IsZero()
.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Signed192)
EndpointOffset(System.Int32,System.Int32)
GetCylinderCapsuleScaledHalf(FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetCylinderCapsuleEndpointCoordinate(FixedMathSharp.Signed320,FixedMathSharp.Signed320,System.Int32,FixedMathSharp.Signed320,System.Int32)
DotCylinderCapsuleDirection(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleDirection,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3)
DotCylinderCapsuleDirection(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleDirection,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
ProjectCylinderCapsulePerpendicular(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3)
GetCylinderCapsulePerpendicular(FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d)
ImportCylinderCapsuleFeature(FixedMathSharp.Signed192,System.Span`1<System.UInt64>)
ImportCylinderCapsuleFeature(FixedMathSharp.Signed320,System.Span`1<System.UInt64>)
ImportCylinderCapsuleFeature(FixedMathSharp.Signed576,System.Span`1<System.UInt64>)
ImportCylinderCapsuleFeature(FixedMathSharp.Signed832,System.Span`1<System.UInt64>)
CylinderCapsuleFeatureSlot(System.Span`1<System.UInt64>,System.Int32)
BuildCylinderCapsuleAxisCandidate(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleFeatureGeometry&,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleDirection,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&)
BuildCylinderCapsuleEndpointRimCandidate(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleFeatureGeometry&,System.Int32,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&)
BuildCylinderCapsuleProjectedCornerCandidate(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleFeatureGeometry&,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&)
KeepCylinderCapsuleCandidate(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.Int32>,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&,System.Boolean&)
KeepCylinderCapsuleDirection(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleFeatureGeometry&,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCapsuleDirection,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&,System.Boolean&)
TryGetCenteredFiniteCylinderCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredFiniteCylinderCapsulePenetration(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
MaterializeCylinderCapsulePenetration(FixedMathSharp.Geometry.ConvexContactCandidate&,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
CreateCylinderCapsuleFeatureContact(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
.ctor(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,System.Boolean,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairDepth)
TryGetNonparallelCylinderPairPenetration(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
TrySelectCylinderPairFeature(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.CylinderPairGeometry&,System.Span`1<System.UInt64>,System.Span`1<System.Int32>,System.Int32&,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairSelection&)
TryKeepCylinderPairCapValues(FixedMathSharp.Geometry.CylinderPairGeometry&,System.Int32,System.Int32,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairSelection&)
ShouldKeepCylinderPairValue(FixedMathSharp.Geometry.CylinderPairGeometry&,FixedMathSharp.Geometry.FiniteAxisValueRoot&,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairSelection)
TryHandleRepeatedCylinderPairValue(FixedMathSharp.Geometry.CylinderPairGeometry&,System.Int32,System.Int32,FixedMathSharp.Geometry.FiniteAxisValueRoot,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,FixedMathSharp.Geometry.ConvexContactCandidate,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairSelection&)
.ctor(System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,System.Boolean)
KeepTouch(FixedMathSharp.Vector3d)
KeepRoot(FixedMathSharp.Geometry.FiniteAxisValueRoot,FixedMathSharp.Vector3d)
KeepSimpleRoot(FixedMathSharp.Geometry.FiniteAxisValueRoot,System.Int32,System.Int32,System.Int32)
.ctor(FixedMathSharp.Signed704,FixedMathSharp.Signed192,FixedMathSharp.Signed576,System.UInt64)
get_RadialCoefficient()
TryGetCylinderPairSideFeature(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.CylinderPairGeometry&,System.Boolean,System.Span`1<System.UInt64>,System.Span`1<System.SByte>,System.Span`1<System.UInt64>,FixedMathSharp.Geometry.FiniteAxisValueRoot&,FixedMathSharp.Vector3d&,System.Int32&)
GetKnownCylinderPairSideUnshiftedRoot(FixedMathSharp.Geometry.CylinderPairGeometry&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.Span`1<System.UInt64>,System.Boolean,FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,FixedMathSharp.Geometry.ConvexContactCandidate)
CompareCylinderPairSideUnshiftedEndpoint(FixedMathSharp.Geometry.CylinderPairGeometry&,System.Boolean,FixedMathSharp.Geometry.FiniteAxisPolynomialRoot&,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.SByte>,FixedMathSharp.Geometry.ConvexContactCandidate,System.ReadOnlySpan`1<System.UInt64>,System.Int32)
GetCapsuleProjectionDepths(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&)
IsProjectionNonNegative(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&)
IsSphereProjectionWithinRadius(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&)
CompareProjectionDepths(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&)
GetRoundedDepth(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,System.Boolean&)
GetDepthApproximation(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&)
GetCeilingSquareRoot(FixedMathSharp.Signed704,FixedMathSharp.Signed576)
GetNearestEvenIncrement(System.Int32,System.UInt64)
CompareProjectionDepthToTwiceRaw(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,FixedMathSharp.Signed192)
GetAxisProjection(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3)
GetAxisSquared(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
GetAxisSquared(FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3)
GetMagnitude(FixedMathSharp.Signed704)
BuildLocalCapsuleRadicand(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,System.Span`1<System.UInt64>)
BuildThresholdCapsuleRadicand(FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,System.Span`1<System.UInt64>)
BuildRationalSquare(FixedMathSharp.Signed704,System.Span`1<System.UInt64>)
BuildThresholdRadicand(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,System.Span`1<System.UInt64>)
CompareRadicalPairs(System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
GetMagnitude576(FixedMathSharp.Signed576)
BuildProduct(FixedMathSharp.Signed704,FixedMathSharp.Signed704,System.Span`1<System.UInt64>)
BuildProduct(FixedMathSharp.Signed704,FixedMathSharp.Signed704,FixedMathSharp.Signed320,System.Span`1<System.UInt64>)
BuildProduct(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,System.Span`1<System.UInt64>)
BuildProduct(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed576,System.Span`1<System.UInt64>)
MultiplyBy(System.Span`1<System.UInt64>,FixedMathSharp.Signed320)
MultiplyBy(System.Span`1<System.UInt64>,FixedMathSharp.Signed576)
MultiplyBy(System.Span`1<System.UInt64>,System.ReadOnlySpan`1<System.UInt64>)
GetMagnitude(FixedMathSharp.Signed320,System.Span`1<System.UInt64>)
CopyPositiveRadicand(System.ReadOnlySpan`1<System.UInt64>,System.Span`1<System.UInt64>,System.Span`1<System.UInt64>)
CombineWideSignedMagnitudes(System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.ReadOnlySpan`1<System.UInt64>,System.Int32,System.Span`1<System.UInt64>,System.Int32&)
ShiftLeft(System.Span`1<System.UInt64>,System.Int32)
IsZero(System.ReadOnlySpan`1<System.UInt64>)
.ctor(FixedMathSharp.Signed704,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvexPrismRelations/RadialKind,FixedMathSharp.Signed576,FixedMathSharp.Signed320)
TryGetCenteredFiniteCylindersContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetPositiveRadiusCylinderPairPenetration(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Boolean&)
TryKeepCylinderCylinderAxis(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCylinderPenetration&)
GetCylinderPairDepthSign(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairDepth&)
CompareCylinderPairDepths(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairDepth&,FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderPairDepth&)
GetRoundedCylinderCylinderDepth(FixedMathSharp.Geometry.WideConvexPrismRelations/CylinderCylinderPenetration&,FixedMathSharp.Fixed64&,System.Boolean&)
TryGetSphereBoundaryFaceContact(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.CapsulePlaneRaySelection&,FixedMathSharp.Geometry.CapsulePlaneRayFrame&,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Fixed64&,System.Int32,FixedMathSharp.Geometry.PrismSphereBoundaryAdmission)
KeepSphereProjection(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,System.Boolean,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Geometry.WideConvexPrismRelations/PenetrationCandidate&,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.IPrismCapsuleContactDomain,FixedMathSharp.FixedQuaternion&)
AdmitPrismSpherePoint(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
KeepSphereEdgeAxes(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Geometry.WideConvexPrismRelations/PenetrationCandidate&)
.cctor()
.ctor(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
ToWide()
.ctor(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,System.Boolean,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth)
TryMaterializeContact(FixedMathSharp.Geometry.WideConvexPrismRelations/PenetrationCandidate&,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.FixedQuaternion,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetMatchedPrismAnchor(FixedMathSharp.Geometry.WideConvexPrismRelations/PenetrationCandidate&,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.FixedQuaternion,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Geometry.PrismPointAdmission,System.Boolean,FixedMathSharp.Geometry.IPrismCapsuleContactDomain)
TryGetPrismDomainParameter(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/ProjectionDepth&,System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Geometry.PrismPointAdmission,FixedMathSharp.Signed832&,FixedMathSharp.Signed832&)
GetPrismCoreAnchor(FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Signed832,FixedMathSharp.Signed832)
TryGetPrismSupportInterval(FixedMathSharp.Geometry.WideConvexPrismRelations/PenetrationCandidate&,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Int32,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64,System.Span`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832&,FixedMathSharp.Geometry.RationalSegmentInterval&)
GetPrismSupportProjection(FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.Geometry.WideConvexPrismRelations/RigidAxis3,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Int32,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideConvex2dRelations/RotationFrame2d,System.Span`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
GetProjectedCapsuleSupport(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.WideDirection3d&,System.Span`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832&)
GetPrismCapsuleRadialOffset(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64)
DotPrismSupport(System.ReadOnlySpan`1<FixedMathSharp.Signed576>,System.ReadOnlySpan`1<FixedMathSharp.Signed320>)
GetPrismEdgeConstraint(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
MaterializePrismProjection(System.ReadOnlySpan`1<FixedMathSharp.Signed832>,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Signed832,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion)
FromDirection(FixedMathSharp.Vector3d)
Compare(FixedMathSharp.Signed576,FixedMathSharp.Signed576)
Negate(FixedMathSharp.Geometry.WideConvexPrismRelations/Axis3)
.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
get_Unit()
ClipStrictLinear(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
KeepLower(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
KeepUpper(FixedMathSharp.Signed320,FixedMathSharp.Signed320)
ValidateOrder()
IntersectsSweptUprightCylinderStrict(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,FixedMathSharp.Fixed64)
ClipStrictVerticalOverlap(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.WideConvexPrismRelations/SweepParameterDomain&)
IntersectsStrictPlanarSweep(FixedMathSharp.Geometry.WideConvexPrismRelations/SweepPlanarPath,FixedMathSharp.Geometry.WideConvex2dRelations/RotationFrame2d,FixedMathSharp.Fixed64,System.ReadOnlySpan`1<FixedMathSharp.Vector2d>,System.Int32,FixedMathSharp.Geometry.WideConvexPrismRelations/SweepParameterDomain)
IntersectsOpenCircle(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Geometry.WideConvexPrismRelations/SweepParameterDomain)
IntersectsOpenEdgeStrip(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed320,FixedMathSharp.Geometry.WideConvexPrismRelations/SweepParameterDomain)
HasStrictNegativeQuadratic(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Geometry.WideConvexPrismRelations/SweepParameterDomain)
EvaluateQuadratic(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
CompareRatioToBound(FixedMathSharp.Signed576,FixedMathSharp.Signed576,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
CompareRatios(FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320,FixedMathSharp.Signed320)
GetSweepFootprintWinding(System.ReadOnlySpan`1<FixedMathSharp.Vector2d>)
GetSweepEdge(FixedMathSharp.Geometry.WideConvexPrismRelations/SweepPlanarPath,FixedMathSharp.Geometry.WideConvex2dRelations/RotationFrame2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
Cross(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
Dot(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)