| | | 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 | | |
| | | 8 | | using System; |
| | | 9 | | |
| | | 10 | | namespace FixedMathSharp.Geometry; |
| | | 11 | | |
| | | 12 | | /// <content> |
| | | 13 | | /// Contains projection helpers for computing wide-precision separating-axis |
| | | 14 | | /// projection depths of convex prism shapes (capsules, disks, cones) onto arbitrary axes. |
| | | 15 | | /// </content> |
| | | 16 | | internal static partial class WideConvexPrismRelations |
| | | 17 | | { |
| | | 18 | | private static void GetShapeProjectionDepths( |
| | | 19 | | FiniteShapeKind shapeKind, |
| | | 20 | | Axis3 axis, |
| | | 21 | | RigidAxis3 shapeAxis, |
| | | 22 | | Fixed64 axisLength, |
| | | 23 | | Fixed64 radius, |
| | | 24 | | Signed576 prismMinimum, |
| | | 25 | | Signed576 prismMaximum, |
| | | 26 | | out ProjectionDepth positive, |
| | | 27 | | out ProjectionDepth negative) |
| | | 28 | | { |
| | 1259 | 29 | | Signed576 alignment = GetAxisProjection(axis, shapeAxis); |
| | 1259 | 30 | | Signed704 axial = WideArithmetic.MultiplySigned576ToSigned704( |
| | 1259 | 31 | | alignment, |
| | 1259 | 32 | | Signed320.ExtendValue(Signed192.Raw(axisLength))); |
| | 1259 | 33 | | Signed192 commonWithoutScale = WideArithmetic.AddSigned192( |
| | 1259 | 34 | | shapeAxis.RotationDenominator, |
| | 1259 | 35 | | shapeAxis.RotationDenominator); |
| | 1259 | 36 | | Signed320 commonWide = WideArithmetic.MultiplySigned192( |
| | 1259 | 37 | | commonWithoutScale, |
| | 1259 | 38 | | Scale); |
| | 1259 | 39 | | _ = Signed192.TryNarrowSigned( |
| | 1259 | 40 | | Signed576.ExtendValue(commonWide), |
| | 1259 | 41 | | out Signed192 common); |
| | 1259 | 42 | | Signed704 scaledMinimum = |
| | 1259 | 43 | | WideArithmetic.MultiplySigned576ToSigned704( |
| | 1259 | 44 | | prismMinimum, |
| | 1259 | 45 | | Signed320.ExtendValue(commonWithoutScale)); |
| | 1259 | 46 | | Signed704 scaledMaximum = |
| | 1259 | 47 | | WideArithmetic.MultiplySigned576ToSigned704( |
| | 1259 | 48 | | prismMaximum, |
| | 1259 | 49 | | Signed320.ExtendValue(commonWithoutScale)); |
| | 1259 | 50 | | Signed576 axisSquared = GetAxisSquared(axis); |
| | 1259 | 51 | | Signed320 shapeAxisSquared = GetAxisSquared(shapeAxis); |
| | | 52 | | |
| | 1259 | 53 | | if (shapeKind != FiniteShapeKind.Cone) |
| | | 54 | | { |
| | 1071 | 55 | | Signed704 axialMagnitude = GetMagnitude(axial); |
| | 1071 | 56 | | RadialKind radialKind = shapeKind == FiniteShapeKind.Capsule |
| | 1071 | 57 | | ? RadialKind.Capsule |
| | 1071 | 58 | | : RadialKind.Disk; |
| | 1071 | 59 | | Signed832 planeSquared = radialKind == RadialKind.Disk |
| | 1071 | 60 | | ? GetPlaneSquared( |
| | 1071 | 61 | | axisSquared, |
| | 1071 | 62 | | shapeAxisSquared, |
| | 1071 | 63 | | alignment) |
| | 1071 | 64 | | : default; |
| | 1071 | 65 | | positive = new ProjectionDepth( |
| | 1071 | 66 | | WideArithmetic.SubtractSigned704( |
| | 1071 | 67 | | axialMagnitude, |
| | 1071 | 68 | | scaledMinimum), |
| | 1071 | 69 | | common, |
| | 1071 | 70 | | radius, |
| | 1071 | 71 | | radialKind, |
| | 1071 | 72 | | axisSquared, |
| | 1071 | 73 | | shapeAxisSquared, |
| | 1071 | 74 | | planeSquared); |
| | 1071 | 75 | | negative = new ProjectionDepth( |
| | 1071 | 76 | | WideArithmetic.AddSigned704( |
| | 1071 | 77 | | axialMagnitude, |
| | 1071 | 78 | | scaledMaximum), |
| | 1071 | 79 | | common, |
| | 1071 | 80 | | radius, |
| | 1071 | 81 | | radialKind, |
| | 1071 | 82 | | axisSquared, |
| | 1071 | 83 | | shapeAxisSquared, |
| | 1071 | 84 | | planeSquared); |
| | 1071 | 85 | | return; |
| | | 86 | | } |
| | | 87 | | |
| | 188 | 88 | | Signed832 conePlaneSquared = GetPlaneSquared( |
| | 188 | 89 | | axisSquared, |
| | 188 | 90 | | shapeAxisSquared, |
| | 188 | 91 | | alignment); |
| | 188 | 92 | | Signed704 doubledAxial = WideArithmetic.AddSigned704(axial, axial); |
| | 188 | 93 | | bool positiveUsesBase = doubledAxial.Sign <= 0 |
| | 188 | 94 | | || IsDiskRadicalAtLeast( |
| | 188 | 95 | | common, |
| | 188 | 96 | | radius, |
| | 188 | 97 | | conePlaneSquared, |
| | 188 | 98 | | shapeAxisSquared, |
| | 188 | 99 | | doubledAxial); |
| | 188 | 100 | | Signed704 negativeBaseThreshold = |
| | 188 | 101 | | WideArithmetic.SubtractSigned704(default, doubledAxial); |
| | 188 | 102 | | bool negativeUsesBase = negativeBaseThreshold.Sign <= 0 |
| | 188 | 103 | | || IsDiskRadicalAtLeast( |
| | 188 | 104 | | common, |
| | 188 | 105 | | radius, |
| | 188 | 106 | | conePlaneSquared, |
| | 188 | 107 | | shapeAxisSquared, |
| | 188 | 108 | | negativeBaseThreshold); |
| | 188 | 109 | | positive = new ProjectionDepth( |
| | 188 | 110 | | WideArithmetic.SubtractSigned704( |
| | 188 | 111 | | positiveUsesBase |
| | 188 | 112 | | ? WideArithmetic.SubtractSigned704(default, axial) |
| | 188 | 113 | | : axial, |
| | 188 | 114 | | scaledMinimum), |
| | 188 | 115 | | common, |
| | 188 | 116 | | radius, |
| | 188 | 117 | | positiveUsesBase ? RadialKind.Disk : RadialKind.None, |
| | 188 | 118 | | axisSquared, |
| | 188 | 119 | | shapeAxisSquared, |
| | 188 | 120 | | conePlaneSquared, |
| | 188 | 121 | | positiveUsesBase ? ShapeSupportFeature.ConeBase : ShapeSupportFeature.ConeApex); |
| | 188 | 122 | | negative = new ProjectionDepth( |
| | 188 | 123 | | WideArithmetic.SubtractSigned704( |
| | 188 | 124 | | scaledMaximum, |
| | 188 | 125 | | negativeUsesBase |
| | 188 | 126 | | ? WideArithmetic.SubtractSigned704(default, axial) |
| | 188 | 127 | | : axial), |
| | 188 | 128 | | common, |
| | 188 | 129 | | radius, |
| | 188 | 130 | | negativeUsesBase ? RadialKind.Disk : RadialKind.None, |
| | 188 | 131 | | axisSquared, |
| | 188 | 132 | | shapeAxisSquared, |
| | 188 | 133 | | conePlaneSquared, |
| | 188 | 134 | | negativeUsesBase ? ShapeSupportFeature.ConeBase : ShapeSupportFeature.ConeApex); |
| | 188 | 135 | | } |
| | | 136 | | |
| | | 137 | | private static bool IsProjectionNonNegative(in ProjectionDepth depth) |
| | | 138 | | { |
| | 11005 | 139 | | if (depth.Rational.Sign >= 0) |
| | 5763 | 140 | | return true; |
| | 5242 | 141 | | if (depth.RadialKind == RadialKind.None) |
| | 24 | 142 | | return false; |
| | | 143 | | |
| | 5218 | 144 | | if (depth.RadialKind == RadialKind.Capsule) |
| | | 145 | | { |
| | 706 | 146 | | Span<ulong> left = stackalloc ulong[40]; |
| | 706 | 147 | | Span<ulong> right = stackalloc ulong[40]; |
| | 706 | 148 | | BuildLocalCapsuleRadicand(depth, left); |
| | 706 | 149 | | BuildRationalSquare(depth.Rational, right); |
| | 706 | 150 | | return WideArithmetic.CompareMagnitudeEqualLength(left, right) >= 0; |
| | | 151 | | } |
| | | 152 | | |
| | 4512 | 153 | | return IsDiskRadicalAtLeast( |
| | 4512 | 154 | | depth.Common, |
| | 4512 | 155 | | depth.Radius, |
| | 4512 | 156 | | depth.PlaneSquared, |
| | 4512 | 157 | | depth.ShapeAxisSquared, |
| | 4512 | 158 | | GetMagnitude(depth.Rational)); |
| | | 159 | | } |
| | | 160 | | |
| | | 161 | | private static int CompareProjectionDepths( |
| | | 162 | | in ProjectionDepth left, |
| | | 163 | | in ProjectionDepth right) |
| | | 164 | | { |
| | 4314 | 165 | | Signed576 unit = Signed576.ExtendValue( |
| | 4314 | 166 | | Signed320.ExtendValue( |
| | 4314 | 167 | | Signed192.Signed(1L))); |
| | 4314 | 168 | | GetRadialProjection( |
| | 4314 | 169 | | left, |
| | 4314 | 170 | | unit, |
| | 4314 | 171 | | out Signed320 leftCoefficient, |
| | 4314 | 172 | | out Signed832 leftNumerator, |
| | 4314 | 173 | | out Signed576 leftDenominator); |
| | 4314 | 174 | | GetRadialProjection( |
| | 4314 | 175 | | right, |
| | 4314 | 176 | | unit, |
| | 4314 | 177 | | out Signed320 rightCoefficient, |
| | 4314 | 178 | | out Signed832 rightNumerator, |
| | 4314 | 179 | | out Signed576 rightDenominator); |
| | 4314 | 180 | | return WideArithmetic.CompareRadialProjectionDepths( |
| | 4314 | 181 | | left.Rational, |
| | 4314 | 182 | | leftCoefficient, |
| | 4314 | 183 | | leftNumerator, |
| | 4314 | 184 | | leftDenominator, |
| | 4314 | 185 | | left.AxisSquared, |
| | 4314 | 186 | | right.Rational, |
| | 4314 | 187 | | rightCoefficient, |
| | 4314 | 188 | | rightNumerator, |
| | 4314 | 189 | | rightDenominator, |
| | 4314 | 190 | | right.AxisSquared); |
| | | 191 | | } |
| | | 192 | | |
| | | 193 | | private static int CompareSignedProjectionDepths( |
| | | 194 | | in ProjectionDepth left, |
| | | 195 | | in ProjectionDepth right) |
| | | 196 | | { |
| | | 197 | | // Cylinder-capsule admission adds the same capsule radius to every |
| | | 198 | | // candidate, so base projections retain their ordering even when |
| | | 199 | | // either base is negative. Squared depths preserve order only within |
| | | 200 | | // one sign partition. |
| | 1351 | 201 | | bool leftNonNegative = IsProjectionNonNegative(left); |
| | 1351 | 202 | | bool rightNonNegative = IsProjectionNonNegative(right); |
| | 1351 | 203 | | if (leftNonNegative != rightNonNegative) |
| | 253 | 204 | | return leftNonNegative ? 1 : -1; |
| | | 205 | | |
| | 1098 | 206 | | int squaredComparison = CompareProjectionDepths(left, right); |
| | 1098 | 207 | | return leftNonNegative |
| | 1098 | 208 | | ? squaredComparison |
| | 1098 | 209 | | : -squaredComparison; |
| | | 210 | | } |
| | | 211 | | |
| | | 212 | | private static void GetRadialProjection( |
| | | 213 | | in ProjectionDepth depth, |
| | | 214 | | Signed576 unit, |
| | | 215 | | out Signed320 coefficient, |
| | | 216 | | out Signed832 numerator, |
| | | 217 | | out Signed576 denominator) |
| | | 218 | | { |
| | 8628 | 219 | | if (depth.RadialKind == RadialKind.None) |
| | | 220 | | { |
| | 930 | 221 | | coefficient = default; |
| | 930 | 222 | | numerator = default; |
| | 930 | 223 | | denominator = unit; |
| | 930 | 224 | | return; |
| | | 225 | | } |
| | | 226 | | |
| | 7698 | 227 | | coefficient = WideArithmetic.MultiplySigned192( |
| | 7698 | 228 | | depth.Common, |
| | 7698 | 229 | | Signed192.Raw(depth.Radius)); |
| | 7698 | 230 | | if (depth.RadialKind == RadialKind.Capsule) |
| | | 231 | | { |
| | 3691 | 232 | | numerator = Signed832.ExtendValue( |
| | 3691 | 233 | | depth.AxisSquared); |
| | 3691 | 234 | | denominator = unit; |
| | | 235 | | } |
| | | 236 | | else |
| | | 237 | | { |
| | 4007 | 238 | | numerator = depth.PlaneSquared; |
| | 4007 | 239 | | denominator = Signed576.ExtendValue( |
| | 4007 | 240 | | depth.ShapeAxisSquared); |
| | | 241 | | } |
| | 4007 | 242 | | } |
| | | 243 | | |
| | | 244 | | private static Fixed64 GetRoundedDepth( |
| | | 245 | | in ProjectionDepth depth, |
| | | 246 | | out bool isClamped) |
| | | 247 | | { |
| | 40 | 248 | | if (CompareProjectionDepthToTwiceRaw( |
| | 40 | 249 | | depth, |
| | 40 | 250 | | new Signed192(0UL, 0UL, unchecked((ulong)long.MaxValue << 1))) > 0) |
| | | 251 | | { |
| | 2 | 252 | | isClamped = true; |
| | 2 | 253 | | return Fixed64.MaxValue; |
| | | 254 | | } |
| | | 255 | | |
| | 38 | 256 | | Fixed64 approximation = GetDepthApproximation(depth); |
| | | 257 | | // Sign-aware rational roots and one-sided disk roots keep this estimate |
| | | 258 | | // at or below the exact depth. The admitted Q32-scaled axes keep exact |
| | | 259 | | // upward correction within five raw units. |
| | | 260 | | while (true) |
| | | 261 | | { |
| | 45 | 262 | | Signed192 upperMidpoint = new( |
| | 45 | 263 | | 0UL, |
| | 45 | 264 | | 0UL, |
| | 45 | 265 | | unchecked((ulong)approximation.m_rawValue << 1) | 1UL); |
| | 45 | 266 | | int upperComparison = |
| | 45 | 267 | | CompareProjectionDepthToTwiceRaw( |
| | 45 | 268 | | depth, |
| | 45 | 269 | | upperMidpoint); |
| | 45 | 270 | | if ((approximation < Fixed64.MaxValue) |
| | 45 | 271 | | & (upperComparison |
| | 45 | 272 | | + (approximation.m_rawValue & 1L) > 0)) |
| | | 273 | | { |
| | 7 | 274 | | approximation = Fixed64.FromRaw( |
| | 7 | 275 | | approximation.m_rawValue + 1L); |
| | 7 | 276 | | continue; |
| | | 277 | | } |
| | | 278 | | |
| | 38 | 279 | | isClamped = false; |
| | 38 | 280 | | return approximation; |
| | | 281 | | } |
| | | 282 | | } |
| | | 283 | | |
| | | 284 | | private static Fixed64 GetDepthApproximation( |
| | | 285 | | in ProjectionDepth depth) |
| | | 286 | | { |
| | 38 | 287 | | Signed704 axisSquared = |
| | 38 | 288 | | Signed704.ExtendValue(depth.AxisSquared); |
| | 38 | 289 | | Signed576 axisFloor = |
| | 38 | 290 | | WideArithmetic.GetFloorSquareRoot(axisSquared); |
| | 38 | 291 | | Signed576 axisCeiling = |
| | 38 | 292 | | GetCeilingSquareRoot(axisSquared, axisFloor); |
| | 38 | 293 | | Signed576 rationalAxisLength = depth.Rational.Sign < 0 |
| | 38 | 294 | | ? axisFloor |
| | 38 | 295 | | : axisCeiling; |
| | 38 | 296 | | Signed576 rationalDenominator = WideArithmetic.MultiplySigned576( |
| | 38 | 297 | | rationalAxisLength, |
| | 38 | 298 | | depth.Common); |
| | 38 | 299 | | Fixed64 radial = depth.RadialKind switch |
| | 38 | 300 | | { |
| | 26 | 301 | | RadialKind.Capsule => depth.Radius, |
| | 11 | 302 | | RadialKind.Disk => GetDiskDepthApproximation( |
| | 11 | 303 | | depth, |
| | 11 | 304 | | axisCeiling), |
| | 1 | 305 | | _ => Fixed64.Zero, |
| | 38 | 306 | | }; |
| | 38 | 307 | | Signed704 combinedNumerator = WideArithmetic.AddSigned704( |
| | 38 | 308 | | depth.Rational, |
| | 38 | 309 | | WideArithmetic.MultiplySigned576ToSigned704( |
| | 38 | 310 | | rationalDenominator, |
| | 38 | 311 | | Signed320.ExtendValue(Signed192.Raw(radial)))); |
| | | 312 | | // Cancel rational and radial support before narrowing so individually |
| | | 313 | | // out-of-domain terms cannot discard a representable shallow depth. |
| | 38 | 314 | | return Fixed64.GetNonNegativeRawRatioFloor( |
| | 38 | 315 | | combinedNumerator, |
| | 38 | 316 | | Signed704.ExtendValue(rationalDenominator)); |
| | | 317 | | } |
| | | 318 | | |
| | | 319 | | private static Fixed64 GetDiskDepthApproximation( |
| | | 320 | | in ProjectionDepth depth, |
| | | 321 | | Signed576 axisCeiling) |
| | | 322 | | { |
| | 1023 | 323 | | Signed576 planeLength = |
| | 1023 | 324 | | WideArithmetic.GetFloorSquareRootOfProduct( |
| | 1023 | 325 | | depth.PlaneSquared, |
| | 1023 | 326 | | Signed192.Signed(1L)); |
| | 1023 | 327 | | Signed192 shapeAxisFloor = WideArithmetic.GetFloorSquareRoot( |
| | 1023 | 328 | | depth.ShapeAxisSquared, |
| | 1023 | 329 | | out Signed192 shapeAxisRemainder); |
| | 1023 | 330 | | Signed192 shapeAxisCeiling = shapeAxisRemainder.IsZero |
| | 1023 | 331 | | ? shapeAxisFloor |
| | 1023 | 332 | | : WideArithmetic.AddSigned192( |
| | 1023 | 333 | | shapeAxisFloor, |
| | 1023 | 334 | | Signed192.Signed(1L)); |
| | 1023 | 335 | | Signed576 numerator = WideArithmetic.MultiplySigned576( |
| | 1023 | 336 | | planeLength, |
| | 1023 | 337 | | Signed192.Raw(depth.Radius)); |
| | 1023 | 338 | | Signed576 denominator = WideArithmetic.MultiplySigned576( |
| | 1023 | 339 | | axisCeiling, |
| | 1023 | 340 | | shapeAxisCeiling); |
| | 1023 | 341 | | return Fixed64.GetNonNegativeRawRatioFloor( |
| | 1023 | 342 | | Signed704.ExtendValue(numerator), |
| | 1023 | 343 | | Signed704.ExtendValue(denominator)); |
| | | 344 | | } |
| | | 345 | | |
| | | 346 | | private static Signed576 GetCeilingSquareRoot( |
| | | 347 | | Signed704 squared, |
| | | 348 | | Signed576 floor) |
| | | 349 | | { |
| | 38 | 350 | | _ = Signed320.TryNarrowSigned( |
| | 38 | 351 | | floor, |
| | 38 | 352 | | out Signed320 narrowFloor); |
| | 38 | 353 | | Signed704 floorSquared = |
| | 38 | 354 | | WideArithmetic.MultiplySigned576ToSigned704( |
| | 38 | 355 | | floor, |
| | 38 | 356 | | narrowFloor); |
| | 38 | 357 | | return WideArithmetic.SubtractSigned704( |
| | 38 | 358 | | squared, |
| | 38 | 359 | | floorSquared).IsZero |
| | 38 | 360 | | ? floor |
| | 38 | 361 | | : WideArithmetic.AddSigned576( |
| | 38 | 362 | | floor, |
| | 38 | 363 | | Signed576.ExtendValue( |
| | 38 | 364 | | Signed320.ExtendValue( |
| | 38 | 365 | | Signed192.Signed(1L)))); |
| | | 366 | | } |
| | | 367 | | |
| | | 368 | | private static ulong GetNearestEvenIncrement( |
| | | 369 | | int midpointComparison, |
| | | 370 | | ulong floor) => |
| | 164 | 371 | | unchecked((ulong)( |
| | 164 | 372 | | (midpointComparison |
| | 164 | 373 | | + (int)(floor & 1UL) |
| | 164 | 374 | | + 1) >> 1)); |
| | | 375 | | |
| | | 376 | | private static int CompareProjectionDepthToTwiceRaw( |
| | | 377 | | in ProjectionDepth depth, |
| | | 378 | | Signed192 twiceRaw) |
| | | 379 | | { |
| | 409 | 380 | | Span<ulong> leftFirst = stackalloc ulong[40]; |
| | 409 | 381 | | Span<ulong> leftSecond = stackalloc ulong[40]; |
| | 409 | 382 | | Span<ulong> rightFirst = stackalloc ulong[40]; |
| | 409 | 383 | | Span<ulong> rightSecond = stackalloc ulong[40]; |
| | 409 | 384 | | leftFirst.Clear(); |
| | 409 | 385 | | leftSecond.Clear(); |
| | 409 | 386 | | rightFirst.Clear(); |
| | 409 | 387 | | rightSecond.Clear(); |
| | | 388 | | |
| | 409 | 389 | | Span<ulong> rationalTerm = depth.Rational.Sign >= 0 |
| | 409 | 390 | | ? leftFirst |
| | 409 | 391 | | : rightFirst; |
| | 409 | 392 | | BuildProduct( |
| | 409 | 393 | | depth.Rational, |
| | 409 | 394 | | depth.Rational, |
| | 409 | 395 | | depth.ShapeAxisSquared, |
| | 409 | 396 | | rationalTerm); |
| | 409 | 397 | | ShiftLeft(rationalTerm, 2); |
| | 409 | 398 | | if (depth.RadialKind != RadialKind.None) |
| | | 399 | | { |
| | 406 | 400 | | Span<ulong> radialTerm = IsZero(leftFirst) |
| | 406 | 401 | | ? leftFirst |
| | 406 | 402 | | : leftSecond; |
| | 406 | 403 | | if (depth.RadialKind == RadialKind.Capsule) |
| | 56 | 404 | | BuildThresholdCapsuleRadicand(depth, radialTerm); |
| | | 405 | | else |
| | 350 | 406 | | BuildLocalDiskRadicand(depth, radialTerm); |
| | 406 | 407 | | ShiftLeft(radialTerm, 2); |
| | | 408 | | } |
| | | 409 | | |
| | 409 | 410 | | Signed320 thresholdCoefficient = |
| | 409 | 411 | | WideArithmetic.MultiplySigned192(depth.Common, twiceRaw); |
| | 409 | 412 | | BuildThresholdRadicand( |
| | 409 | 413 | | thresholdCoefficient, |
| | 409 | 414 | | depth.ShapeAxisSquared, |
| | 409 | 415 | | depth.AxisSquared, |
| | 409 | 416 | | rightSecond); |
| | 409 | 417 | | return CompareRadicalPairs( |
| | 409 | 418 | | leftFirst, |
| | 409 | 419 | | leftSecond, |
| | 409 | 420 | | rightFirst, |
| | 409 | 421 | | rightSecond); |
| | | 422 | | } |
| | | 423 | | |
| | | 424 | | private static bool IsDiskRadicalAtLeast( |
| | | 425 | | Signed192 common, |
| | | 426 | | Fixed64 radius, |
| | | 427 | | Signed832 planeSquared, |
| | | 428 | | Signed320 shapeAxisSquared, |
| | | 429 | | Signed704 threshold) |
| | | 430 | | { |
| | 4636 | 431 | | Span<ulong> radial = stackalloc ulong[40]; |
| | 4636 | 432 | | Span<ulong> rational = stackalloc ulong[40]; |
| | 4636 | 433 | | Signed320 coefficient = WideArithmetic.MultiplySigned192( |
| | 4636 | 434 | | common, |
| | 4636 | 435 | | Signed192.Raw(radius)); |
| | 4636 | 436 | | BuildProduct(coefficient, coefficient, planeSquared, radial); |
| | 4636 | 437 | | BuildProduct(threshold, threshold, shapeAxisSquared, rational); |
| | 4636 | 438 | | return WideArithmetic.CompareMagnitudeEqualLength(radial, rational) >= 0; |
| | | 439 | | } |
| | | 440 | | |
| | | 441 | | private static Signed576 GetAxisProjection( |
| | | 442 | | Axis3 axis, |
| | | 443 | | RigidAxis3 direction) => |
| | 9181 | 444 | | WideArithmetic.AddSigned576( |
| | 9181 | 445 | | WideArithmetic.AddSigned576( |
| | 9181 | 446 | | WideArithmetic.MultiplySigned320( |
| | 9181 | 447 | | axis.X, |
| | 9181 | 448 | | Signed320.ExtendValue(direction.X)), |
| | 9181 | 449 | | WideArithmetic.MultiplySigned320( |
| | 9181 | 450 | | axis.Y, |
| | 9181 | 451 | | Signed320.ExtendValue(direction.Y))), |
| | 9181 | 452 | | WideArithmetic.MultiplySigned320( |
| | 9181 | 453 | | axis.Z, |
| | 9181 | 454 | | Signed320.ExtendValue(direction.Z))); |
| | | 455 | | |
| | | 456 | | private static Signed576 GetAxisSquared(Axis3 axis) => |
| | 5200 | 457 | | WideArithmetic.AddSigned576( |
| | 5200 | 458 | | WideArithmetic.AddSigned576( |
| | 5200 | 459 | | WideArithmetic.MultiplySigned320(axis.X, axis.X), |
| | 5200 | 460 | | WideArithmetic.MultiplySigned320(axis.Y, axis.Y)), |
| | 5200 | 461 | | WideArithmetic.MultiplySigned320(axis.Z, axis.Z)); |
| | | 462 | | |
| | | 463 | | private static Signed320 GetAxisSquared(RigidAxis3 axis) => |
| | 7455 | 464 | | WideArithmetic.AddSigned320( |
| | 7455 | 465 | | WideArithmetic.AddSigned320( |
| | 7455 | 466 | | WideArithmetic.MultiplySigned192(axis.X, axis.X), |
| | 7455 | 467 | | WideArithmetic.MultiplySigned192(axis.Y, axis.Y)), |
| | 7455 | 468 | | WideArithmetic.MultiplySigned192(axis.Z, axis.Z)); |
| | | 469 | | |
| | | 470 | | private static Signed832 GetPlaneSquared( |
| | | 471 | | Signed576 axisSquared, |
| | | 472 | | Signed320 shapeAxisSquared, |
| | | 473 | | Signed576 alignment) => |
| | 6203 | 474 | | WideArithmetic.SubtractSigned832( |
| | 6203 | 475 | | WideArithmetic.MultiplyNonNegativeToSigned832( |
| | 6203 | 476 | | Signed832.ExtendValue(axisSquared), |
| | 6203 | 477 | | shapeAxisSquared), |
| | 6203 | 478 | | WideArithmetic.MultiplySigned576ToSigned832( |
| | 6203 | 479 | | alignment, |
| | 6203 | 480 | | alignment)); |
| | | 481 | | |
| | | 482 | | private static Signed704 GetMagnitude(Signed704 value) => |
| | 5583 | 483 | | value.Sign >= 0 |
| | 5583 | 484 | | ? value |
| | 5583 | 485 | | : WideArithmetic.SubtractSigned704(default, value); |
| | | 486 | | |
| | | 487 | | private static void BuildLocalCapsuleRadicand( |
| | | 488 | | in ProjectionDepth depth, |
| | | 489 | | Span<ulong> result) |
| | | 490 | | { |
| | 706 | 491 | | Signed320 coefficient = WideArithmetic.MultiplySigned192( |
| | 706 | 492 | | depth.Common, |
| | 706 | 493 | | Signed192.Raw(depth.Radius)); |
| | 706 | 494 | | BuildProduct( |
| | 706 | 495 | | coefficient, |
| | 706 | 496 | | coefficient, |
| | 706 | 497 | | depth.AxisSquared, |
| | 706 | 498 | | result); |
| | 706 | 499 | | } |
| | | 500 | | |
| | | 501 | | private static void BuildThresholdCapsuleRadicand( |
| | | 502 | | in ProjectionDepth depth, |
| | | 503 | | Span<ulong> result) |
| | | 504 | | { |
| | 732 | 505 | | Signed320 coefficient = WideArithmetic.MultiplySigned192( |
| | 732 | 506 | | depth.Common, |
| | 732 | 507 | | Signed192.Raw(depth.Radius)); |
| | 732 | 508 | | BuildProduct( |
| | 732 | 509 | | coefficient, |
| | 732 | 510 | | coefficient, |
| | 732 | 511 | | depth.ShapeAxisSquared, |
| | 732 | 512 | | depth.AxisSquared, |
| | 732 | 513 | | result); |
| | 732 | 514 | | } |
| | | 515 | | |
| | | 516 | | private static void BuildLocalDiskRadicand( |
| | | 517 | | in ProjectionDepth depth, |
| | | 518 | | Span<ulong> result) |
| | | 519 | | { |
| | 3012 | 520 | | Signed320 coefficient = WideArithmetic.MultiplySigned192( |
| | 3012 | 521 | | depth.Common, |
| | 3012 | 522 | | Signed192.Raw(depth.Radius)); |
| | 3012 | 523 | | BuildProduct( |
| | 3012 | 524 | | coefficient, |
| | 3012 | 525 | | coefficient, |
| | 3012 | 526 | | depth.PlaneSquared, |
| | 3012 | 527 | | result); |
| | 3012 | 528 | | } |
| | | 529 | | |
| | | 530 | | private static void BuildRationalSquare( |
| | | 531 | | Signed704 rational, |
| | | 532 | | Span<ulong> result) => |
| | 706 | 533 | | BuildProduct(rational, rational, result); |
| | | 534 | | |
| | | 535 | | private static void BuildThresholdRadicand( |
| | | 536 | | Signed320 coefficient, |
| | | 537 | | Signed320 shapeAxisSquared, |
| | | 538 | | Signed576 axisSquared, |
| | | 539 | | Span<ulong> result) => |
| | 2395 | 540 | | BuildProduct( |
| | 2395 | 541 | | coefficient, |
| | 2395 | 542 | | coefficient, |
| | 2395 | 543 | | shapeAxisSquared, |
| | 2395 | 544 | | axisSquared, |
| | 2395 | 545 | | result); |
| | | 546 | | |
| | | 547 | | private static int CompareRadicalPairs( |
| | | 548 | | ReadOnlySpan<ulong> leftFirst, |
| | | 549 | | ReadOnlySpan<ulong> leftSecond, |
| | | 550 | | ReadOnlySpan<ulong> rightFirst, |
| | | 551 | | ReadOnlySpan<ulong> rightSecond) |
| | | 552 | | { |
| | 6917 | 553 | | int termWords = leftFirst.Length; |
| | 6917 | 554 | | int productWords = termWords * 2; |
| | 6917 | 555 | | Span<ulong> leftBase = stackalloc ulong[termWords]; |
| | 6917 | 556 | | Span<ulong> rightBase = stackalloc ulong[termWords]; |
| | 6917 | 557 | | WideArithmetic.AddEqualMagnitudes(leftFirst, leftSecond, leftBase); |
| | 6917 | 558 | | WideArithmetic.AddEqualMagnitudes(rightFirst, rightSecond, rightBase); |
| | 6917 | 559 | | int baseComparison = WideArithmetic.CompareMagnitudeEqualLength(leftBase, rightBase); |
| | 6917 | 560 | | Span<ulong> baseMagnitude = stackalloc ulong[termWords]; |
| | 6917 | 561 | | if (baseComparison >= 0) |
| | 3251 | 562 | | WideArithmetic.SubtractEqualMagnitudes(leftBase, rightBase, baseMagnitude); |
| | | 563 | | else |
| | 3666 | 564 | | WideArithmetic.SubtractEqualMagnitudes(rightBase, leftBase, baseMagnitude); |
| | | 565 | | |
| | 6917 | 566 | | Span<ulong> leftProduct = stackalloc ulong[productWords]; |
| | 6917 | 567 | | Span<ulong> rightProduct = stackalloc ulong[productWords]; |
| | 6917 | 568 | | WideArithmetic.MultiplyMagnitudes(leftFirst, leftSecond, leftProduct); |
| | 6917 | 569 | | WideArithmetic.MultiplyMagnitudes(rightFirst, rightSecond, rightProduct); |
| | 6917 | 570 | | if (baseComparison >= 0) |
| | | 571 | | { |
| | 3251 | 572 | | return ComparePositiveRadicalDifference( |
| | 3251 | 573 | | baseMagnitude, |
| | 3251 | 574 | | leftProduct, |
| | 3251 | 575 | | rightProduct); |
| | | 576 | | } |
| | | 577 | | |
| | 3666 | 578 | | return -ComparePositiveRadicalDifference( |
| | 3666 | 579 | | baseMagnitude, |
| | 3666 | 580 | | rightProduct, |
| | 3666 | 581 | | leftProduct); |
| | | 582 | | } |
| | | 583 | | |
| | | 584 | | private static int ComparePositiveRadicalDifference( |
| | | 585 | | ReadOnlySpan<ulong> positiveBase, |
| | | 586 | | ReadOnlySpan<ulong> sameSideProduct, |
| | | 587 | | ReadOnlySpan<ulong> oppositeSideProduct) |
| | | 588 | | { |
| | 6917 | 589 | | int productWords = sameSideProduct.Length; |
| | 6917 | 590 | | int squaredWords = productWords * 2; |
| | 6917 | 591 | | Span<ulong> baseSquared = stackalloc ulong[productWords]; |
| | 6917 | 592 | | Span<ulong> fourSame = stackalloc ulong[productWords]; |
| | 6917 | 593 | | Span<ulong> fourOpposite = stackalloc ulong[productWords]; |
| | 6917 | 594 | | WideArithmetic.MultiplyMagnitudes(positiveBase, positiveBase, baseSquared); |
| | 6917 | 595 | | sameSideProduct.CopyTo(fourSame); |
| | 6917 | 596 | | oppositeSideProduct.CopyTo(fourOpposite); |
| | 6917 | 597 | | ShiftLeft(fourSame, 2); |
| | 6917 | 598 | | ShiftLeft(fourOpposite, 2); |
| | | 599 | | |
| | 6917 | 600 | | Span<ulong> knownLeft = stackalloc ulong[productWords]; |
| | 6917 | 601 | | WideArithmetic.AddEqualMagnitudes(baseSquared, fourSame, knownLeft); |
| | 6917 | 602 | | int knownComparison = WideArithmetic.CompareMagnitudeEqualLength( |
| | 6917 | 603 | | knownLeft, |
| | 6917 | 604 | | fourOpposite); |
| | 6917 | 605 | | if (knownComparison > 0) |
| | 5100 | 606 | | return 1; |
| | | 607 | | |
| | 1817 | 608 | | Span<ulong> remainder = stackalloc ulong[productWords]; |
| | 1817 | 609 | | if (knownComparison == 0) |
| | | 610 | | { |
| | 69 | 611 | | return Math.Sign( |
| | 69 | 612 | | WideArithmetic.GetActiveMagnitudeLength(positiveBase) |
| | 69 | 613 | | * WideArithmetic.GetActiveMagnitudeLength(sameSideProduct)); |
| | | 614 | | } |
| | | 615 | | |
| | 1748 | 616 | | WideArithmetic.SubtractEqualMagnitudes(fourOpposite, knownLeft, remainder); |
| | 1748 | 617 | | Span<ulong> crossSquared = stackalloc ulong[squaredWords]; |
| | 1748 | 618 | | Span<ulong> remainderSquared = stackalloc ulong[squaredWords]; |
| | 1748 | 619 | | WideArithmetic.MultiplyMagnitudes(baseSquared, sameSideProduct, crossSquared); |
| | 1748 | 620 | | ShiftLeft(crossSquared, 4); |
| | 1748 | 621 | | WideArithmetic.MultiplyMagnitudes(remainder, remainder, remainderSquared); |
| | 1748 | 622 | | return WideArithmetic.CompareMagnitudeEqualLength(crossSquared, remainderSquared); |
| | | 623 | | } |
| | | 624 | | } |