| | | 1 | | //======================================================================= |
| | | 2 | | // CylinderHullSupport.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 static FixedMathSharp.Geometry.CylinderContactAlgebra; |
| | | 10 | | |
| | | 11 | | namespace FixedMathSharp.Geometry; |
| | | 12 | | |
| | | 13 | | /// <summary>A single exact cylinder chart shared by every feature of a convex hull.</summary> |
| | | 14 | | internal readonly ref struct CylinderHullSupport |
| | | 15 | | { |
| | | 16 | | private readonly ReadOnlySpan<Vector3d> points; |
| | | 17 | | internal readonly CylinderPolytopeFrame Frame; |
| | | 18 | | private readonly Signed320 divisor; |
| | | 19 | | internal readonly Signed320 RawScale; |
| | | 20 | | internal readonly Signed576 HalfHeight, Radius; |
| | | 21 | | internal readonly WideRationalBasis3d WorldBasis; |
| | | 22 | | internal readonly int ValueShift; |
| | | 23 | | internal readonly bool IsAffine; |
| | 85948 | 24 | | internal bool IsEmpty => points.IsEmpty; |
| | 405589 | 25 | | internal int Count => points.Length; |
| | | 26 | | |
| | | 27 | | internal CylinderHullSupport(in ScaledRigidFrame3d source, ReadOnlySpan<Vector3d> points, |
| | | 28 | | Vector3d center, FixedQuaternion rotation, Fixed64 height, Fixed64 radius) |
| | | 29 | | { |
| | 247 | 30 | | this.points = points; |
| | 247 | 31 | | IsAffine = !source.IsRigid; |
| | 247 | 32 | | Frame = IsAffine ? new CylinderPolytopeFrame(source, center, rotation, Signed192.Raw(height), radius) |
| | 247 | 33 | | : new CylinderPolytopeFrame(center, rotation, height, radius, source.RigidOrigin, source.Rotation); |
| | 247 | 34 | | WorldBasis = new WideRationalBasis3d(rotation); |
| | 247 | 35 | | Signed320 scale = WideArithmetic.AddSigned320(Frame.CoordinateDenominator, Frame.CoordinateDenominator); |
| | 247 | 36 | | Signed320 radial = Signed320.NarrowValue(WideArithmetic.MultiplySigned320(Frame.CoordinateDenominator, Frame.Rad |
| | 247 | 37 | | divisor = WideArithmetic.GetGreatestCommonDivisor(WideArithmetic.GetGreatestCommonDivisor(scale, Frame.Cap), rad |
| | 4398 | 38 | | for (int i = 0; i < points.Length; i++) |
| | | 39 | | { |
| | 1952 | 40 | | WideAxis3 point = Frame.Transform(points[i]); |
| | 1952 | 41 | | divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, point.X); |
| | 1952 | 42 | | divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, point.Y); |
| | 1952 | 43 | | divisor = WideArithmetic.GetGreatestCommonDivisor(divisor, point.Z); |
| | | 44 | | } |
| | 247 | 45 | | RawScale = WideArithmetic.DivideExactSigned320(scale, divisor); |
| | 247 | 46 | | HalfHeight = Signed576.ExtendValue(WideArithmetic.DivideExactSigned320(Frame.Cap, divisor)); |
| | 247 | 47 | | Radius = Signed576.ExtendValue(WideArithmetic.DivideExactSigned320(radial, divisor)); |
| | 247 | 48 | | Span<Signed576> bounds = stackalloc Signed576[3] { HalfHeight, Radius, default }; |
| | 247 | 49 | | ValueShift = TriangleCircularGeometry.GetValueShift(bounds, 4); |
| | 4398 | 50 | | for (int i = 0; i < points.Length; i++) |
| | | 51 | | { |
| | 1952 | 52 | | WideDirection3d point = Vertex(i); |
| | 5856 | 53 | | bounds[0] = point.X; bounds[1] = point.Y; bounds[2] = point.Z; |
| | 1952 | 54 | | ValueShift = Math.Max(ValueShift, TriangleCircularGeometry.GetValueShift(bounds, 4)); |
| | | 55 | | } |
| | 247 | 56 | | } |
| | | 57 | | |
| | | 58 | | internal WideDirection3d Vertex(int index) |
| | | 59 | | { |
| | 399469 | 60 | | WideAxis3 point = Frame.Transform(points[index]); |
| | 399469 | 61 | | return new WideDirection3d(WideArithmetic.DivideExactSigned320(point.X, divisor), |
| | 399469 | 62 | | WideArithmetic.DivideExactSigned320(point.Y, divisor), WideArithmetic.DivideExactSigned320(point.Z, divisor) |
| | | 63 | | } |
| | | 64 | | |
| | | 65 | | internal bool Supports(in WideDirection3d point, ReadOnlySpan<ulong> direction, ReadOnlySpan<int> signs) |
| | | 66 | | { |
| | 1928 | 67 | | Span<ulong> projection = stackalloc ulong[Words]; |
| | 33394 | 68 | | for (int i = 0; i < Count; i++) |
| | | 69 | | { |
| | 14854 | 70 | | Dot(TriangleCircularGeometry.Subtract(point, Vertex(i)), direction, signs, projection, out int sign); |
| | 14939 | 71 | | if (sign < 0) return false; |
| | | 72 | | } |
| | 1843 | 73 | | return true; |
| | | 74 | | } |
| | | 75 | | |
| | | 76 | | internal bool Supports(in TriangleCircularGeometry geometry, in WideDirection3d point, |
| | | 77 | | ReadOnlySpan<ulong> direction, ReadOnlySpan<int> signs) |
| | | 78 | | { |
| | 59798 | 79 | | Span<ulong> projection = stackalloc ulong[Words]; |
| | 693074 | 80 | | for (int i = 0; i < Count; i++) |
| | | 81 | | { |
| | 317714 | 82 | | Dot(TriangleCircularGeometry.Subtract(point, geometry.TransformHullVertex(Vertex(i))), |
| | 317714 | 83 | | direction, signs, projection, out int sign); |
| | 348689 | 84 | | if (sign < 0) return false; |
| | | 85 | | } |
| | 28823 | 86 | | return true; |
| | | 87 | | } |
| | | 88 | | |
| | | 89 | | internal int GetCoreValueShift(in TriangleCircularGeometry chart, in WideDirection3d coreOffset) |
| | | 90 | | { |
| | 121 | 91 | | Span<Signed576> bounds = stackalloc Signed576[4] { chart.HalfHeight, chart.Radius, coreOffset.X, coreOffset.Z }; |
| | 121 | 92 | | int shift = TriangleCircularGeometry.GetValueShift(bounds, 5); |
| | 2178 | 93 | | for (int i = 0; i < Count; i++) |
| | | 94 | | { |
| | 968 | 95 | | WideDirection3d point = chart.TransformHullVertex(Vertex(i)); |
| | 2904 | 96 | | bounds[0] = point.X; bounds[1] = point.Y; bounds[2] = point.Z; |
| | 968 | 97 | | shift = Math.Max(shift, TriangleCircularGeometry.GetValueShift(bounds, 5)); |
| | | 98 | | } |
| | 121 | 99 | | return shift; |
| | | 100 | | } |
| | | 101 | | |
| | | 102 | | internal bool HasExtrudedBallMinimumCertificate(in ScaledRigidFrame3d source, ReadOnlySpan<int> triangles, |
| | | 103 | | in TriangleCircularGeometry chart, in WideDirection3d coreOffset, in WideDirection3d coreAxis, ConvexContactCand |
| | | 104 | | { |
| | | 105 | | // Hull+core has only original-face and edge-cross-core facets. Its |
| | | 106 | | // centered ball plus the cylinder's ball bounds every slab exit below. |
| | 74 | 107 | | Signed576 ball = WideArithmetic.SubtractSigned576(chart.Radius, chart.HalfHeight).Sign <= 0 ? chart.Radius : cha |
| | 148 | 108 | | Span<ulong> direction = stackalloc ulong[3 * Words], values = stackalloc ulong[ConvexContactCandidate.Slots * Wo |
| | 148 | 109 | | Span<int> directionSigns = stackalloc int[3], signs = stackalloc int[ConvexContactCandidate.Slots]; |
| | 74 | 110 | | Span<ulong> work = stackalloc ulong[7 * Words]; |
| | 148 | 111 | | values.Clear(); signs.Clear(); |
| | 1392 | 112 | | for (int index = 0; index < triangles.Length; index += 3) |
| | | 113 | | { |
| | 1950 | 114 | | int a = triangles[index], b = triangles[index + 1], c = triangles[index + 2]; |
| | 650 | 115 | | var geometry = new TriangleCircularGeometry(new FixedTriangle(points[a], points[b], points[c]), source, this |
| | 650 | 116 | | .WithCoreChart(chart); |
| | 6384 | 117 | | for (int feature = 0; feature < 4; feature++) |
| | | 118 | | { |
| | 2570 | 119 | | WideDirection3d normal = feature == 0 ? geometry.FaceNormal : WideDirection3d.Cross(geometry.Edge(featur |
| | 2716 | 120 | | if (normal.IsZero) { if (feature == 0) return false; continue; } |
| | 2425 | 121 | | WideDirection3d point = geometry.Vertex(feature == 0 ? 0 : feature - 1); |
| | 2425 | 122 | | WriteDirection(normal, direction, directionSigns); |
| | 2425 | 123 | | Dot(point, direction, directionSigns, Slot(work, 0), out int planeSign); |
| | 2425 | 124 | | if (planeSign < 0) |
| | 10417 | 125 | | for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[comp |
| | 2425 | 126 | | if (!Supports(geometry, point, direction, directionSigns)) |
| | | 127 | | { |
| | 1082 | 128 | | if (planeSign != 0) { if (feature == 0) return false; continue; } |
| | 803 | 129 | | for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[comp |
| | 139 | 130 | | if (!Supports(geometry, point, direction, directionSigns)) { if (feature == 0) return false; continu |
| | | 131 | | } |
| | 1859 | 132 | | Dot(coreOffset, direction, directionSigns, Slot(work, 6), out _); |
| | 1859 | 133 | | WideArithmetic.AddMagnitudeInto(Slot(work, 6), Slot(work, 0)); |
| | 1859 | 134 | | SumSquares(direction, Slot(work, 1)); |
| | 1859 | 135 | | Import(ball, Slot(work, 2)); |
| | 1859 | 136 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3)); |
| | 1859 | 137 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 4)); |
| | 1859 | 138 | | Import(Signed576.ExtendValue(chart.RawScale), Slot(work, 2)); |
| | 1859 | 139 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3)); |
| | 1859 | 140 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 5)); |
| | 1859 | 141 | | int gap = BuildRadialCandidate(Slot(work, 0), IsZero(Slot(work, 0)) ? 0 : 1, Slot(work, 4), Slot(work, 5 |
| | 1878 | 142 | | if (WideConvexPrismRelations.CompareConvexContactCandidates(new ConvexContactCandidate(values, signs, ga |
| | | 143 | | } |
| | | 144 | | } |
| | 46 | 145 | | return true; |
| | | 146 | | } |
| | | 147 | | |
| | | 148 | | internal bool HasBallMinimumCertificate(in ScaledRigidFrame3d source, ReadOnlySpan<int> triangles, |
| | | 149 | | ConvexContactCandidate best) |
| | | 150 | | { |
| | | 151 | | // Hull contains B(d) and cylinder contains B(min(R,H)); their sum |
| | | 152 | | // bounds every exit below by d+min(R,H), with no rounded square root. |
| | 401 | 153 | | Signed576 ball = WideArithmetic.SubtractSigned576(Radius, HalfHeight).Sign <= 0 ? Radius : HalfHeight; |
| | 802 | 154 | | Span<ulong> direction = stackalloc ulong[3 * Words], values = stackalloc ulong[ConvexContactCandidate.Slots * Wo |
| | 802 | 155 | | Span<int> directionSigns = stackalloc int[3], signs = stackalloc int[ConvexContactCandidate.Slots]; |
| | 401 | 156 | | Span<ulong> work = stackalloc ulong[6 * Words]; |
| | 802 | 157 | | values.Clear(); signs.Clear(); |
| | 3974 | 158 | | for (int index = 0; index < triangles.Length; index += 3) |
| | | 159 | | { |
| | 5754 | 160 | | int a = triangles[index], b = triangles[index + 1], c = triangles[index + 2]; |
| | 1918 | 161 | | var triangle = new FixedTriangle(points[a], points[b], points[c]); |
| | 1918 | 162 | | var geometry = new TriangleCircularGeometry(triangle, source, this, a, b, c); |
| | 1919 | 163 | | if (geometry.FaceNormal.IsZero) return false; |
| | 1917 | 164 | | WriteDirection(geometry.FaceNormal, direction, directionSigns); |
| | 1917 | 165 | | Dot(geometry.A, direction, directionSigns, Slot(work, 0), out int planeSign); |
| | 1917 | 166 | | if (planeSign < 0) |
| | 2409 | 167 | | for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[componen |
| | 1917 | 168 | | if (!Supports(geometry.A, direction, directionSigns)) |
| | | 169 | | { |
| | 157 | 170 | | if (planeSign != 0) return false; |
| | 121 | 171 | | for (int component = 0; component < 3; component++) directionSigns[component] = -directionSigns[componen |
| | 12 | 172 | | if (!Supports(geometry.A, direction, directionSigns)) return false; |
| | | 173 | | } |
| | 1843 | 174 | | SumSquares(direction, Slot(work, 1)); |
| | 1843 | 175 | | Import(ball, Slot(work, 2)); |
| | 1843 | 176 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3)); |
| | 1843 | 177 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 4)); |
| | 1843 | 178 | | Import(Signed576.ExtendValue(RawScale), Slot(work, 2)); |
| | 1843 | 179 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 2), Slot(work, 2), Slot(work, 3)); |
| | 1843 | 180 | | WideArithmetic.MultiplyMagnitudes(Slot(work, 1), Slot(work, 3), Slot(work, 5)); |
| | 1843 | 181 | | int gap = BuildRadialCandidate(Slot(work, 0), planeSign == 0 ? 0 : 1, |
| | 1843 | 182 | | Slot(work, 4), Slot(work, 5), values, signs); |
| | 1843 | 183 | | if (WideConvexPrismRelations.CompareConvexContactCandidates(new ConvexContactCandidate(values, signs, gap), |
| | 257 | 184 | | return false; |
| | | 185 | | } |
| | 69 | 186 | | return true; |
| | | 187 | | } |
| | | 188 | | } |