| | | 1 | | //======================================================================= |
| | | 2 | | // TriangleCylinderEdgeContacts.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 | | using System.Runtime.CompilerServices; |
| | | 10 | | using static FixedMathSharp.Geometry.CylinderContactAlgebra; |
| | | 11 | | using static FixedMathSharp.Geometry.CircularRimContactAlgebra; |
| | | 12 | | |
| | | 13 | | namespace FixedMathSharp.Geometry; |
| | | 14 | | |
| | | 15 | | /// <summary>Every admitted triangle-edge/cylinder-rim stationary support root.</summary> |
| | | 16 | | internal static partial class TriangleCylinderEdgeContacts |
| | | 17 | | { |
| | | 18 | | private const int ParameterSlots = 30; |
| | | 19 | | private const int ValueCellWords = (16 * (ValueWords * 64 + 11) + 255) / 64; |
| | | 20 | | |
| | | 21 | | |
| | | 22 | | |
| | | 23 | | internal static bool TryGetContact(in TriangleCircularGeometry geometry, in WideDirection3d coreOffset, in WideDirec |
| | | 24 | | Vector2d coreDirection, Fixed64 coreLength, FixedTriangle triangle, |
| | | 25 | | in ConvexContactCandidate analytic, Fixed64 analyticDepth, bool analyticClamped, Fixed64 radius, out bool hasBet |
| | | 26 | | out Vector3d radialPoint, out Vector3d trianglePoint, out Fixed64 depth, out bool clamped, |
| | | 27 | | out int mask, out int cap, out int coreSign, out int integralRadialMask, out bool strictOverlap, bool materializ |
| | | 28 | | { |
| | 253 | 29 | | int ValueWords = geometry.ValueWords; |
| | 253 | 30 | | int ValueCellWords = (16 * (geometry.ValueWords * 64 + 11) + 255) / 64; |
| | 759 | 31 | | hasBetter = false; normal = radialPoint = trianglePoint = default; depth = default; |
| | 506 | 32 | | clamped = false; mask = cap = coreSign = 0; |
| | 506 | 33 | | integralRadialMask = 5; strictOverlap = analytic.GapSign > 0; |
| | 253 | 34 | | Span<ulong> bestValues = stackalloc ulong[5 * ValueWords]; |
| | 253 | 35 | | Span<sbyte> bestSigns = stackalloc sbyte[5]; |
| | 253 | 36 | | Span<ulong> bestCell = stackalloc ulong[ValueCellWords]; |
| | 253 | 37 | | Selection best = default; |
| | 253 | 38 | | if (!TryKeepContacts(geometry, coreOffset, coreAxis, analytic, analyticDepth, analyticClamped, |
| | 260 | 39 | | bestValues, bestSigns, bestCell, ref best)) return false; |
| | 246 | 40 | | if (!best.HasValue) |
| | 170 | 41 | | return true; |
| | 76 | 42 | | strictOverlap = !best.IsZero; |
| | 81 | 43 | | if (!materialize) return true; |
| | 284 | 44 | | hasBetter = true; mask = (1 << best.Edge) | (1 << ((best.Edge + 1) % 3)); cap = best.Cap; coreSign = best.CoreSi |
| | | 45 | | TriangleCircularGeometry winnerStorage; |
| | 71 | 46 | | scoped ref readonly TriangleCircularGeometry winner = ref geometry; |
| | 71 | 47 | | if (coreSign != 0) |
| | | 48 | | { |
| | 29 | 49 | | winnerStorage = geometry.AtCoreRegion(coreOffset, coreSign); |
| | 29 | 50 | | winner = ref winnerStorage; |
| | | 51 | | } |
| | 71 | 52 | | GetMaterials(winner, coreAxis, coreDirection, coreLength, triangle, best, radius, |
| | 71 | 53 | | out normal, out radialPoint, out trianglePoint, out integralRadialMask); |
| | 71 | 54 | | if (!best.IsZero) |
| | | 55 | | { |
| | 55 | 56 | | FiniteAxisValueRoot root = Restore(bestValues, bestSigns, bestCell, best); |
| | 55 | 57 | | ConvexContactValueRoot.GetRoundedDepth(geometry.RawScale, geometry.ValueShift, |
| | 55 | 58 | | ref root, out depth, out clamped); |
| | | 59 | | } |
| | 71 | 60 | | return true; |
| | | 61 | | } |
| | | 62 | | |
| | | 63 | | internal static bool TryKeepContacts(in TriangleCircularGeometry geometry, in WideDirection3d coreOffset, in WideDir |
| | | 64 | | in ConvexContactCandidate analytic, Fixed64 analyticDepth, bool analyticClamped, |
| | | 65 | | scoped Span<ulong> bestValues, scoped Span<sbyte> bestSigns, scoped Span<ulong> bestCell, ref Selection best, |
| | | 66 | | in CylinderHullSupport hull = default, int triangleIndex = 0) |
| | | 67 | | { |
| | 1618 | 68 | | int regionStart = coreAxis.IsZero ? 0 : -1; |
| | 7192 | 69 | | for (int region = regionStart; region <= (coreAxis.IsZero ? 0 : 1); region += 2) |
| | | 70 | | { |
| | | 71 | | TriangleCircularGeometry endpointStorage; |
| | 1986 | 72 | | scoped ref readonly TriangleCircularGeometry endpoint = ref geometry; |
| | 1986 | 73 | | if (region != 0) |
| | | 74 | | { |
| | 737 | 75 | | endpointStorage = geometry.AtCoreRegion(coreOffset, region); |
| | 737 | 76 | | endpoint = ref endpointStorage; |
| | | 77 | | } |
| | 15852 | 78 | | for (int edge = 0; edge < 3; edge++) |
| | | 79 | | { |
| | 5948 | 80 | | WideDirection3d e = endpoint.Edge(edge); |
| | | 81 | | // Parallel edges project a circle; perpendicular edges project a |
| | | 82 | | // line segment. Their extrema are already analytic fan boundaries. |
| | 5948 | 83 | | if (e.Y.IsZero || e.X.IsZero && e.Z.IsZero) |
| | | 84 | | continue; |
| | 4105 | 85 | | GetBasis(e, out WideDirection3d u, out WideDirection3d v); |
| | 24608 | 86 | | for (int firstSign = -1; firstSign <= 1; firstSign += 2) |
| | 49218 | 87 | | for (int secondSign = -1; secondSign <= 1; secondSign += 2) |
| | 98432 | 88 | | for (int capSign = -1; capSign <= 1; capSign += 2) |
| | | 89 | | { |
| | 32814 | 90 | | WideDirection3d first = firstSign > 0 ? u : -u; |
| | 32814 | 91 | | WideDirection3d second = secondSign > 0 ? v : -v; |
| | 32814 | 92 | | if (!TryCharts(endpoint, coreAxis, region, edge, first, second, capSign, analytic, |
| | 32814 | 93 | | ((ulong)analyticDepth.m_rawValue << 1) | 1UL, !analyticClamped, |
| | 32814 | 94 | | bestValues, bestSigns, bestCell, ref best, hull, triangleIndex)) |
| | 8 | 95 | | return false; |
| | | 96 | | } |
| | | 97 | | } |
| | | 98 | | } |
| | 1610 | 99 | | return true; |
| | | 100 | | } |
| | | 101 | | |
| | | 102 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 103 | | private static bool TryCharts(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, int region, int edg |
| | | 104 | | in WideDirection3d originalFirst, in WideDirection3d originalSecond, int cap, scoped ConvexContactCandidate anal |
| | | 105 | | ulong upperTwiceRaw, bool hasUpperBound, |
| | | 106 | | scoped Span<ulong> bestValues, scoped Span<sbyte> bestSigns, scoped Span<ulong> bestCell, |
| | | 107 | | ref Selection best, in CylinderHullSupport hull, int triangleIndex) |
| | | 108 | | { |
| | 32814 | 109 | | int Words = geometry.ParameterWords; |
| | 32814 | 110 | | scoped ref readonly WideDirection3d first = ref originalFirst; |
| | 32814 | 111 | | scoped ref readonly WideDirection3d second = ref originalSecond; |
| | 32814 | 112 | | bool firstAdmitted = IsChartAdmitted(geometry, coreAxis, region, edge, first, second, cap); |
| | 32814 | 113 | | bool secondAdmitted = IsChartAdmitted(geometry, coreAxis, region, edge, second, first, cap); |
| | 55370 | 114 | | if (!firstAdmitted && !secondAdmitted) return true; |
| | 10258 | 115 | | if (!firstAdmitted) |
| | | 116 | | { |
| | 1782 | 117 | | first = ref originalSecond; |
| | 1782 | 118 | | second = ref originalFirst; |
| | | 119 | | } |
| | 10258 | 120 | | Span<ulong> data = stackalloc ulong[ParameterSlots * Words]; |
| | 10258 | 121 | | Span<sbyte> signs = stackalloc sbyte[ParameterSlots]; |
| | 10258 | 122 | | BuildParameter(geometry, coreAxis, region, edge, first, second, cap, data, signs); |
| | 10258 | 123 | | if (!TryChart(geometry, coreAxis, region, edge, first, second, cap, analytic, upperTwiceRaw, hasUpperBound, |
| | 10262 | 124 | | data, signs, bestValues, bestSigns, bestCell, ref best, hull, triangleIndex)) return false; |
| | 13385 | 125 | | if (!firstAdmitted || !secondAdmitted) return true; |
| | | 126 | | // Finish the first chart before mutation; winners already own copied |
| | | 127 | | // value algebra. Preserve its earlier canonical rank when depths tie. |
| | 7123 | 128 | | ReverseParameterChart(data, signs); |
| | 7123 | 129 | | WriteAdmission(geometry, coreAxis, region, edge, second, first, data, signs); |
| | 7123 | 130 | | return TryChart(geometry, coreAxis, region, edge, second, first, cap, analytic, upperTwiceRaw, hasUpperBound, |
| | 7123 | 131 | | data, signs, bestValues, bestSigns, bestCell, ref best, hull, triangleIndex); |
| | | 132 | | } |
| | | 133 | | |
| | | 134 | | private static bool IsChartAdmitted(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, int region, i |
| | | 135 | | in WideDirection3d first, in WideDirection3d second, int cap) |
| | | 136 | | { |
| | | 137 | | |
| | | 138 | | // Each admission projection is affine on t in (0,1] and must be |
| | | 139 | | // positive. Nonpositive endpoints exclude every root before construction. |
| | | 140 | | // GetBasis gives exactly one nonzero Y component. Its cap projection |
| | | 141 | | // cannot change sign inside this chart, so this check fully admits it. |
| | 65628 | 142 | | if (first.Y.Sign * cap <= 0 && WideArithmetic.AddSigned576(first.Y, second.Y).Sign * cap <= 0) |
| | 32808 | 143 | | return false; |
| | 32820 | 144 | | WideDirection3d outward = TriangleCircularGeometry.Subtract(geometry.Vertex(edge), geometry.Vertex((edge + 2) % |
| | 65640 | 145 | | Signed832 firstCone = WideDirection3d.Dot(first, outward), secondCone = WideDirection3d.Dot(second, outward); |
| | 32820 | 146 | | if (firstCone.Sign <= 0 && WideArithmetic.AddSigned832(firstCone, secondCone).Sign <= 0) |
| | 13543 | 147 | | return false; |
| | 19277 | 148 | | if (!coreAxis.IsZero) |
| | | 149 | | { |
| | 8742 | 150 | | Signed832 firstCore = WideDirection3d.Dot(coreAxis, first), secondCore = WideDirection3d.Dot(coreAxis, secon |
| | 4371 | 151 | | if (firstCore.Sign * region <= 0 && WideArithmetic.AddSigned832(firstCore, secondCore).Sign * region <= 0) |
| | 1892 | 152 | | return false; |
| | | 153 | | } |
| | 17385 | 154 | | return true; |
| | | 155 | | } |
| | | 156 | | |
| | | 157 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 158 | | private static bool TryChart(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, int region, int edge |
| | | 159 | | in WideDirection3d first, in WideDirection3d second, int cap, scoped ConvexContactCandidate analytic, |
| | | 160 | | ulong upperTwiceRaw, bool hasUpperBound, scoped Span<ulong> data, scoped Span<sbyte> signs, |
| | | 161 | | scoped Span<ulong> bestValues, scoped Span<sbyte> bestSigns, scoped Span<ulong> bestCell, |
| | | 162 | | ref Selection best, in CylinderHullSupport hull, int triangleIndex) |
| | | 163 | | { |
| | 17381 | 164 | | int Words = geometry.ParameterWords; |
| | 17381 | 165 | | int ValueWords = geometry.ValueWords; |
| | 17381 | 166 | | int ValueCellWords = (16 * (geometry.ValueWords * 64 + 11) + 255) / 64; |
| | 17381 | 167 | | Span<ulong> batchCells = stackalloc ulong[64]; |
| | 17381 | 168 | | Span<int> batchShifts = stackalloc int[8]; |
| | 17381 | 169 | | FiniteAxisValueRoots roots = WideFiniteAxisIntersection.GetFiniteValueRoots( |
| | 17381 | 170 | | data[..(5 * Words)], signs[..5], batchCells, batchShifts); |
| | 17381 | 171 | | Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords( |
| | 17381 | 172 | | data[..(5 * Words)], signs[..5])]; |
| | 17381 | 173 | | Span<ulong> values = stackalloc ulong[5 * ValueWords]; |
| | 17381 | 174 | | Span<sbyte> valueSigns = stackalloc sbyte[5]; |
| | 17381 | 175 | | Span<ulong> valueCell = stackalloc ulong[ValueCellWords]; |
| | 17381 | 176 | | bool valuesReady = false; |
| | 55744 | 177 | | for (int ordinal = 0; ordinal < roots.Count; ordinal++) |
| | | 178 | | { |
| | 10499 | 179 | | scoped FiniteAxisValueRoot root = FiniteAxisValueRoots.GetRoot(roots, ordinal, |
| | 10499 | 180 | | data[..(5 * Words)], signs[..5], cell); |
| | | 181 | | // Zero third-vertex alignment is the already-ranked face normal; |
| | | 182 | | // this boundary cannot improve that earlier canonical feature. |
| | 10499 | 183 | | if (Sign(ref root, data, signs, 26, 2) <= 0) |
| | | 184 | | continue; |
| | 8448 | 185 | | if (!hull.IsEmpty && !SupportsHull(hull, geometry, geometry.Vertex(edge), first, second, ref root)) |
| | | 186 | | continue; |
| | | 187 | | // Boundary roots belong to the explicit seam fan. Reject the |
| | | 188 | | // opposite endpoint's hemisphere before sign rejection or ranking. |
| | 5106 | 189 | | if (!coreAxis.IsZero && Sign(ref root, data, signs, 28, 2) <= 0) |
| | | 190 | | continue; |
| | 5004 | 191 | | int kSign = Sign(ref root, data, signs, 8, 2); |
| | 5004 | 192 | | if (kSign == 0 || Sign(ref root, data, signs, 10, 3) != -kSign) |
| | | 193 | | continue; |
| | 2588 | 194 | | int gapSign = Sign(ref root, data, signs, 13, 3) * kSign; |
| | 2588 | 195 | | if (gapSign < 0) |
| | 8 | 196 | | return false; |
| | 2580 | 197 | | if (!hull.IsEmpty && !TriangleCylinderRimWitnesses.IsRootPointOnEdge(geometry, edge, first, second, ref root |
| | | 198 | | continue; |
| | 2406 | 199 | | if (best.IsZero || analytic.GapSign == 0) |
| | | 200 | | continue; |
| | 2310 | 201 | | var candidate = new Selection |
| | 2310 | 202 | | { |
| | 2310 | 203 | | HasValue = true, |
| | 2310 | 204 | | First = first, |
| | 2310 | 205 | | Second = second, |
| | 2310 | 206 | | Edge = edge, |
| | 2310 | 207 | | Cap = cap, |
| | 2310 | 208 | | CoreSign = region, |
| | 2310 | 209 | | ParameterOrdinal = ordinal, |
| | 2310 | 210 | | Triangle = triangleIndex |
| | 2310 | 211 | | }; |
| | 2310 | 212 | | if (gapSign == 0) |
| | | 213 | | { |
| | 34 | 214 | | candidate.IsZero = true; best = candidate; |
| | 17 | 215 | | continue; |
| | | 216 | | } |
| | | 217 | | // The unclamped analytic depth d bounds its exact gap by d+1/2 raw. |
| | | 218 | | // Reject positive roots at or above that bound before value mapping; |
| | | 219 | | // admitted negative and zero gaps have already retained their semantics. |
| | 2293 | 220 | | if (hasUpperBound && ConvexContactValueRoot.CompareSquaredGapToTwiceRaw(ref root, |
| | 2293 | 221 | | data.Slice(16 * Words, 5 * Words), signs.Slice(16, 5), |
| | 2293 | 222 | | data.Slice(21 * Words, 5 * Words), signs.Slice(21, 5), upperTwiceRaw) >= 0) |
| | | 223 | | continue; |
| | 116 | 224 | | if (!valuesReady) |
| | | 225 | | { |
| | 115 | 226 | | BuildValues(geometry.Edge(edge), geometry.CapOffset(edge, cap), geometry.Radius, geometry.ValueShift, va |
| | 115 | 227 | | valuesReady = true; |
| | | 228 | | } |
| | 116 | 229 | | scoped FiniteAxisValueRoot value = ConvexContactValueRoot.MapSquaredValue( |
| | 116 | 230 | | geometry.RawScale, geometry.ValueShift, ref root, |
| | 116 | 231 | | data.Slice(16 * Words, 5 * Words), signs.Slice(16, 5), |
| | 116 | 232 | | data.Slice(21 * Words, 5 * Words), signs.Slice(21, 5), |
| | 116 | 233 | | values, valueSigns, valueCell); |
| | 116 | 234 | | int comparison = best.HasValue |
| | 116 | 235 | | ? WideFiniteAxisIntersection.CompareFiniteValueRoots(value, Restore(bestValues, bestSigns, bestCell, bes |
| | 116 | 236 | | : ConvexContactValueRoot.CompareRootSquared(geometry.RawScale, geometry.ValueShift, ref value, analytic) |
| | 116 | 237 | | if (comparison >= 0) |
| | | 238 | | continue; |
| | 231 | 239 | | values.CopyTo(bestValues); valueSigns.CopyTo(bestSigns); valueCell.CopyTo(bestCell); |
| | 154 | 240 | | candidate.ValueOrdinal = value.Ordinal; candidate.ValueShift = value.DenominatorShift; |
| | 154 | 241 | | candidate.IsRational = value.IsRational; best = candidate; |
| | | 242 | | } |
| | 17373 | 243 | | return true; |
| | | 244 | | } |
| | | 245 | | |
| | | 246 | | private static void BuildParameter(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, int region, in |
| | | 247 | | in WideDirection3d first, in WideDirection3d second, int cap, Span<ulong> data, Span<sbyte> signs) |
| | | 248 | | { |
| | | 249 | | |
| | 10347 | 250 | | CircularRimContactAlgebra.BuildParameter(geometry.CapOffset(edge, cap), geometry.Radius, geometry.RawScale, |
| | 10347 | 251 | | first, second, data, signs, useRigidBounds: !geometry.IsAffine); |
| | 10347 | 252 | | WriteAdmission(geometry, coreAxis, region, edge, first, second, data, signs); |
| | 10347 | 253 | | } |
| | | 254 | | |
| | | 255 | | private static void WriteAdmission(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, int region, in |
| | | 256 | | in WideDirection3d first, in WideDirection3d second, Span<ulong> data, Span<sbyte> signs) |
| | | 257 | | { |
| | | 258 | | |
| | 17470 | 259 | | WideDirection3d outward = TriangleCircularGeometry.Subtract(geometry.Vertex(edge), geometry.Vertex((edge + 2) % |
| | 17470 | 260 | | Write(data, signs, 26, WideDirection3d.Dot(outward, first)); |
| | 17470 | 261 | | Write(data, signs, 27, WideDirection3d.Dot(outward, second)); |
| | 17470 | 262 | | if (!coreAxis.IsZero) |
| | | 263 | | { |
| | 5028 | 264 | | Signed832 firstCore = WideDirection3d.Dot(coreAxis, first), secondCore = WideDirection3d.Dot(coreAxis, secon |
| | 2514 | 265 | | Write(data, signs, 28, region < 0 ? WideArithmetic.SubtractSigned832(default, firstCore) : firstCore); |
| | 2514 | 266 | | Write(data, signs, 29, region < 0 ? WideArithmetic.SubtractSigned832(default, secondCore) : secondCore); |
| | | 267 | | } |
| | 17470 | 268 | | } |
| | | 269 | | |
| | | 270 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | | 271 | | internal static void GetMaterials(in TriangleCircularGeometry geometry, in WideDirection3d coreAxis, |
| | | 272 | | Vector2d coreDirection, Fixed64 coreLength, FixedTriangle triangle, in Selection best, Fixed64 radius, |
| | | 273 | | out Vector3d normal, out Vector3d radialPoint, out Vector3d trianglePoint, out int integralRadialMask) |
| | | 274 | | { |
| | 89 | 275 | | int Words = geometry.ParameterWords; |
| | 89 | 276 | | Span<ulong> data = stackalloc ulong[ParameterSlots * Words]; |
| | 89 | 277 | | Span<sbyte> signs = stackalloc sbyte[ParameterSlots]; |
| | 89 | 278 | | BuildParameter(geometry, coreAxis, best.CoreSign, best.Edge, best.First, best.Second, best.Cap, data, signs); |
| | 89 | 279 | | Span<ulong> cell = stackalloc ulong[WideFiniteAxisIntersection.GetFiniteValueRootCellWords(data[..(5 * Words)], |
| | 89 | 280 | | bool found = WideFiniteAxisIntersection.TryGetFiniteValueRoot(data[..(5 * Words)], signs[..5], |
| | 89 | 281 | | best.ParameterOrdinal, cell, out FiniteAxisValueRoot root); |
| | | 282 | | System.Diagnostics.Debug.Assert(found); |
| | 89 | 283 | | trianglePoint = TriangleCylinderRimWitnesses.GetRootPoint(geometry, triangle, best.Edge, best.First, best.Second |
| | 89 | 284 | | Span<ulong> gradient = stackalloc ulong[54]; |
| | 89 | 285 | | Span<sbyte> gradientSigns = stackalloc sbyte[6]; |
| | 89 | 286 | | WriteGradient(best.First, best.Second, gradient, gradientSigns); |
| | 178 | 287 | | gradient.Slice(18, 18).Clear(); gradientSigns.Slice(2, 2).Clear(); |
| | 89 | 288 | | radialPoint = ConvexContactValueRoot.GetScaledNormalizedDirection(ref root, gradient, gradientSigns, radius, -1) |
| | 89 | 289 | | integralRadialMask = 5; |
| | 89 | 290 | | if (best.CoreSign != 0) |
| | | 291 | | { |
| | 36 | 292 | | Vector3d core = TriangleCapsuleSlabEndpointWitnesses.GetCore(coreDirection, coreLength, best.CoreSign, |
| | 36 | 293 | | out FixedPointAnchorTerm3d term); |
| | 36 | 294 | | radialPoint = TriangleCapsuleSlabEndpointWitnesses.AdjustRoot(ref root, gradient, gradientSigns, |
| | 36 | 295 | | radius, core, term, radialPoint, out integralRadialMask); |
| | | 296 | | } |
| | 89 | 297 | | WriteGradient(Transform(geometry.WorldBasis, best.First), Transform(geometry.WorldBasis, best.Second), gradient, |
| | 89 | 298 | | normal = ConvexContactValueRoot.GetNormalizedDirection(ref root, gradient, gradientSigns, 1); |
| | 89 | 299 | | } |
| | | 300 | | |
| | | 301 | | internal static FiniteAxisValueRoot Restore(ReadOnlySpan<ulong> values, ReadOnlySpan<sbyte> signs, |
| | 93 | 302 | | Span<ulong> cell, in Selection selection) => new() |
| | 93 | 303 | | { |
| | 93 | 304 | | Coefficients = values, |
| | 93 | 305 | | Signs = signs, |
| | 93 | 306 | | LowerNumerator = cell, |
| | 93 | 307 | | DenominatorShift = selection.ValueShift, |
| | 93 | 308 | | Ordinal = selection.ValueOrdinal, |
| | 93 | 309 | | IsRational = selection.IsRational |
| | 93 | 310 | | }; |
| | | 311 | | |
| | | 312 | | private static bool SupportsHull(in CylinderHullSupport hull, in TriangleCircularGeometry geometry, in WideDirection |
| | | 313 | | in WideDirection3d first, in WideDirection3d second, ref FiniteAxisValueRoot root) |
| | | 314 | | { |
| | 13624 | 315 | | Span<ulong> query = stackalloc ulong[26]; Span<sbyte> signs = stackalloc sbyte[2]; |
| | 82472 | 316 | | for (int i = 0; i < hull.Count; i++) |
| | | 317 | | { |
| | 37766 | 318 | | WideDirection3d difference = TriangleCircularGeometry.Subtract(point, geometry.TransformHullVertex(hull.Vert |
| | 75532 | 319 | | Signed832 a = WideDirection3d.Dot(difference, first), b = WideDirection3d.Dot(difference, second); |
| | 75532 | 320 | | WideArithmetic.GetMagnitude(a, query[..13]); signs[0] = (sbyte)a.Sign; |
| | 75532 | 321 | | WideArithmetic.GetMagnitude(b, query[13..]); signs[1] = (sbyte)b.Sign; |
| | 41108 | 322 | | if (WideFiniteAxisIntersection.GetSignAtFiniteValueRootAndRefine(ref root, query, signs) < 0) return false; |
| | | 323 | | } |
| | 3470 | 324 | | return true; |
| | | 325 | | } |
| | | 326 | | } |