| | | 1 | | //======================================================================= |
| | | 2 | | // ConePlaneRaySelection.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>Caller-owned storage for an admitted, unrounded plane-ray maximum.</summary> |
| | | 13 | | internal ref struct ConePlaneRaySelection |
| | | 14 | | { |
| | | 15 | | internal const int FieldWords = 64; |
| | | 16 | | internal const int RootWords = 40; |
| | | 17 | | internal const int StorageWords = 4 * FieldWords + RootWords + ConePlaneRayPoint.StorageWords; |
| | | 18 | | internal const int SignCount = 4 + ConePlaneRayPoint.SignCount; |
| | | 19 | | internal readonly Span<ulong> Values; |
| | | 20 | | internal readonly Span<int> Signs; |
| | | 21 | | internal bool HasValue; |
| | | 22 | | internal ConePlaneRayEvent CurrentEvent, MaximumEvent; |
| | | 23 | | internal ConePlaneRayEventSource CurrentSource, MaximumSource; |
| | | 24 | | private Signed832 metric; |
| | | 25 | | private Signed320 scale; |
| | | 26 | | private readonly int _fieldWords, _rootWords; |
| | 20769 | 27 | | internal ReadOnlySpan<ulong> Root => Values.Slice(4 * _fieldWords, _rootWords); |
| | 23958 | 28 | | internal ConePlaneRayPoint Point => new(Values[(4 * _fieldWords + _rootWords)..], Signs[4..], _fieldWords); |
| | | 29 | | |
| | | 30 | | internal ConePlaneRaySelection(Span<ulong> values, Span<int> signs, |
| | | 31 | | int fieldWords = FieldWords, int rootWords = RootWords) |
| | | 32 | | { |
| | 113666 | 33 | | int storageWords = 12 * fieldWords + rootWords; |
| | 113666 | 34 | | if (values.Length < storageWords || signs.Length < SignCount) |
| | 2 | 35 | | throw new ArgumentException("The plane-ray selection storage is too small."); |
| | 340992 | 36 | | Values = values[..storageWords]; Signs = signs[..SignCount]; HasValue = false; |
| | 227328 | 37 | | _fieldWords = fieldWords; _rootWords = rootWords; |
| | 113664 | 38 | | CurrentEvent = MaximumEvent = default; |
| | 113664 | 39 | | CurrentSource = MaximumSource = default; |
| | 227328 | 40 | | metric = default; scale = default; |
| | 113664 | 41 | | } |
| | | 42 | | |
| | | 43 | | internal bool Keep(scoped ContactQuadratic numerator, scoped ContactQuadratic denominator, scoped ReadOnlySpan<ulong |
| | | 44 | | in ConePlaneRayFrame frame, scoped ConePlaneRayPoint point) |
| | | 45 | | { |
| | 15470 | 46 | | if (numerator.Sign(root) < 0 || denominator.Sign(root) <= 0) |
| | 2 | 47 | | throw new ArgumentException("The admitted depth must be nonnegative with a positive denominator."); |
| | 15468 | 48 | | ContactQuadratic bestN = ContactQuadratic.At(Values, Signs, 0, _fieldWords); |
| | 15468 | 49 | | ContactQuadratic bestD = ContactQuadratic.At(Values, Signs, 1, _fieldWords); |
| | 15468 | 50 | | if (HasValue) |
| | | 51 | | { |
| | 5811 | 52 | | int comparison = ContactQuadratic.CompareRatios(numerator, denominator, root, bestN, bestD, Root); |
| | 5811 | 53 | | int pointOrder = comparison == 0 ? point.CompareTo(Point, root, Root) : 0; |
| | 5811 | 54 | | if (comparison < 0 || comparison == 0 && (pointOrder > 0 || pointOrder == 0 |
| | 5811 | 55 | | && ConePlaneRayEvents.CompareCoincidentProvenance(CurrentSource, CurrentEvent, MaximumSource, Maximu |
| | 3775 | 56 | | return false; |
| | | 57 | | } |
| | 23386 | 58 | | numerator.CopyTo(bestN); denominator.CopyTo(bestD); |
| | 11693 | 59 | | Span<ulong> retainedRoot = Values.Slice(4 * _fieldWords, _rootWords); |
| | 23386 | 60 | | root.CopyTo(retainedRoot); retainedRoot[root.Length..].Clear(); |
| | 11693 | 61 | | metric = frame.NormalSquared; |
| | 11693 | 62 | | scale = WideArithmetic.AddSigned320(frame.Finite.ShapeFrame.CoordinateDenominator, frame.Finite.ShapeFrame.Coord |
| | 11693 | 63 | | HasValue = true; |
| | 11693 | 64 | | MaximumEvent = CurrentEvent; |
| | 11693 | 65 | | MaximumSource = CurrentSource; |
| | | 66 | | // Rational points have zero radical coefficients, so the exit's root |
| | | 67 | | // also represents them. Side and certified exits already share the |
| | | 68 | | // point field. Retain only the winning point, never a wide event pool. |
| | 11693 | 69 | | point.CopyTo(Point); |
| | 11693 | 70 | | return true; |
| | | 71 | | } |
| | | 72 | | |
| | | 73 | | internal bool KeepEvaluated(scoped in ConePlaneRaySelection other, in ConePlaneRayFrame frame) |
| | | 74 | | { |
| | 9094 | 75 | | if (!other.HasValue) return false; |
| | 13056 | 76 | | CurrentSource = other.MaximumSource; CurrentEvent = other.MaximumEvent; |
| | 6528 | 77 | | return Keep(ContactQuadratic.At(other.Values, other.Signs, 0, other._fieldWords), |
| | 6528 | 78 | | ContactQuadratic.At(other.Values, other.Signs, 1, other._fieldWords), other.Root, frame, other.Point); |
| | | 79 | | } |
| | | 80 | | |
| | | 81 | | /// <summary>Restores metadata after reborrowing previously populated winning coefficient/sign storage.</summary> |
| | | 82 | | internal void Restore(in ConePlaneRayFrame frame, in ConePlaneRayEventSource source, ConePlaneRayEvent descriptor) |
| | | 83 | | { |
| | 9045 | 84 | | metric = frame.NormalSquared; |
| | 9045 | 85 | | scale = WideArithmetic.AddSigned320(frame.Finite.ShapeFrame.CoordinateDenominator, frame.Finite.ShapeFrame.Coord |
| | 27135 | 86 | | CurrentSource = MaximumSource = source; CurrentEvent = MaximumEvent = descriptor; HasValue = true; |
| | 9045 | 87 | | } |
| | | 88 | | |
| | | 89 | | internal bool TryMaterialize(in ConePlaneRayFrame frame, int orientation, |
| | | 90 | | out Vector3d lower, out Vector3d upper, out Fixed64 depth) |
| | 1600 | 91 | | => TryMaterialize(frame, orientation, Vector3d.Zero, out lower, out upper, out depth); |
| | | 92 | | |
| | | 93 | | internal bool TryMaterialize(in ConePlaneRayFrame frame, int orientation, Vector3d samplingOrigin, |
| | | 94 | | out Vector3d lower, out Vector3d upper, out Fixed64 depth) |
| | | 95 | | { |
| | 3248 | 96 | | lower = upper = default; depth = default; |
| | 1625 | 97 | | if (orientation != 1 && orientation != -1) throw new ArgumentOutOfRangeException(nameof(orientation)); |
| | 1623 | 98 | | return HasValue && ConePlaneRayPointMaterialization.TryGetPointInFrame(frame, Point, Root, samplingOrigin, out l |
| | 1623 | 99 | | && TryGetRoundedMaximumDepth(out depth) |
| | 1623 | 100 | | && ConePlaneRayPointMaterialization.TryGetExitPointInFrame(frame, Point, Root, |
| | 1623 | 101 | | ContactQuadratic.At(Values, Signs, 0, _fieldWords), ContactQuadratic.At(Values, Signs, 1, _fieldWords), |
| | | 102 | | } |
| | | 103 | | |
| | | 104 | | internal bool TryGetRoundedMaximumDepth(out Fixed64 depth) |
| | | 105 | | { |
| | 1714 | 106 | | depth = Fixed64.Zero; |
| | 1714 | 107 | | if (!HasValue) |
| | 4 | 108 | | return false; |
| | 1710 | 109 | | Span<ulong> metricWords = stackalloc ulong[13]; |
| | 1710 | 110 | | WideArithmetic.GetMagnitude(metric, metricWords); |
| | 1710 | 111 | | Span<ulong> scaleWords = stackalloc ulong[5]; |
| | 1710 | 112 | | WideArithmetic.GetMagnitude(scale, out scaleWords[4], out scaleWords[3], out scaleWords[2], out scaleWords[1], o |
| | 1710 | 113 | | return ContactQuadratic.TryRoundRootRatio(ContactQuadratic.At(Values, Signs, 0, _fieldWords), |
| | 1710 | 114 | | ContactQuadratic.At(Values, Signs, 1, _fieldWords), Root, metricWords, scaleWords, out depth); |
| | | 115 | | } |
| | | 116 | | |
| | | 117 | | internal Fixed64 GetRoundedMaximumDepth() |
| | | 118 | | { |
| | 121 | 119 | | if (!HasValue) |
| | 1 | 120 | | throw new InvalidOperationException("The plane-ray selection has no admitted value."); |
| | 120 | 121 | | if (!TryGetRoundedMaximumDepth(out Fixed64 depth)) |
| | 1 | 122 | | throw new OverflowException("The exact plane-ray maximum is outside the Fixed64 range."); |
| | 119 | 123 | | return depth; |
| | | 124 | | } |
| | | 125 | | |
| | | 126 | | /// <summary>Compares physical depths across certified coplanar members in the same cone chart.</summary> |
| | | 127 | | /// <remarks>Member coordinate denominators may differ; the reduced normal metric must agree.</remarks> |
| | | 128 | | internal int CompareDepth(scoped in ConePlaneRaySelection other) |
| | | 129 | | { |
| | 39 | 130 | | if (!HasValue || !other.HasValue) |
| | 2 | 131 | | throw new InvalidOperationException("Both plane-ray selections must have an admitted value."); |
| | 37 | 132 | | if (metric.IsZero || !metric.Equals(other.metric)) |
| | 2 | 133 | | throw new ArgumentException("The plane-ray selections must share a nonzero reduced normal metric."); |
| | 35 | 134 | | ContactQuadratic firstD = ContactQuadratic.At(Values, Signs, 1, _fieldWords); |
| | 35 | 135 | | ContactQuadratic secondD = ContactQuadratic.At(other.Values, other.Signs, 1, other._fieldWords); |
| | | 136 | | // sqrt(metric) cancels. Each positive 2D scale adds at most three limbs; |
| | | 137 | | // the ratio owner's subsequent cross products reserve their full widths. |
| | 35 | 138 | | int words = Math.Max(ContactQuadratic.ActiveWords(firstD), ContactQuadratic.ActiveWords(secondD)) + 5; |
| | 70 | 139 | | Span<ulong> values = stackalloc ulong[4 * words]; Span<int> signs = stackalloc int[4]; |
| | 35 | 140 | | ContactQuadratic firstScaled = ContactQuadratic.At(values, signs, 0, words); |
| | 35 | 141 | | ContactQuadratic secondScaled = ContactQuadratic.At(values, signs, 1, words); |
| | 35 | 142 | | ContactQuadratic.Scale(firstD, Signed576.ExtendValue(scale), firstScaled); |
| | 35 | 143 | | ContactQuadratic.Scale(secondD, Signed576.ExtendValue(other.scale), secondScaled); |
| | 35 | 144 | | return ContactQuadratic.CompareRatios(ContactQuadratic.At(Values, Signs, 0, _fieldWords), firstScaled, Root, |
| | 35 | 145 | | ContactQuadratic.At(other.Values, other.Signs, 0, other._fieldWords), secondScaled, other.Root); |
| | | 146 | | } |
| | | 147 | | } |