| | | 1 | | //======================================================================= |
| | | 2 | | // WideFiniteConeIntersection.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 | | /// <summary> |
| | | 13 | | /// Owns exact full-domain finite-cone containment and segment reduction. |
| | | 14 | | /// </summary> |
| | | 15 | | internal static partial class WideFiniteConeIntersection |
| | | 16 | | { |
| | 1 | 17 | | private static readonly Signed192 One = Signed192.Signed(1L); |
| | 1 | 18 | | private static readonly Signed192 Four = Signed192.Signed(4L); |
| | 1 | 19 | | private static readonly Signed192 AxisScaleSquared = new(0UL, 1UL, 0UL); |
| | | 20 | | |
| | | 21 | | #region Nested Types |
| | | 22 | | |
| | | 23 | | private readonly struct RationalBound |
| | | 24 | | { |
| | | 25 | | internal readonly Signed192 Numerator; |
| | | 26 | | internal readonly Signed192 Denominator; |
| | | 27 | | |
| | | 28 | | internal RationalBound(Signed192 numerator, Signed192 denominator) |
| | | 29 | | { |
| | 131703 | 30 | | Numerator = numerator; |
| | 131703 | 31 | | Denominator = denominator; |
| | 131703 | 32 | | } |
| | | 33 | | } |
| | | 34 | | |
| | | 35 | | private readonly struct ConeData |
| | | 36 | | { |
| | | 37 | | internal readonly Signed192 StartAxial; |
| | | 38 | | internal readonly Signed192 AxialVelocity; |
| | | 39 | | internal readonly Signed192 MaximumAxial; |
| | | 40 | | internal readonly Signed576 Coefficient; |
| | | 41 | | internal readonly Signed576 Projection; |
| | | 42 | | internal readonly Signed576 Constant; |
| | | 43 | | |
| | | 44 | | internal ConeData( |
| | | 45 | | Signed192 startAxial, |
| | | 46 | | Signed192 axialVelocity, |
| | | 47 | | Signed192 maximumAxial, |
| | | 48 | | Signed576 coefficient, |
| | | 49 | | Signed576 projection, |
| | | 50 | | Signed576 constant) |
| | | 51 | | { |
| | 70867 | 52 | | StartAxial = startAxial; |
| | 70867 | 53 | | AxialVelocity = axialVelocity; |
| | 70867 | 54 | | MaximumAxial = maximumAxial; |
| | 70867 | 55 | | Coefficient = coefficient; |
| | 70867 | 56 | | Projection = projection; |
| | 70867 | 57 | | Constant = constant; |
| | 70867 | 58 | | } |
| | | 59 | | |
| | | 60 | | internal ConeData NegatedPolynomial() => |
| | 64 | 61 | | new( |
| | 64 | 62 | | StartAxial, |
| | 64 | 63 | | AxialVelocity, |
| | 64 | 64 | | MaximumAxial, |
| | 64 | 65 | | WideArithmetic.SubtractSigned576(default, Coefficient), |
| | 64 | 66 | | WideArithmetic.SubtractSigned576(default, Projection), |
| | 64 | 67 | | WideArithmetic.SubtractSigned576(default, Constant)); |
| | | 68 | | } |
| | | 69 | | |
| | | 70 | | #endregion |
| | | 71 | | |
| | | 72 | | internal static bool TryGetApexInterval( |
| | | 73 | | FixedSegment query, |
| | | 74 | | Vector3d apex, |
| | | 75 | | Vector3d apexToBaseDirection, |
| | | 76 | | Fixed64 height, |
| | | 77 | | Fixed64 baseRadius, |
| | | 78 | | out Fixed64 entry, |
| | | 79 | | out Fixed64 exit, |
| | | 80 | | out bool startContained, |
| | | 81 | | out bool endContainedStrict) => |
| | 43 | 82 | | TrySolve( |
| | 43 | 83 | | CreateApexData(query, apex, apexToBaseDirection, height, baseRadius), |
| | 43 | 84 | | Fixed64.One, |
| | 43 | 85 | | out entry, |
| | 43 | 86 | | out exit, |
| | 43 | 87 | | out startContained, |
| | 43 | 88 | | out endContainedStrict); |
| | | 89 | | |
| | | 90 | | internal static bool TrySolveUnitPolynomial( |
| | | 91 | | Signed576 coefficient, |
| | | 92 | | Signed576 projection, |
| | | 93 | | Signed576 constant, |
| | | 94 | | Fixed64 outputScale, |
| | | 95 | | out Fixed64 entry, |
| | | 96 | | out Fixed64 exit) => |
| | 34482 | 97 | | TrySolveBoundedPolynomial( |
| | 34482 | 98 | | new ConeData(default, default, default, coefficient, projection, constant), |
| | 34482 | 99 | | new RationalBound(default, One), |
| | 34482 | 100 | | new RationalBound(One, One), |
| | 34482 | 101 | | outputScale, |
| | 34482 | 102 | | out entry, |
| | 34482 | 103 | | out exit); |
| | | 104 | | |
| | | 105 | | internal static bool TryGetApexDistanceInterval( |
| | | 106 | | FixedSegment query, |
| | | 107 | | Vector3d apex, |
| | | 108 | | Vector3d apexToBaseDirection, |
| | | 109 | | Fixed64 height, |
| | | 110 | | Fixed64 baseRadius, |
| | | 111 | | Fixed64 totalDistance, |
| | | 112 | | out Fixed64 entry, |
| | | 113 | | out Fixed64 exit, |
| | | 114 | | out bool startContained, |
| | | 115 | | out bool endContainedStrict) => |
| | 363 | 116 | | TrySolve( |
| | 363 | 117 | | CreateApexData(query, apex, apexToBaseDirection, height, baseRadius), |
| | 363 | 118 | | totalDistance, |
| | 363 | 119 | | out entry, |
| | 363 | 120 | | out exit, |
| | 363 | 121 | | out startContained, |
| | 363 | 122 | | out endContainedStrict); |
| | | 123 | | |
| | | 124 | | internal static bool TryGetCenteredInterval( |
| | | 125 | | FixedSegment query, |
| | | 126 | | Vector3d center, |
| | | 127 | | Vector3d baseToApexDirection, |
| | | 128 | | Fixed64 height, |
| | | 129 | | Fixed64 baseRadius, |
| | | 130 | | out Fixed64 entry, |
| | | 131 | | out Fixed64 exit, |
| | | 132 | | out bool startContained, |
| | | 133 | | out bool endContainedStrict) => |
| | 3 | 134 | | TrySolve( |
| | 3 | 135 | | CreateCenteredData(query, center, baseToApexDirection, height, baseRadius), |
| | 3 | 136 | | Fixed64.One, |
| | 3 | 137 | | out entry, |
| | 3 | 138 | | out exit, |
| | 3 | 139 | | out startContained, |
| | 3 | 140 | | out endContainedStrict); |
| | | 141 | | |
| | | 142 | | internal static bool TryGetCenteredDistanceInterval( |
| | | 143 | | FixedSegment query, |
| | | 144 | | Vector3d center, |
| | | 145 | | Vector3d baseToApexDirection, |
| | | 146 | | Fixed64 height, |
| | | 147 | | Fixed64 baseRadius, |
| | | 148 | | Fixed64 totalDistance, |
| | | 149 | | out Fixed64 entry, |
| | | 150 | | out Fixed64 exit, |
| | | 151 | | out bool startContained, |
| | | 152 | | out bool endContainedStrict) => |
| | 17 | 153 | | TrySolve( |
| | 17 | 154 | | CreateCenteredData(query, center, baseToApexDirection, height, baseRadius), |
| | 17 | 155 | | totalDistance, |
| | 17 | 156 | | out entry, |
| | 17 | 157 | | out exit, |
| | 17 | 158 | | out startContained, |
| | 17 | 159 | | out endContainedStrict); |
| | | 160 | | |
| | | 161 | | internal static bool ContainsPointInApexCone( |
| | | 162 | | Vector3d point, |
| | | 163 | | Vector3d apex, |
| | | 164 | | Vector3d apexToBaseDirection, |
| | | 165 | | Fixed64 height, |
| | | 166 | | Fixed64 baseRadius, |
| | | 167 | | bool strict) |
| | | 168 | | { |
| | 8 | 169 | | Signed192 heightRaw = Signed192.Signed(height.m_rawValue); |
| | 8 | 170 | | Signed192 axisLengthSquared = GetDot( |
| | 8 | 171 | | apexToBaseDirection, |
| | 8 | 172 | | Vector3d.Zero, |
| | 8 | 173 | | apexToBaseDirection, |
| | 8 | 174 | | Vector3d.Zero); |
| | 8 | 175 | | bool exactUnitAxis = IsExactUnitAxis(axisLengthSquared); |
| | 8 | 176 | | Signed192 axisProjection = GetDot( |
| | 8 | 177 | | point, |
| | 8 | 178 | | apex, |
| | 8 | 179 | | apexToBaseDirection, |
| | 8 | 180 | | Vector3d.Zero); |
| | 8 | 181 | | Signed192 axial = exactUnitAxis ? axisProjection : GetScaledRaw(axisProjection); |
| | 8 | 182 | | return ContainsPoint( |
| | 8 | 183 | | point, |
| | 8 | 184 | | apex, |
| | 8 | 185 | | heightRaw, |
| | 8 | 186 | | axisLengthSquared, |
| | 8 | 187 | | axisProjection, |
| | 8 | 188 | | axial, |
| | 8 | 189 | | exactUnitAxis ? GetScaledRaw(heightRaw) : GetAxisHeightProduct(axisLengthSquared, heightRaw), |
| | 8 | 190 | | One, |
| | 8 | 191 | | exactUnitAxis, |
| | 8 | 192 | | baseRadius, |
| | 8 | 193 | | strict); |
| | | 194 | | } |
| | | 195 | | |
| | | 196 | | internal static bool ContainsPointInCenteredCone( |
| | | 197 | | Vector3d point, |
| | | 198 | | Vector3d center, |
| | | 199 | | Vector3d baseToApexDirection, |
| | | 200 | | Fixed64 height, |
| | | 201 | | Fixed64 baseRadius, |
| | | 202 | | bool strict) |
| | | 203 | | { |
| | 11 | 204 | | Signed192 heightRaw = Signed192.Signed(height.m_rawValue); |
| | 11 | 205 | | Signed192 axisLengthSquared = GetDot( |
| | 11 | 206 | | baseToApexDirection, |
| | 11 | 207 | | Vector3d.Zero, |
| | 11 | 208 | | baseToApexDirection, |
| | 11 | 209 | | Vector3d.Zero); |
| | 11 | 210 | | bool exactUnitAxis = IsExactUnitAxis(axisLengthSquared); |
| | 11 | 211 | | Signed192 axisProjection = GetDot(point, center, baseToApexDirection, Vector3d.Zero); |
| | 11 | 212 | | Signed192 maximumAxial = exactUnitAxis |
| | 11 | 213 | | ? GetScaledRaw(heightRaw) |
| | 11 | 214 | | : GetAxisHeightProduct(axisLengthSquared, heightRaw); |
| | 11 | 215 | | Signed192 axial = exactUnitAxis |
| | 11 | 216 | | ? WideArithmetic.SubtractSigned192(GetHalfScaledRaw(heightRaw), axisProjection) |
| | 11 | 217 | | : WideArithmetic.SubtractSigned192(maximumAxial, WideArithmetic.Double(GetScaledRaw(axisProjection))); |
| | 11 | 218 | | return ContainsPoint( |
| | 11 | 219 | | point, |
| | 11 | 220 | | center, |
| | 11 | 221 | | heightRaw, |
| | 11 | 222 | | axisLengthSquared, |
| | 11 | 223 | | axisProjection, |
| | 11 | 224 | | axial, |
| | 11 | 225 | | exactUnitAxis ? maximumAxial : WideArithmetic.Double(maximumAxial), |
| | 11 | 226 | | exactUnitAxis ? One : Four, |
| | 11 | 227 | | exactUnitAxis, |
| | 11 | 228 | | baseRadius, |
| | 11 | 229 | | strict); |
| | | 230 | | } |
| | | 231 | | |
| | | 232 | | private static bool ContainsPoint( |
| | | 233 | | Vector3d point, |
| | | 234 | | Vector3d origin, |
| | | 235 | | Signed192 heightRaw, |
| | | 236 | | Signed192 axisLengthSquared, |
| | | 237 | | Signed192 axisProjection, |
| | | 238 | | Signed192 axial, |
| | | 239 | | Signed192 maximumAxial, |
| | | 240 | | Signed192 radialScale, |
| | | 241 | | bool exactUnitAxis, |
| | | 242 | | Fixed64 baseRadius, |
| | | 243 | | bool strict) |
| | | 244 | | { |
| | 19 | 245 | | Signed192 distanceSquared = GetDot(point, origin, point, origin); |
| | 19 | 246 | | Signed320 radial = GetRadialTerm(distanceSquared, axisProjection, axisLengthSquared); |
| | 19 | 247 | | Signed320 heightSquared = WideArithmetic.MultiplySigned192(heightRaw, heightRaw); |
| | 19 | 248 | | Signed192 radiusRaw = Signed192.Signed(baseRadius.m_rawValue); |
| | 19 | 249 | | Signed320 radiusSquared = WideArithmetic.MultiplySigned192(radiusRaw, radiusRaw); |
| | 19 | 250 | | Signed576 polynomial = SubtractConeTerms( |
| | 19 | 251 | | heightSquared, |
| | 19 | 252 | | radial, |
| | 19 | 253 | | axisLengthSquared, |
| | 19 | 254 | | radialScale, |
| | 19 | 255 | | exactUnitAxis, |
| | 19 | 256 | | radiusSquared, |
| | 19 | 257 | | WideArithmetic.MultiplySigned192(axial, axial)); |
| | 19 | 258 | | return IsContained(axial, polynomial, maximumAxial, strict); |
| | | 259 | | } |
| | | 260 | | |
| | | 261 | | private static bool TrySolve( |
| | | 262 | | ConeData data, |
| | | 263 | | Fixed64 outputScale, |
| | | 264 | | out Fixed64 entry, |
| | | 265 | | out Fixed64 exit, |
| | | 266 | | out bool startContained, |
| | | 267 | | out bool endContainedStrict) |
| | | 268 | | { |
| | 426 | 269 | | startContained = IsContained( |
| | 426 | 270 | | data.StartAxial, |
| | 426 | 271 | | data.Constant, |
| | 426 | 272 | | data.MaximumAxial, |
| | 426 | 273 | | strict: false); |
| | 426 | 274 | | Signed192 endAxial = WideArithmetic.AddSigned192(data.StartAxial, data.AxialVelocity); |
| | 426 | 275 | | endContainedStrict = false; |
| | | 276 | | |
| | 426 | 277 | | if (!TryGetAxialInterval(data, out RationalBound lower, out RationalBound upper)) |
| | | 278 | | { |
| | 70 | 279 | | entry = default; |
| | 70 | 280 | | exit = default; |
| | 70 | 281 | | return false; |
| | | 282 | | } |
| | | 283 | | |
| | 356 | 284 | | if (endAxial.Sign > 0 |
| | 356 | 285 | | && WideArithmetic.SubtractSigned192(endAxial, data.MaximumAxial).Sign < 0) |
| | | 286 | | { |
| | 201 | 287 | | endContainedStrict = Evaluate(data, One, One).Sign < 0; |
| | | 288 | | } |
| | | 289 | | |
| | 356 | 290 | | return TrySolveBoundedPolynomial(data, lower, upper, outputScale, out entry, out exit); |
| | | 291 | | } |
| | | 292 | | |
| | | 293 | | private static bool TrySolveBoundedPolynomial( |
| | | 294 | | ConeData data, |
| | | 295 | | RationalBound lower, |
| | | 296 | | RationalBound upper, |
| | | 297 | | Fixed64 outputScale, |
| | | 298 | | out Fixed64 entry, |
| | | 299 | | out Fixed64 exit) => |
| | 35234 | 300 | | TrySolveBoundedUnitPolynomial( |
| | 35234 | 301 | | Signed832.ExtendValue(data.Coefficient), |
| | 35234 | 302 | | Signed832.ExtendValue(data.Projection), |
| | 35234 | 303 | | Signed832.ExtendValue(data.Constant), |
| | 35234 | 304 | | Signed320.ExtendValue(lower.Numerator), |
| | 35234 | 305 | | Signed320.ExtendValue(lower.Denominator), |
| | 35234 | 306 | | Signed320.ExtendValue(upper.Numerator), |
| | 35234 | 307 | | Signed320.ExtendValue(upper.Denominator), |
| | 35234 | 308 | | outputScale, |
| | 35234 | 309 | | out entry, |
| | 35234 | 310 | | out exit); |
| | | 311 | | |
| | | 312 | | /// <summary> |
| | | 313 | | /// Reduces an axially clipped conic on [0, 1]. Rigid triangle coefficients |
| | | 314 | | /// have fewer than 660 magnitude bits and clip ratios at most 200; midpoint |
| | | 315 | | /// root evaluations fit Signed832, while clipped evaluations use fixed |
| | | 316 | | /// transient magnitude storage. The admitted cone interval is connected. |
| | | 317 | | /// </summary> |
| | | 318 | | internal static bool TrySolveBoundedUnitPolynomial( |
| | | 319 | | Signed832 coefficient, |
| | | 320 | | Signed832 projection, |
| | | 321 | | Signed832 constant, |
| | | 322 | | Signed320 lowerNumerator, |
| | | 323 | | Signed320 lowerDenominator, |
| | | 324 | | Signed320 upperNumerator, |
| | | 325 | | Signed320 upperDenominator, |
| | | 326 | | Fixed64 outputScale, |
| | | 327 | | out Fixed64 entry, |
| | | 328 | | out Fixed64 exit) |
| | | 329 | | { |
| | 35419 | 330 | | bool compact = TryGetCompactPolynomial( |
| | 35419 | 331 | | coefficient, projection, constant, |
| | 35419 | 332 | | lowerNumerator, lowerDenominator, upperNumerator, upperDenominator, |
| | 35419 | 333 | | out ConeData data); |
| | 35419 | 334 | | bool lowerContained = (compact |
| | 35419 | 335 | | ? Evaluate(data, Signed192.NarrowValue(lowerNumerator), Signed192.NarrowValue(lowerDenominator)).Sign |
| | 35419 | 336 | | : GetPolynomialSignAtRationalParameter(coefficient, projection, constant, lowerNumerator, lowerDenominator)) |
| | 35419 | 337 | | bool upperContained = (compact |
| | 35419 | 338 | | ? Evaluate(data, Signed192.NarrowValue(upperNumerator), Signed192.NarrowValue(upperDenominator)).Sign |
| | 35419 | 339 | | : GetPolynomialSignAtRationalParameter(coefficient, projection, constant, upperNumerator, upperDenominator)) |
| | | 340 | | |
| | 35419 | 341 | | if (lowerContained && upperContained) |
| | | 342 | | { |
| | 805 | 343 | | entry = Round(lowerNumerator, lowerDenominator, outputScale); |
| | 805 | 344 | | exit = Round(upperNumerator, upperDenominator, outputScale); |
| | 805 | 345 | | return true; |
| | | 346 | | } |
| | | 347 | | |
| | 34614 | 348 | | if (coefficient.IsZero) |
| | | 349 | | { |
| | 3485 | 350 | | if (!lowerContained && !upperContained) |
| | | 351 | | { |
| | 3478 | 352 | | entry = default; |
| | 3478 | 353 | | exit = default; |
| | 3478 | 354 | | return false; |
| | | 355 | | } |
| | | 356 | | |
| | 7 | 357 | | Fixed64 root = compact |
| | 7 | 358 | | ? RoundLinearRoot(data, outputScale) |
| | 7 | 359 | | : RoundLinearRoot(projection, constant, outputScale); |
| | 7 | 360 | | entry = lowerContained ? Round(lowerNumerator, lowerDenominator, outputScale) : root; |
| | 7 | 361 | | exit = upperContained ? Round(upperNumerator, upperDenominator, outputScale) : root; |
| | 7 | 362 | | return true; |
| | | 363 | | } |
| | | 364 | | |
| | 31129 | 365 | | bool opensUp = coefficient.Sign > 0; |
| | 31129 | 366 | | if (!opensUp && !lowerContained && !upperContained) |
| | | 367 | | { |
| | 33 | 368 | | entry = default; |
| | 33 | 369 | | exit = default; |
| | 33 | 370 | | return false; |
| | | 371 | | } |
| | | 372 | | |
| | 31096 | 373 | | if (opensUp |
| | 31096 | 374 | | && ((!lowerContained && (compact |
| | 31096 | 375 | | ? EvaluateDerivative(data, new RationalBound(Signed192.NarrowValue(lowerNumerator), Signed192.Narrow |
| | 31096 | 376 | | : GetDerivativeSign(coefficient, projection, lowerNumerator, lowerDenominator)) >= 0) |
| | 31096 | 377 | | || (!upperContained && (compact |
| | 31096 | 378 | | ? EvaluateDerivative(data, new RationalBound(Signed192.NarrowValue(upperNumerator), Signed192.Narrow |
| | 31096 | 379 | | : GetDerivativeSign(coefficient, projection, upperNumerator, upperDenominator)) <= 0))) |
| | | 380 | | { |
| | 1246 | 381 | | entry = default; |
| | 1246 | 382 | | exit = default; |
| | 1246 | 383 | | return false; |
| | | 384 | | } |
| | | 385 | | |
| | 29850 | 386 | | if (!opensUp) |
| | | 387 | | { |
| | 64 | 388 | | coefficient = WideArithmetic.SubtractSigned832(default, coefficient); |
| | 64 | 389 | | projection = WideArithmetic.SubtractSigned832(default, projection); |
| | 64 | 390 | | constant = WideArithmetic.SubtractSigned832(default, constant); |
| | | 391 | | } |
| | 29850 | 392 | | ConeData normalized = opensUp ? data : data.NegatedPolynomial(); |
| | 29850 | 393 | | Signed832 discriminant = compact |
| | 29850 | 394 | | ? WideArithmetic.SubtractSigned832( |
| | 29850 | 395 | | WideArithmetic.MultiplySigned576ToSigned832(normalized.Projection, normalized.Projection), |
| | 29850 | 396 | | WideArithmetic.MultiplySigned576ToSigned832(normalized.Coefficient, normalized.Constant)) |
| | 29850 | 397 | | : default; |
| | 29850 | 398 | | int discriminantSign = compact ? discriminant.Sign |
| | 29850 | 399 | | : constant.Sign < 0 ? 1 |
| | 29850 | 400 | | : WideArithmetic.CompareNonNegativeProducts(projection, projection, coefficient, constant); |
| | 29850 | 401 | | if (discriminantSign < 0) |
| | | 402 | | { |
| | 18621 | 403 | | entry = default; |
| | 18621 | 404 | | exit = default; |
| | 18621 | 405 | | return false; |
| | | 406 | | } |
| | | 407 | | |
| | 11229 | 408 | | Signed192 outputScaleRaw = Signed192.Signed(outputScale.m_rawValue); |
| | 11229 | 409 | | Signed192 outputScaleSquared = SquareRaw(outputScale.m_rawValue); |
| | 11229 | 410 | | Signed576 scaledSquareRoot = compact |
| | 11229 | 411 | | ? WideArithmetic.GetFloorSquareRootOfProduct(discriminant, outputScaleSquared) |
| | 11229 | 412 | | : default; |
| | | 413 | | |
| | 11229 | 414 | | if (opensUp) |
| | | 415 | | { |
| | 11165 | 416 | | entry = lowerContained |
| | 11165 | 417 | | ? Round(lowerNumerator, lowerDenominator, outputScale) |
| | 11165 | 418 | | : compact ? RoundLowerRoot(normalized, scaledSquareRoot, outputScaleRaw) |
| | 11165 | 419 | | : RoundRoot(coefficient, projection, constant, outputScale, upperRoot: false); |
| | 11165 | 420 | | exit = upperContained |
| | 11165 | 421 | | ? Round(upperNumerator, upperDenominator, outputScale) |
| | 11165 | 422 | | : compact ? RoundUpperRoot(normalized, scaledSquareRoot, outputScaleRaw) |
| | 11165 | 423 | | : RoundRoot(coefficient, projection, constant, outputScale, upperRoot: true); |
| | 11165 | 424 | | return true; |
| | | 425 | | } |
| | | 426 | | |
| | 64 | 427 | | entry = lowerContained |
| | 64 | 428 | | ? Round(lowerNumerator, lowerDenominator, outputScale) |
| | 64 | 429 | | : compact ? RoundUpperRoot(normalized, scaledSquareRoot, outputScaleRaw) |
| | 64 | 430 | | : RoundRoot(coefficient, projection, constant, outputScale, upperRoot: true); |
| | 64 | 431 | | exit = upperContained |
| | 64 | 432 | | ? Round(upperNumerator, upperDenominator, outputScale) |
| | 64 | 433 | | : compact ? RoundLowerRoot(normalized, scaledSquareRoot, outputScaleRaw) |
| | 64 | 434 | | : RoundRoot(coefficient, projection, constant, outputScale, upperRoot: false); |
| | 64 | 435 | | return true; |
| | | 436 | | } |
| | | 437 | | |
| | | 438 | | private static bool TryGetCompactPolynomial( |
| | | 439 | | Signed832 coefficient, |
| | | 440 | | Signed832 projection, |
| | | 441 | | Signed832 constant, |
| | | 442 | | Signed320 lowerNumerator, |
| | | 443 | | Signed320 lowerDenominator, |
| | | 444 | | Signed320 upperNumerator, |
| | | 445 | | Signed320 upperDenominator, |
| | | 446 | | out ConeData data) |
| | | 447 | | { |
| | 35419 | 448 | | int coefficientBits = GetMagnitudeBitLength(coefficient); |
| | 35419 | 449 | | int projectionBits = GetMagnitudeBitLength(projection); |
| | 35419 | 450 | | int constantBits = GetMagnitudeBitLength(constant); |
| | 35419 | 451 | | int coefficientMaximum = Math.Max(coefficientBits, Math.Max(projectionBits, constantBits)); |
| | 35419 | 452 | | int boundMaximum = Math.Max( |
| | 35419 | 453 | | Math.Max(GetMagnitudeBitLength(lowerNumerator), GetMagnitudeBitLength(lowerDenominator)), |
| | 35419 | 454 | | Math.Max(GetMagnitudeBitLength(upperNumerator), GetMagnitudeBitLength(upperDenominator))); |
| | | 455 | | // Rational squares fit Signed320, evaluated sums and the discriminant |
| | | 456 | | // fit Signed832, and the linear numerator retains its 63-bit scale. |
| | | 457 | | // The discriminant proof also bounds the scaled quadratic-root |
| | | 458 | | // numerator below 480 bits, inside the existing Signed576 owner. |
| | 35419 | 459 | | if (coefficientMaximum > 575 || boundMaximum > 159 |
| | 35419 | 460 | | || coefficientMaximum + 2 * boundMaximum > 828 |
| | 35419 | 461 | | || 2 * projectionBits > 830 || coefficientBits + constantBits > 830 |
| | 35419 | 462 | | || (coefficient.IsZero && constantBits > 512)) |
| | | 463 | | { |
| | 48 | 464 | | data = default; |
| | 48 | 465 | | return false; |
| | | 466 | | } |
| | | 467 | | |
| | 35371 | 468 | | data = new ConeData(default, default, default, |
| | 35371 | 469 | | NarrowCoefficient(coefficient), NarrowCoefficient(projection), NarrowCoefficient(constant)); |
| | 35371 | 470 | | return true; |
| | | 471 | | } |
| | | 472 | | |
| | | 473 | | private static Signed576 NarrowCoefficient(Signed832 value) => |
| | 106113 | 474 | | new(value.Word8, value.Word7, value.Word6, value.Word5, value.Word4, |
| | 106113 | 475 | | value.Word3, value.Word2, value.Word1, value.Word0); |
| | | 476 | | |
| | | 477 | | private static int GetMagnitudeBitLength(Signed832 value) |
| | | 478 | | { |
| | 106257 | 479 | | Span<ulong> magnitude = stackalloc ulong[13]; |
| | 106257 | 480 | | WideArithmetic.GetMagnitude(value, magnitude); |
| | 106257 | 481 | | return WideArithmetic.GetMagnitudeBitLength(magnitude); |
| | | 482 | | } |
| | | 483 | | |
| | | 484 | | private static int GetMagnitudeBitLength(Signed320 value) |
| | | 485 | | { |
| | 141676 | 486 | | Span<ulong> magnitude = stackalloc ulong[5]; |
| | 141676 | 487 | | WideArithmetic.GetMagnitude(value, out magnitude[4], out magnitude[3], |
| | 141676 | 488 | | out magnitude[2], out magnitude[1], out magnitude[0]); |
| | 141676 | 489 | | return WideArithmetic.GetMagnitudeBitLength(magnitude); |
| | | 490 | | } |
| | | 491 | | |
| | | 492 | | /// <summary> |
| | | 493 | | /// Returns the exact sign of A*n*n + 2*B*n*d + C*d*d without narrowing |
| | | 494 | | /// rigid-frame clip ratios or their homogenized products. |
| | | 495 | | /// </summary> |
| | | 496 | | internal static int GetPolynomialSignAtRationalParameter( |
| | | 497 | | Signed832 coefficient, |
| | | 498 | | Signed832 projection, |
| | | 499 | | Signed832 constant, |
| | | 500 | | Signed320 numerator, |
| | | 501 | | Signed320 denominator) => |
| | 20722 | 502 | | EvaluateWidePolynomialSign(coefficient, projection, constant, numerator, denominator, derivative: false); |
| | | 503 | | |
| | | 504 | | internal static int GetPolynomialSignAtRationalParameter(Signed832 coefficient, Signed832 projection, |
| | | 505 | | Signed832 constant, Signed832 numerator, Signed832 denominator) |
| | | 506 | | { |
| | 506 | 507 | | Span<ulong> n = stackalloc ulong[13], d = stackalloc ulong[13]; |
| | 506 | 508 | | WideArithmetic.GetMagnitude(numerator, n); WideArithmetic.GetMagnitude(denominator, d); |
| | 253 | 509 | | return EvaluateWidePolynomialSign(coefficient, projection, constant, |
| | 253 | 510 | | n, numerator.Sign, d, denominator.Sign, derivative: false); |
| | | 511 | | } |
| | | 512 | | |
| | | 513 | | private static int GetDerivativeSign( |
| | | 514 | | Signed832 coefficient, |
| | | 515 | | Signed832 projection, |
| | | 516 | | Signed320 numerator, |
| | | 517 | | Signed320 denominator) => |
| | 40 | 518 | | EvaluateWidePolynomialSign(coefficient, projection, default, numerator, denominator, derivative: true); |
| | | 519 | | |
| | | 520 | | private static int EvaluateWidePolynomialSign( |
| | | 521 | | Signed832 coefficient, |
| | | 522 | | Signed832 projection, |
| | | 523 | | Signed832 constant, |
| | | 524 | | Signed320 numerator, |
| | | 525 | | Signed320 denominator, |
| | | 526 | | bool derivative) |
| | | 527 | | { |
| | | 528 | | // Even the full carrier products occupy fewer than 1,474 bits including |
| | | 529 | | // the doubled middle term and addition carries; 24 words retain them. |
| | | 530 | | // Current rigid query bounds are smaller: 660 + 2*200 + 2 < 1,063 bits. |
| | 20762 | 531 | | Span<ulong> n = stackalloc ulong[5]; |
| | 20762 | 532 | | Span<ulong> d = stackalloc ulong[5]; |
| | 20762 | 533 | | WideArithmetic.GetMagnitude(numerator, out n[4], out n[3], out n[2], out n[1], out n[0]); |
| | 20762 | 534 | | WideArithmetic.GetMagnitude(denominator, out d[4], out d[3], out d[2], out d[1], out d[0]); |
| | 20762 | 535 | | return EvaluateWidePolynomialSign(coefficient, projection, constant, |
| | 20762 | 536 | | n, numerator.Sign, d, denominator.Sign, derivative); |
| | | 537 | | } |
| | | 538 | | |
| | | 539 | | private static int EvaluateWidePolynomialSign(Signed832 coefficient, Signed832 projection, Signed832 constant, |
| | | 540 | | ReadOnlySpan<ulong> n, int numeratorSign, ReadOnlySpan<ulong> d, int denominatorSign, bool derivative) |
| | | 541 | | { |
| | 21015 | 542 | | int factorWords = n.Length + d.Length; |
| | | 543 | | // Full coefficient and two complete parameter magnitudes plus one |
| | | 544 | | // limb retain the doubled middle term and both addition carries. |
| | 21015 | 545 | | int words = 14 + factorWords; |
| | 42030 | 546 | | Span<ulong> factor = stackalloc ulong[factorWords], coefficientMagnitude = stackalloc ulong[13], |
| | 42030 | 547 | | product = stackalloc ulong[words], sum = stackalloc ulong[words]; |
| | 21015 | 548 | | sum.Clear(); |
| | 21015 | 549 | | int sign = 0; |
| | 168040 | 550 | | for (int term = 0; term < (derivative ? 2 : 3); term++) |
| | | 551 | | { |
| | 63005 | 552 | | Signed832 value = term == 0 ? coefficient : term == 1 ? projection : constant; |
| | 63005 | 553 | | WideArithmetic.GetMagnitude(value, coefficientMagnitude); |
| | | 554 | | int factorSign; |
| | 63005 | 555 | | if (derivative) |
| | | 556 | | { |
| | 80 | 557 | | factor.Clear(); |
| | 80 | 558 | | (term == 0 ? n : d).CopyTo(factor); |
| | 80 | 559 | | factorSign = term == 0 ? numeratorSign : denominatorSign; |
| | | 560 | | } |
| | | 561 | | else |
| | | 562 | | { |
| | 62925 | 563 | | WideArithmetic.MultiplyMagnitudes(term == 2 ? d : n, term == 0 ? n : d, factor); |
| | 62925 | 564 | | factorSign = term == 0 ? numeratorSign * numeratorSign |
| | 62925 | 565 | | : term == 1 ? numeratorSign * denominatorSign : denominatorSign * denominatorSign; |
| | | 566 | | } |
| | 63005 | 567 | | WideArithmetic.MultiplyMagnitudes(coefficientMagnitude, factor, product); |
| | 63005 | 568 | | WideArithmetic.AddShiftedSignedMagnitude(product, value.Sign * factorSign, |
| | 63005 | 569 | | !derivative && term == 1 ? 1 : 0, sum, ref sign); |
| | | 570 | | } |
| | 21015 | 571 | | return sign; |
| | | 572 | | } |
| | | 573 | | |
| | | 574 | | private static Fixed64 RoundRoot( |
| | | 575 | | Signed832 coefficient, |
| | | 576 | | Signed832 projection, |
| | | 577 | | Signed832 constant, |
| | | 578 | | Fixed64 outputScale, |
| | | 579 | | bool upperRoot) |
| | | 580 | | { |
| | 24 | 581 | | ulong low = 0UL; |
| | 24 | 582 | | ulong high = (ulong)outputScale.m_rawValue; |
| | 24 | 583 | | Signed192 denominator = new(0UL, 0UL, high << 1); |
| | 1003 | 584 | | while (low < high) |
| | | 585 | | { |
| | 979 | 586 | | ulong candidate = low + ((high - low) >> 1); |
| | 979 | 587 | | Signed192 numerator = new(0UL, 0UL, (candidate << 1) | 1UL); |
| | 979 | 588 | | Signed832 first = WideArithmetic.MultiplySigned832(coefficient, numerator); |
| | 979 | 589 | | Signed832 second = WideArithmetic.MultiplySigned832(projection, denominator); |
| | 979 | 590 | | int derivativeSign = WideArithmetic.AddSigned832(first, second).Sign; |
| | 979 | 591 | | Signed832 mixed = WideArithmetic.MultiplySigned832(second, numerator); |
| | 979 | 592 | | int polynomialSign = WideArithmetic.AddSigned832( |
| | 979 | 593 | | WideArithmetic.AddSigned832( |
| | 979 | 594 | | WideArithmetic.MultiplySigned832(first, numerator), |
| | 979 | 595 | | WideArithmetic.AddSigned832(mixed, mixed)), |
| | 979 | 596 | | WideArithmetic.MultiplySigned832( |
| | 979 | 597 | | WideArithmetic.MultiplySigned832(constant, denominator), denominator)).Sign; |
| | 979 | 598 | | bool exactRoot = polynomialSign == 0 && (upperRoot ? derivativeSign >= 0 : derivativeSign <= 0); |
| | 979 | 599 | | bool precedes = exactRoot ? (candidate & 1UL) != 0UL |
| | 979 | 600 | | : upperRoot ? derivativeSign < 0 || polynomialSign < 0 |
| | 979 | 601 | | : derivativeSign < 0 && polynomialSign > 0; |
| | 979 | 602 | | if (precedes) |
| | 427 | 603 | | low = candidate + 1UL; |
| | | 604 | | else |
| | 552 | 605 | | high = candidate; |
| | | 606 | | } |
| | 24 | 607 | | return Fixed64.FromRaw((long)low); |
| | | 608 | | } |
| | | 609 | | |
| | | 610 | | private static Fixed64 RoundLinearRoot(Signed832 projection, Signed832 constant, Fixed64 outputScale) |
| | | 611 | | { |
| | 2 | 612 | | Signed832 numerator = WideArithmetic.MultiplySigned832( |
| | 2 | 613 | | WideArithmetic.SubtractSigned832(default, constant), Signed192.Raw(outputScale)); |
| | 2 | 614 | | Signed832 denominator = WideArithmetic.AddSigned832(projection, projection); |
| | 2 | 615 | | _ = Fixed64.TryGetSignedRawRatio(numerator, denominator, 0, out Fixed64 root); |
| | 2 | 616 | | return root; |
| | | 617 | | } |
| | | 618 | | |
| | | 619 | | private static Fixed64 Round(Signed320 numerator, Signed320 denominator, Fixed64 outputScale) |
| | | 620 | | { |
| | 2426 | 621 | | _ = Fixed64.TryGetSignedRawRatio( |
| | 2426 | 622 | | WideArithmetic.MultiplySigned320(numerator, Signed192.Raw(outputScale)), |
| | 2426 | 623 | | Signed576.ExtendValue(denominator), out Fixed64 result); |
| | 2426 | 624 | | return result; |
| | | 625 | | } |
| | | 626 | | |
| | | 627 | | private static Fixed64 RoundLinearRoot(ConeData data, Fixed64 outputScale) |
| | | 628 | | { |
| | 5 | 629 | | Signed192 outputScaleRaw = Signed192.Signed(outputScale.m_rawValue); |
| | 5 | 630 | | Signed576 numerator = WideArithmetic.MultiplySigned576( |
| | 5 | 631 | | WideArithmetic.SubtractSigned576(default, data.Constant), |
| | 5 | 632 | | outputScaleRaw); |
| | 5 | 633 | | Signed576 denominator = WideArithmetic.AddSigned576(data.Projection, data.Projection); |
| | 5 | 634 | | _ = Fixed64.TryGetSignedRawRatio(numerator, denominator, out Fixed64 root); |
| | 5 | 635 | | return root; |
| | | 636 | | } |
| | | 637 | | |
| | | 638 | | private static Fixed64 RoundLowerRoot( |
| | | 639 | | ConeData normalized, |
| | | 640 | | Signed576 scaledSquareRoot, |
| | | 641 | | Signed192 outputScaleRaw) |
| | | 642 | | { |
| | 11082 | 643 | | Signed576 negativeScaledProjection = WideArithmetic.SubtractSigned576( |
| | 11082 | 644 | | default, |
| | 11082 | 645 | | WideArithmetic.MultiplySigned576(normalized.Projection, outputScaleRaw)); |
| | 11082 | 646 | | Signed576 numerator = WideArithmetic.SubtractSigned576( |
| | 11082 | 647 | | negativeScaledProjection, |
| | 11082 | 648 | | scaledSquareRoot); |
| | 11082 | 649 | | _ = Fixed64.TryGetSignedRawRatio(numerator, normalized.Coefficient, out Fixed64 candidate); |
| | | 650 | | |
| | 11082 | 651 | | long upperRaw = candidate.m_rawValue; |
| | 11082 | 652 | | Signed192 upper = Signed192.Signed(upperRaw); |
| | 11082 | 653 | | Signed192 midpoint = WideArithmetic.SubtractSigned192( |
| | 11082 | 654 | | WideArithmetic.AddSigned192(upper, upper), |
| | 11082 | 655 | | One); |
| | 11082 | 656 | | Signed192 doubleScale = WideArithmetic.AddSigned192(outputScaleRaw, outputScaleRaw); |
| | 11082 | 657 | | Signed832 value = Evaluate(normalized, midpoint, doubleScale); |
| | 11082 | 658 | | if (value.IsZero) |
| | 22 | 659 | | return candidate; |
| | | 660 | | |
| | 11060 | 661 | | return Fixed64.FromRaw(value.Sign > 0 ? upperRaw : upperRaw - 1L); |
| | | 662 | | } |
| | | 663 | | |
| | | 664 | | private static Fixed64 RoundUpperRoot( |
| | | 665 | | ConeData normalized, |
| | | 666 | | Signed576 scaledSquareRoot, |
| | | 667 | | Signed192 outputScaleRaw) |
| | | 668 | | { |
| | 10543 | 669 | | Signed576 negativeScaledProjection = WideArithmetic.SubtractSigned576( |
| | 10543 | 670 | | default, |
| | 10543 | 671 | | WideArithmetic.MultiplySigned576(normalized.Projection, outputScaleRaw)); |
| | 10543 | 672 | | Signed576 numerator = WideArithmetic.AddSigned576( |
| | 10543 | 673 | | negativeScaledProjection, |
| | 10543 | 674 | | scaledSquareRoot); |
| | 10543 | 675 | | _ = Fixed64.TryGetSignedRawRatio(numerator, normalized.Coefficient, out Fixed64 candidate); |
| | | 676 | | |
| | 10543 | 677 | | long lowerRaw = candidate.m_rawValue; |
| | 10543 | 678 | | Signed192 lower = Signed192.Signed(lowerRaw); |
| | 10543 | 679 | | Signed192 midpoint = WideArithmetic.AddSigned192( |
| | 10543 | 680 | | WideArithmetic.AddSigned192(lower, lower), |
| | 10543 | 681 | | One); |
| | 10543 | 682 | | Signed192 doubleScale = WideArithmetic.AddSigned192(outputScaleRaw, outputScaleRaw); |
| | 10543 | 683 | | Signed832 value = Evaluate(normalized, midpoint, doubleScale); |
| | 10543 | 684 | | if (value.IsZero) |
| | 26 | 685 | | return candidate; |
| | | 686 | | |
| | 10517 | 687 | | return Fixed64.FromRaw(value.Sign <= 0 ? lowerRaw + 1L : lowerRaw); |
| | | 688 | | } |
| | | 689 | | |
| | | 690 | | private static bool TryGetAxialInterval( |
| | | 691 | | ConeData data, |
| | | 692 | | out RationalBound lower, |
| | | 693 | | out RationalBound upper) |
| | | 694 | | { |
| | 426 | 695 | | RationalBound zero = new(default, One); |
| | 426 | 696 | | RationalBound one = new(One, One); |
| | 426 | 697 | | lower = zero; |
| | 426 | 698 | | upper = one; |
| | | 699 | | |
| | 426 | 700 | | if (data.AxialVelocity.IsZero) |
| | | 701 | | { |
| | 135 | 702 | | return data.StartAxial.Sign >= 0 |
| | 135 | 703 | | && WideArithmetic.SubtractSigned192(data.StartAxial, data.MaximumAxial).Sign <= 0; |
| | | 704 | | } |
| | | 705 | | |
| | 291 | 706 | | RationalBound first = Normalize( |
| | 291 | 707 | | WideArithmetic.SubtractSigned192(default, data.StartAxial), |
| | 291 | 708 | | data.AxialVelocity); |
| | 291 | 709 | | RationalBound second = Normalize( |
| | 291 | 710 | | WideArithmetic.SubtractSigned192(data.MaximumAxial, data.StartAxial), |
| | 291 | 711 | | data.AxialVelocity); |
| | 291 | 712 | | if (Compare(first, second) > 0) |
| | 138 | 713 | | (first, second) = (second, first); |
| | | 714 | | |
| | 291 | 715 | | if (Compare(second, zero) < 0 || Compare(first, one) > 0) |
| | 30 | 716 | | return false; |
| | 261 | 717 | | if (Compare(first, zero) > 0) |
| | 110 | 718 | | lower = first; |
| | 261 | 719 | | if (Compare(second, one) < 0) |
| | 110 | 720 | | upper = second; |
| | 261 | 721 | | return true; |
| | | 722 | | } |
| | | 723 | | |
| | | 724 | | private static ConeData CreateApexData( |
| | | 725 | | FixedSegment query, |
| | | 726 | | Vector3d apex, |
| | | 727 | | Vector3d apexToBaseDirection, |
| | | 728 | | Fixed64 height, |
| | | 729 | | Fixed64 baseRadius) |
| | | 730 | | { |
| | 406 | 731 | | Signed192 heightRaw = Signed192.Signed(height.m_rawValue); |
| | 406 | 732 | | return CreateData( |
| | 406 | 733 | | query, |
| | 406 | 734 | | apex, |
| | 406 | 735 | | apexToBaseDirection, |
| | 406 | 736 | | heightRaw, |
| | 406 | 737 | | centered: false, |
| | 406 | 738 | | baseRadius); |
| | | 739 | | } |
| | | 740 | | |
| | | 741 | | private static ConeData CreateCenteredData( |
| | | 742 | | FixedSegment query, |
| | | 743 | | Vector3d center, |
| | | 744 | | Vector3d baseToApexDirection, |
| | | 745 | | Fixed64 height, |
| | | 746 | | Fixed64 baseRadius) |
| | | 747 | | { |
| | 20 | 748 | | Signed192 heightRaw = Signed192.Signed(height.m_rawValue); |
| | 20 | 749 | | return CreateData( |
| | 20 | 750 | | query, |
| | 20 | 751 | | center, |
| | 20 | 752 | | baseToApexDirection, |
| | 20 | 753 | | heightRaw, |
| | 20 | 754 | | centered: true, |
| | 20 | 755 | | baseRadius); |
| | | 756 | | } |
| | | 757 | | |
| | | 758 | | private static ConeData CreateData( |
| | | 759 | | FixedSegment query, |
| | | 760 | | Vector3d origin, |
| | | 761 | | Vector3d axisDirection, |
| | | 762 | | Signed192 heightRaw, |
| | | 763 | | bool centered, |
| | | 764 | | Fixed64 baseRadius) |
| | | 765 | | { |
| | 426 | 766 | | Signed192 startDistanceSquared = GetDot(query.Start, origin, query.Start, origin); |
| | 426 | 767 | | Signed192 startDirectionProjection = GetDot(query.Start, origin, query.End, query.Start); |
| | 426 | 768 | | Signed192 directionLengthSquared = GetDot(query.End, query.Start, query.End, query.Start); |
| | 426 | 769 | | Signed192 axisLengthSquared = GetDot(axisDirection, Vector3d.Zero, axisDirection, Vector3d.Zero); |
| | 426 | 770 | | bool exactUnitAxis = IsExactUnitAxis(axisLengthSquared); |
| | 426 | 771 | | Signed192 startAxisProjection = GetDot(query.Start, origin, axisDirection, Vector3d.Zero); |
| | 426 | 772 | | Signed192 directionAxisProjection = GetDot(query.End, query.Start, axisDirection, Vector3d.Zero); |
| | 426 | 773 | | Signed192 maximumAxisHeight = exactUnitAxis |
| | 426 | 774 | | ? GetScaledRaw(heightRaw) |
| | 426 | 775 | | : GetAxisHeightProduct(axisLengthSquared, heightRaw); |
| | | 776 | | Signed192 startAxial; |
| | | 777 | | Signed192 axialVelocity; |
| | | 778 | | Signed192 maximumAxial; |
| | | 779 | | Signed192 radialScale; |
| | 426 | 780 | | if (exactUnitAxis) |
| | | 781 | | { |
| | 386 | 782 | | startAxial = centered |
| | 386 | 783 | | ? WideArithmetic.SubtractSigned192(GetHalfScaledRaw(heightRaw), startAxisProjection) |
| | 386 | 784 | | : startAxisProjection; |
| | 386 | 785 | | axialVelocity = centered |
| | 386 | 786 | | ? WideArithmetic.SubtractSigned192(default, directionAxisProjection) |
| | 386 | 787 | | : directionAxisProjection; |
| | 386 | 788 | | maximumAxial = maximumAxisHeight; |
| | 386 | 789 | | radialScale = One; |
| | | 790 | | } |
| | | 791 | | else |
| | | 792 | | { |
| | 40 | 793 | | Signed192 scaledStartAxisProjection = GetScaledRaw(startAxisProjection); |
| | 40 | 794 | | Signed192 scaledDirectionAxisProjection = GetScaledRaw(directionAxisProjection); |
| | 40 | 795 | | startAxial = centered |
| | 40 | 796 | | ? WideArithmetic.SubtractSigned192(maximumAxisHeight, WideArithmetic.Double(scaledStartAxisProjection)) |
| | 40 | 797 | | : scaledStartAxisProjection; |
| | 40 | 798 | | axialVelocity = centered |
| | 40 | 799 | | ? WideArithmetic.SubtractSigned192(default, WideArithmetic.Double(scaledDirectionAxisProjection)) |
| | 40 | 800 | | : scaledDirectionAxisProjection; |
| | 40 | 801 | | maximumAxial = centered |
| | 40 | 802 | | ? WideArithmetic.Double(maximumAxisHeight) |
| | 40 | 803 | | : maximumAxisHeight; |
| | 40 | 804 | | radialScale = centered ? Four : One; |
| | | 805 | | } |
| | | 806 | | |
| | 426 | 807 | | Signed320 radialCoefficient = GetRadialTerm( |
| | 426 | 808 | | directionLengthSquared, |
| | 426 | 809 | | directionAxisProjection, |
| | 426 | 810 | | axisLengthSquared); |
| | 426 | 811 | | Signed320 radialProjection = GetRadialTerm( |
| | 426 | 812 | | startDirectionProjection, |
| | 426 | 813 | | startAxisProjection, |
| | 426 | 814 | | directionAxisProjection, |
| | 426 | 815 | | axisLengthSquared); |
| | 426 | 816 | | Signed320 radialConstant = GetRadialTerm( |
| | 426 | 817 | | startDistanceSquared, |
| | 426 | 818 | | startAxisProjection, |
| | 426 | 819 | | axisLengthSquared); |
| | 426 | 820 | | Signed320 heightSquared = WideArithmetic.MultiplySigned192(heightRaw, heightRaw); |
| | 426 | 821 | | Signed192 radiusRaw = Signed192.Signed(baseRadius.m_rawValue); |
| | 426 | 822 | | Signed320 radiusSquared = WideArithmetic.MultiplySigned192(radiusRaw, radiusRaw); |
| | 426 | 823 | | Signed320 axialVelocitySquared = WideArithmetic.MultiplySigned192(axialVelocity, axialVelocity); |
| | 426 | 824 | | Signed320 axialProduct = WideArithmetic.MultiplySigned192(startAxial, axialVelocity); |
| | 426 | 825 | | Signed320 startAxialSquared = WideArithmetic.MultiplySigned192(startAxial, startAxial); |
| | | 826 | | |
| | 426 | 827 | | return new ConeData( |
| | 426 | 828 | | startAxial, |
| | 426 | 829 | | axialVelocity, |
| | 426 | 830 | | maximumAxial, |
| | 426 | 831 | | SubtractConeTerms( |
| | 426 | 832 | | heightSquared, radialCoefficient, axisLengthSquared, radialScale, |
| | 426 | 833 | | exactUnitAxis, |
| | 426 | 834 | | radiusSquared, axialVelocitySquared), |
| | 426 | 835 | | SubtractConeTerms( |
| | 426 | 836 | | heightSquared, radialProjection, axisLengthSquared, radialScale, |
| | 426 | 837 | | exactUnitAxis, |
| | 426 | 838 | | radiusSquared, axialProduct), |
| | 426 | 839 | | SubtractConeTerms( |
| | 426 | 840 | | heightSquared, radialConstant, axisLengthSquared, radialScale, |
| | 426 | 841 | | exactUnitAxis, |
| | 426 | 842 | | radiusSquared, startAxialSquared)); |
| | | 843 | | } |
| | | 844 | | |
| | | 845 | | private static Signed320 GetRadialTerm( |
| | | 846 | | Signed192 squaredLength, |
| | | 847 | | Signed192 axisProjection, |
| | | 848 | | Signed192 axisLengthSquared) => |
| | 871 | 849 | | WideArithmetic.SubtractSigned320( |
| | 871 | 850 | | WideArithmetic.MultiplySigned192(squaredLength, axisLengthSquared), |
| | 871 | 851 | | WideArithmetic.MultiplySigned192(axisProjection, axisProjection)); |
| | | 852 | | |
| | | 853 | | private static Signed320 GetRadialTerm( |
| | | 854 | | Signed192 dot, |
| | | 855 | | Signed192 startAxisProjection, |
| | | 856 | | Signed192 directionAxisProjection, |
| | | 857 | | Signed192 axisLengthSquared) => |
| | 426 | 858 | | WideArithmetic.SubtractSigned320( |
| | 426 | 859 | | WideArithmetic.MultiplySigned192(dot, axisLengthSquared), |
| | 426 | 860 | | WideArithmetic.MultiplySigned192(startAxisProjection, directionAxisProjection)); |
| | | 861 | | |
| | | 862 | | private static Signed576 SubtractConeTerms( |
| | | 863 | | Signed320 heightSquared, |
| | | 864 | | Signed320 radialTerm, |
| | | 865 | | Signed192 axisLengthSquared, |
| | | 866 | | Signed192 radialScale, |
| | | 867 | | bool exactUnitAxis, |
| | | 868 | | Signed320 radiusSquared, |
| | | 869 | | Signed320 axialTerm) |
| | | 870 | | { |
| | 1297 | 871 | | if (exactUnitAxis) |
| | | 872 | | { |
| | 1172 | 873 | | return WideArithmetic.SubtractSigned576( |
| | 1172 | 874 | | WideArithmetic.MultiplySigned320(heightSquared, radialTerm), |
| | 1172 | 875 | | WideArithmetic.MultiplySigned320(radiusSquared, axialTerm)); |
| | | 876 | | } |
| | | 877 | | |
| | 125 | 878 | | Signed576 radial = WideArithmetic.MultiplySigned576( |
| | 125 | 879 | | WideArithmetic.MultiplySigned576( |
| | 125 | 880 | | WideArithmetic.MultiplySigned320(heightSquared, radialTerm), |
| | 125 | 881 | | axisLengthSquared), |
| | 125 | 882 | | radialScale); |
| | 125 | 883 | | Signed576 axial = WideArithmetic.MultiplySigned320(radiusSquared, axialTerm); |
| | 125 | 884 | | return WideArithmetic.SubtractSigned576(radial, axial); |
| | | 885 | | } |
| | | 886 | | |
| | | 887 | | private static Signed832 Evaluate(ConeData data, Signed192 numerator, Signed192 denominator) |
| | | 888 | | { |
| | | 889 | | // Rigid-frame coefficients have fewer than 390 magnitude bits after |
| | | 890 | | // their common quaternion-denominator factor is removed. Clipped |
| | | 891 | | // rational bounds have at most 132-bit magnitudes, so every homogenized |
| | | 892 | | // term and their sum fit below 656 bits. Signed832 also retains the |
| | | 893 | | // wider legacy conic intermediates without the former eleven-word |
| | | 894 | | // truncation. |
| | 92632 | 895 | | Signed320 numeratorSquared = WideArithmetic.MultiplySigned192(numerator, numerator); |
| | 92632 | 896 | | Signed320 numeratorDenominator = WideArithmetic.MultiplySigned192(numerator, denominator); |
| | 92632 | 897 | | Signed320 denominatorSquared = WideArithmetic.MultiplySigned192(denominator, denominator); |
| | 92632 | 898 | | Signed832 first = WideArithmetic.MultiplySigned576ToSigned832(data.Coefficient, numeratorSquared); |
| | 92632 | 899 | | Signed832 second = WideArithmetic.MultiplySigned576ToSigned832(data.Projection, numeratorDenominator); |
| | 92632 | 900 | | Signed832 third = WideArithmetic.MultiplySigned576ToSigned832(data.Constant, denominatorSquared); |
| | 92632 | 901 | | return WideArithmetic.AddSigned832( |
| | 92632 | 902 | | WideArithmetic.AddSigned832(first, WideArithmetic.AddSigned832(second, second)), |
| | 92632 | 903 | | third); |
| | | 904 | | } |
| | | 905 | | |
| | | 906 | | private static Signed832 EvaluateDerivative(ConeData data, RationalBound bound) => |
| | 60513 | 907 | | WideArithmetic.AddSigned832( |
| | 60513 | 908 | | WideArithmetic.MultiplySigned576ToSigned832( |
| | 60513 | 909 | | data.Coefficient, |
| | 60513 | 910 | | Signed320.ExtendValue(bound.Numerator)), |
| | 60513 | 911 | | WideArithmetic.MultiplySigned576ToSigned832( |
| | 60513 | 912 | | data.Projection, |
| | 60513 | 913 | | Signed320.ExtendValue(bound.Denominator))); |
| | | 914 | | |
| | | 915 | | private static bool IsContained( |
| | | 916 | | Signed192 axial, |
| | | 917 | | Signed576 polynomial, |
| | | 918 | | Signed192 maximumAxial, |
| | | 919 | | bool strict) => |
| | 445 | 920 | | IsContained(axial, polynomial.Sign, maximumAxial, strict); |
| | | 921 | | |
| | | 922 | | private static bool IsContained( |
| | | 923 | | Signed192 axial, |
| | | 924 | | int polynomialSign, |
| | | 925 | | Signed192 maximumAxial, |
| | | 926 | | bool strict) |
| | | 927 | | { |
| | 445 | 928 | | int maximumSign = WideArithmetic.SubtractSigned192(axial, maximumAxial).Sign; |
| | 445 | 929 | | return strict |
| | 445 | 930 | | ? axial.Sign > 0 && maximumSign < 0 && polynomialSign < 0 |
| | 445 | 931 | | : axial.Sign >= 0 && maximumSign <= 0 && polynomialSign <= 0; |
| | | 932 | | } |
| | | 933 | | |
| | | 934 | | private static Signed192 GetScaledRaw(Signed192 value) => |
| | 512 | 935 | | new((value.High << FixedMath.SHIFT_AMOUNT_I) | (value.Middle >> FixedMath.SHIFT_AMOUNT_I), |
| | 512 | 936 | | (value.Middle << FixedMath.SHIFT_AMOUNT_I) | (value.Low >> FixedMath.SHIFT_AMOUNT_I), |
| | 512 | 937 | | value.Low << FixedMath.SHIFT_AMOUNT_I); |
| | | 938 | | |
| | | 939 | | private static Signed192 GetAxisHeightProduct( |
| | | 940 | | Signed192 axisLengthSquared, |
| | | 941 | | Signed192 heightRaw) |
| | | 942 | | { |
| | 45 | 943 | | Signed320 product = WideArithmetic.MultiplySigned192(axisLengthSquared, heightRaw); |
| | | 944 | | // IsNormalized bounds the positive axis square near 2^64; multiplying |
| | | 945 | | // by a positive Fixed64 raw height therefore occupies fewer than 129 bits. |
| | 45 | 946 | | return new Signed192(product.Word2, product.Word1, product.Word0); |
| | | 947 | | } |
| | | 948 | | |
| | | 949 | | private static Signed192 GetHalfScaledRaw(Signed192 value) |
| | | 950 | | { |
| | 27 | 951 | | Signed192 scaled = GetScaledRaw(value); |
| | 27 | 952 | | ulong high = scaled.High; |
| | 27 | 953 | | ulong middle = scaled.Middle; |
| | 27 | 954 | | ulong low = scaled.Low; |
| | 27 | 955 | | WideArithmetic.ShiftRightOne(ref high, ref middle, ref low); |
| | 27 | 956 | | return new Signed192(high, middle, low); |
| | | 957 | | } |
| | | 958 | | |
| | | 959 | | private static bool IsExactUnitAxis(Signed192 axisLengthSquared) => |
| | 445 | 960 | | axisLengthSquared.High == AxisScaleSquared.High |
| | 445 | 961 | | && axisLengthSquared.Middle == AxisScaleSquared.Middle |
| | 445 | 962 | | && axisLengthSquared.Low == AxisScaleSquared.Low; |
| | | 963 | | |
| | | 964 | | private static Signed192 SquareRaw(long raw) |
| | | 965 | | { |
| | 11229 | 966 | | ulong magnitude = (ulong)raw; |
| | 11229 | 967 | | Fixed64.Multiply64To128(magnitude, magnitude, out ulong high, out ulong low); |
| | 11229 | 968 | | return new Signed192(0UL, high, low); |
| | | 969 | | } |
| | | 970 | | |
| | | 971 | | private static RationalBound Normalize(Signed192 numerator, Signed192 denominator) |
| | | 972 | | { |
| | 582 | 973 | | if (denominator.Sign >= 0) |
| | 306 | 974 | | return new RationalBound(numerator, denominator); |
| | | 975 | | |
| | 276 | 976 | | return new RationalBound( |
| | 276 | 977 | | WideArithmetic.SubtractSigned192(default, numerator), |
| | 276 | 978 | | WideArithmetic.SubtractSigned192(default, denominator)); |
| | | 979 | | } |
| | | 980 | | |
| | | 981 | | private static int Compare(RationalBound left, RationalBound right) => |
| | 1380 | 982 | | WideArithmetic.MultiplySubtract( |
| | 1380 | 983 | | left.Numerator, |
| | 1380 | 984 | | right.Denominator, |
| | 1380 | 985 | | right.Numerator, |
| | 1380 | 986 | | left.Denominator).Sign; |
| | | 987 | | |
| | | 988 | | private static Signed192 GetDot( |
| | | 989 | | Vector3d leftEnd, |
| | | 990 | | Vector3d leftStart, |
| | | 991 | | Vector3d rightEnd, |
| | | 992 | | Vector3d rightStart) => |
| | 2613 | 993 | | WideGeometry.GetDifferenceDotProduct3D( |
| | 2613 | 994 | | leftEnd.X, leftStart.X, leftEnd.Y, leftStart.Y, leftEnd.Z, leftStart.Z, |
| | 2613 | 995 | | rightEnd.X, rightStart.X, rightEnd.Y, rightStart.Y, rightEnd.Z, rightStart.Z); |
| | | 996 | | |
| | | 997 | | internal static int EvaluateBoundedUnitPolynomialSign( |
| | | 998 | | Signed576 coefficient, |
| | | 999 | | Signed576 projection, |
| | | 1000 | | Signed576 constant, |
| | | 1001 | | Signed192 numerator, |
| | | 1002 | | Signed192 denominator) => |
| | 64 | 1003 | | Evaluate( |
| | 64 | 1004 | | new ConeData( |
| | 64 | 1005 | | default, |
| | 64 | 1006 | | default, |
| | 64 | 1007 | | default, |
| | 64 | 1008 | | coefficient, |
| | 64 | 1009 | | projection, |
| | 64 | 1010 | | constant), |
| | 64 | 1011 | | numerator, |
| | 64 | 1012 | | denominator).Sign; |
| | | 1013 | | |
| | | 1014 | | internal static int EvaluateBoundedUnitPolynomialDerivativeSign( |
| | | 1015 | | Signed576 coefficient, |
| | | 1016 | | Signed576 projection, |
| | | 1017 | | Signed192 numerator, |
| | | 1018 | | Signed192 denominator) => |
| | 64 | 1019 | | EvaluateDerivative( |
| | 64 | 1020 | | new ConeData( |
| | 64 | 1021 | | default, |
| | 64 | 1022 | | default, |
| | 64 | 1023 | | default, |
| | 64 | 1024 | | coefficient, |
| | 64 | 1025 | | projection, |
| | 64 | 1026 | | default), |
| | 64 | 1027 | | new RationalBound(numerator, denominator)).Sign; |
| | | 1028 | | |
| | | 1029 | | internal static bool TrySolveBoundedUnitPolynomial( |
| | | 1030 | | Signed576 coefficient, |
| | | 1031 | | Signed576 projection, |
| | | 1032 | | Signed576 constant, |
| | | 1033 | | Signed192 lowerNumerator, |
| | | 1034 | | Signed192 lowerDenominator, |
| | | 1035 | | Signed192 upperNumerator, |
| | | 1036 | | Signed192 upperDenominator, |
| | | 1037 | | Fixed64 outputScale, |
| | | 1038 | | out Fixed64 entry, |
| | | 1039 | | out Fixed64 exit) => |
| | 396 | 1040 | | TrySolveBoundedPolynomial( |
| | 396 | 1041 | | new ConeData( |
| | 396 | 1042 | | default, |
| | 396 | 1043 | | default, |
| | 396 | 1044 | | default, |
| | 396 | 1045 | | coefficient, |
| | 396 | 1046 | | projection, |
| | 396 | 1047 | | constant), |
| | 396 | 1048 | | new RationalBound(lowerNumerator, lowerDenominator), |
| | 396 | 1049 | | new RationalBound(upperNumerator, upperDenominator), |
| | 396 | 1050 | | outputScale, |
| | 396 | 1051 | | out entry, |
| | 396 | 1052 | | out exit); |
| | | 1053 | | } |