| | | 1 | | //======================================================================= |
| | | 2 | | // FixedPointAnchorTerm3d.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 | | /// Retains the residual between rounded local feature components and their |
| | | 14 | | /// exact centered-axis construction over the shared 2*Q32.32 denominator. |
| | | 15 | | /// </summary> |
| | | 16 | | internal readonly struct FixedPointAnchorTerm3d : IEquatable<FixedPointAnchorTerm3d> |
| | | 17 | | { |
| | | 18 | | internal readonly long X; |
| | | 19 | | internal readonly long Y; |
| | | 20 | | internal readonly long Z; |
| | | 21 | | |
| | | 22 | | internal FixedPointAnchorTerm3d( |
| | | 23 | | long x, |
| | | 24 | | long y, |
| | | 25 | | long z) |
| | | 26 | | { |
| | 8550 | 27 | | X = x; |
| | 8550 | 28 | | Y = y; |
| | 8550 | 29 | | Z = z; |
| | 8550 | 30 | | } |
| | | 31 | | |
| | 27409 | 32 | | internal bool IsZero => X == 0L && Y == 0L && Z == 0L; |
| | | 33 | | |
| | | 34 | | internal static FixedPointAnchorTerm3d CreateCenteredAxisSupport( |
| | | 35 | | Vector3d localAxis, |
| | | 36 | | Fixed64 signedAxisLength, |
| | | 37 | | Vector3d localRadialDirection, |
| | | 38 | | Fixed64 radius, |
| | | 39 | | Vector3d roundedAxialOffset, |
| | | 40 | | Vector3d roundedRadialOffset) => |
| | 8530 | 41 | | new( |
| | 8530 | 42 | | GetResidual( |
| | 8530 | 43 | | localAxis.X, |
| | 8530 | 44 | | signedAxisLength, |
| | 8530 | 45 | | localRadialDirection.X, |
| | 8530 | 46 | | radius, |
| | 8530 | 47 | | roundedAxialOffset.X, |
| | 8530 | 48 | | roundedRadialOffset.X), |
| | 8530 | 49 | | GetResidual( |
| | 8530 | 50 | | localAxis.Y, |
| | 8530 | 51 | | signedAxisLength, |
| | 8530 | 52 | | localRadialDirection.Y, |
| | 8530 | 53 | | radius, |
| | 8530 | 54 | | roundedAxialOffset.Y, |
| | 8530 | 55 | | roundedRadialOffset.Y), |
| | 8530 | 56 | | GetResidual( |
| | 8530 | 57 | | localAxis.Z, |
| | 8530 | 58 | | signedAxisLength, |
| | 8530 | 59 | | localRadialDirection.Z, |
| | 8530 | 60 | | radius, |
| | 8530 | 61 | | roundedAxialOffset.Z, |
| | 8530 | 62 | | roundedRadialOffset.Z)); |
| | | 63 | | |
| | | 64 | | internal static FixedPointAnchorTerm3d CreateRadialSupport( |
| | | 65 | | Vector3d localRadialDirection, |
| | | 66 | | Fixed64 radius, |
| | | 67 | | Vector3d roundedRadialOffset) => |
| | 421 | 68 | | CreateCenteredAxisSupport( |
| | 421 | 69 | | Vector3d.Zero, |
| | 421 | 70 | | Fixed64.Zero, |
| | 421 | 71 | | localRadialDirection, |
| | 421 | 72 | | radius, |
| | 421 | 73 | | Vector3d.Zero, |
| | 421 | 74 | | roundedRadialOffset); |
| | | 75 | | |
| | | 76 | | internal static FixedPointAnchorTerm3d LiftPlanarXZ( |
| | | 77 | | FixedPointAnchorTerm2d planarTerm) => |
| | 5 | 78 | | new(planarTerm.X, 0L, planarTerm.Y); |
| | | 79 | | |
| | | 80 | | internal static long GetResidual( |
| | | 81 | | Fixed64 localAxis, |
| | | 82 | | Fixed64 signedAxisLength, |
| | | 83 | | Fixed64 localRadialDirection, |
| | | 84 | | Fixed64 radius, |
| | | 85 | | Fixed64 roundedAxialOffset, |
| | | 86 | | Fixed64 roundedRadialOffset) |
| | | 87 | | { |
| | | 88 | | // Retain the rounding residual of the local feature construction. |
| | | 89 | | // The axial term can include two rounding steps (length/2, then |
| | | 90 | | // multiplication by the axis), not just a single half-raw error. |
| | | 91 | | // Only the low word is retained. Unchecked raw arithmetic preserves |
| | | 92 | | // that word through cancellation even when intermediate products or |
| | | 93 | | // the rounded-offset sum overflow; Fixed64 operators would not. |
| | 26895 | 94 | | return unchecked( |
| | 26895 | 95 | | localAxis.m_rawValue * signedAxisLength.m_rawValue |
| | 26895 | 96 | | + 2L * localRadialDirection.m_rawValue * radius.m_rawValue |
| | 26895 | 97 | | - Fixed64.Two.m_rawValue |
| | 26895 | 98 | | * (roundedAxialOffset.m_rawValue + roundedRadialOffset.m_rawValue)); |
| | | 99 | | } |
| | | 100 | | |
| | | 101 | | internal static Signed192 Denominator => |
| | 5371 | 102 | | Signed192.Signed(Fixed64.Two.m_rawValue); |
| | | 103 | | |
| | | 104 | | public bool Equals(FixedPointAnchorTerm3d other) => |
| | 825 | 105 | | X == other.X && Y == other.Y && Z == other.Z; |
| | | 106 | | |
| | | 107 | | public override int GetHashCode() |
| | | 108 | | { |
| | | 109 | | unchecked |
| | | 110 | | { |
| | 2 | 111 | | int hash = 17; |
| | 2 | 112 | | hash = (hash * 31) + X.GetHashCode(); |
| | 2 | 113 | | hash = (hash * 31) + Y.GetHashCode(); |
| | 2 | 114 | | hash = (hash * 31) + Z.GetHashCode(); |
| | 2 | 115 | | return hash; |
| | | 116 | | } |
| | | 117 | | } |
| | | 118 | | } |