| | | 1 | | //======================================================================= |
| | | 2 | | // ConePlaneRayEvents.Circles.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 | | /// <content>Generator-circle and projected upper-rim boundary events.</content> |
| | | 13 | | internal static partial class ConePlaneRayEvents |
| | | 14 | | { |
| | | 15 | | // Circle coefficients <1248 bits; chart sums and doubled linear |
| | | 16 | | // denominators <1250 bits fit this 1280-bit bank. Full generator, |
| | | 17 | | // discriminant and exit fields retain their independently larger banks. |
| | | 18 | | |
| | | 19 | | private static bool AccumulateCircles(in ConePlaneRayEventSource source, in ConePlaneRayFrame frame, |
| | | 20 | | scoped ref ConePlaneRaySelection positive, scoped ref ConePlaneRaySelection negative, |
| | | 21 | | scoped ref ConePlaneRayEventSink sink) |
| | | 22 | | { |
| | 45771 | 23 | | if (!sink.IsStreaming) |
| | 45716 | 24 | | return AccumulateCircleChart(source, frame, sink.Target.Kind == ConePlaneRayEventKind.UpperRim, |
| | 45716 | 25 | | sink.Target.Feature, sink.Target.Quadrant, sink.Target.Branch, sink.Target.Branch, Span<ulong>.Empty, Sp |
| | 55 | 26 | | bool axial = frame.Normal.X.IsZero && frame.Normal.Z.IsZero && !frame.Normal.Y.IsZero; |
| | | 27 | | // A defining line is independent of quadrant. Keep one borrowed bank |
| | | 28 | | // for the current lower/upper cohort; quadrant zero fills every used |
| | | 29 | | // entry before the remaining quadrants read it. Axial plane cohorts have no |
| | | 30 | | // nonseam lines. One allocation outside the loop bounds stack usage. |
| | 55 | 31 | | int lineFields = source.IsSegment ? 6 : axial ? 0 : 48; |
| | 55 | 32 | | Span<ulong> lines = stackalloc ulong[lineFields * frame.CircleWords]; |
| | 55 | 33 | | Span<int> lineSigns = stackalloc int[lineFields]; |
| | 55 | 34 | | bool found = false; |
| | | 35 | | // Match the compact inventories exactly, including seam ownership and |
| | | 36 | | // axial pruning. Each chart reuses its defining line for both roots; |
| | | 37 | | // generator endpoints reuse that same admitted circle parameter. |
| | 268 | 38 | | for (int upper = source.IsSegment ? 1 : 0; upper < 2; upper++) |
| | 790 | 39 | | for (int quadrant = 0; quadrant < 4; quadrant++) |
| | | 40 | | { |
| | 316 | 41 | | if (source.Scope == ConePlaneRayEventScope.Plane) |
| | 1152 | 42 | | for (int endpoint = 0; endpoint < 2; endpoint++) |
| | | 43 | | { |
| | 384 | 44 | | if (endpoint == 0 ? (quadrant & 2) != 0 : (quadrant & 1) != 0) continue; |
| | 192 | 45 | | if (!axial || upper == 0 && frame.Radius != Fixed64.Zero && !frame.PlaneConstant.IsZero) |
| | 160 | 46 | | found |= AccumulateCircleChart(source, frame, upper != 0, 8, quadrant, endpoint, endpoint, lines |
| | | 47 | | } |
| | 316 | 48 | | if (axial && source.Scope == ConePlaneRayEventScope.Plane) continue; |
| | 268 | 49 | | int lineCount = source.IsSegment ? 1 : upper == 0 ? 6 : 8; |
| | 2800 | 50 | | for (int line = 0; line < lineCount; line++) |
| | | 51 | | { |
| | 1132 | 52 | | if (source.Scope == ConePlaneRayEventScope.Plane) |
| | | 53 | | { |
| | | 54 | | // The azimuth line has only cardinal seam roots when one |
| | | 55 | | // radial normal component is zero; seam cohorts own them. |
| | 1008 | 56 | | if (line == (upper == 0 ? 5 : 7) |
| | 1008 | 57 | | && (frame.Normal.X.IsZero || frame.Normal.Z.IsZero)) continue; |
| | | 58 | | // Lower line 0 imposes N.g=0 and can retain a generator |
| | | 59 | | // only when c=0. Radial upper lines 4/5 are respectively |
| | | 60 | | // a positive constant and the zero polynomial. Neither |
| | | 61 | | // yields an isolated root. For c!=0, lower line 1 already |
| | | 62 | | // supplies upper line 3's base points and both exits. For |
| | | 63 | | // Ny=0, line 6 projects by reflection onto that same base |
| | | 64 | | // intersection (line 3 when c=0). Lower/cardinal provenance |
| | | 65 | | // wins those ties. Target replay retains every descriptor. |
| | 896 | 66 | | if (upper == 0 ? line >= 2 && line <= 4 || line == 0 && !frame.PlaneConstant.IsZero |
| | 896 | 67 | | : line < 3 || line == 3 && !frame.PlaneConstant.IsZero |
| | 896 | 68 | | || frame.Normal.Y.IsZero && line >= 4 && line <= 6) continue; |
| | | 69 | | } |
| | 388 | 70 | | found |= AccumulateCircleChart(source, frame, upper != 0, line, quadrant, -1, 1, lines, lineSigns, ref p |
| | | 71 | | } |
| | | 72 | | } |
| | 55 | 73 | | return found; |
| | | 74 | | } |
| | | 75 | | |
| | | 76 | | private static bool AccumulateCircleChart(in ConePlaneRayEventSource source, in ConePlaneRayFrame frame, |
| | | 77 | | bool upper, int line, int quadrant, int firstBranch, int lastBranch, |
| | | 78 | | scoped Span<ulong> lines, scoped Span<int> lineSigns, scoped ref ConePlaneRaySelection positive, scoped ref Cone |
| | | 79 | | scoped ref ConePlaneRayEventSink sink) |
| | | 80 | | { |
| | 46266 | 81 | | if (source.IsSegment && (!upper || line != 0)) return false; |
| | 46262 | 82 | | if (!upper && line == 8 && frame.Normal.X.IsZero && frame.Normal.Z.IsZero && !frame.Normal.Y.IsZero) |
| | 369 | 83 | | return AccumulateAxialCardinal(source, frame, quadrant, firstBranch, ref positive, ref negative, ref sink); |
| | 91786 | 84 | | Span<ulong> values = stackalloc ulong[16 * frame.CircleWords], root = stackalloc ulong[frame.RootWords]; |
| | 45893 | 85 | | Span<int> signs = stackalloc int[16]; |
| | 137679 | 86 | | ContactQuadratic x = ContactQuadratic.At(values, signs, 0, frame.CircleWords), z = ContactQuadratic.At(values, s |
| | 137679 | 87 | | ContactQuadratic a = ContactQuadratic.At(values, signs, 3, frame.CircleWords), b = ContactQuadratic.At(values, s |
| | 91786 | 88 | | ContactQuadratic n = ContactQuadratic.At(values, signs, 6, frame.CircleWords), d = ContactQuadratic.At(values, s |
| | 91786 | 89 | | int sx = (quadrant & 1) == 0 ? 1 : -1, sz = (quadrant & 2) == 0 ? 1 : -1; |
| | 45893 | 90 | | ConePlaneRayEventKind kind = upper ? ConePlaneRayEventKind.UpperRim : ConePlaneRayEventKind.LowerCircle; |
| | 45893 | 91 | | if (line == 8) |
| | | 92 | | { |
| | | 93 | | // The cohort enumerator owns each cardinal seam exactly once. |
| | 20580 | 94 | | root.Clear(); n.Set(Integer(firstBranch)); d.Set(Integer(1)); |
| | 6860 | 95 | | var seam = new ConePlaneRayEvent(kind, line, quadrant, firstBranch, firstBranch); |
| | 6860 | 96 | | return AccumulateCircleParameter(source, frame, upper, sx, sz, n, d, root, true, |
| | 6860 | 97 | | seam, default, default, default, 0, ref positive, ref negative, ref sink); |
| | | 98 | | } |
| | 39033 | 99 | | if (sink.IsStreaming) |
| | | 100 | | { |
| | 388 | 101 | | ContactQuadratic lx = ContactQuadratic.At(lines, lineSigns, 3 * line, frame.CircleWords); |
| | 388 | 102 | | ContactQuadratic lz = ContactQuadratic.At(lines, lineSigns, 3 * line + 1, frame.CircleWords); |
| | 388 | 103 | | ContactQuadratic lc = ContactQuadratic.At(lines, lineSigns, 3 * line + 2, frame.CircleWords); |
| | 485 | 104 | | if (quadrant == 0) GetCircleLine(source, frame, upper, line, lx, lz, lc, ref sink); |
| | 1164 | 105 | | lx.CopyTo(x); lz.CopyTo(z); lc.CopyTo(constant); |
| | | 106 | | } |
| | 38645 | 107 | | else GetCircleLine(source, frame, upper, line, x, z, constant, ref sink); |
| | 195165 | 108 | | constant.CopyTo(a); a.Add(x, -sx); z.CopyTo(b, sz); constant.CopyTo(c); c.Add(x, sx); |
| | 78066 | 109 | | bool found = false, conjugate = false; |
| | 156908 | 110 | | for (int branch = firstBranch; branch <= lastBranch; branch += 2) |
| | | 111 | | { |
| | | 112 | | // A successful negative quadratic branch shares its rational |
| | | 113 | | // numerator, positive denominator and discriminant with the next |
| | | 114 | | // branch. Only the radical sign changes; linear/repeated cases |
| | | 115 | | // still use the shared chart owner's positive-branch handling. |
| | | 116 | | bool parameter; |
| | 39829 | 117 | | if (conjugate) { n.Signs[1] = 1; parameter = true; } |
| | | 118 | | else |
| | | 119 | | { |
| | 39217 | 120 | | parameter = ContactQuadratic.TryGetParameter(a, b, c, branch, root, n, d); |
| | 39217 | 121 | | conjugate = parameter && branch == -1; |
| | | 122 | | } |
| | | 123 | | // Cardinal cohorts own the endpoints; test the open chart once. |
| | 39421 | 124 | | if (!parameter || !ConePlaneRayCharts.IsUnitParameter(n, d, root, includeEndpoints: false)) continue; |
| | 3297 | 125 | | var descriptor = new ConePlaneRayEvent(kind, line, quadrant, branch); |
| | 3297 | 126 | | found |= AccumulateCircleParameter(source, frame, upper, sx, sz, n, d, root, line == 0, |
| | 3297 | 127 | | descriptor, x, z, constant, branch == -1 || branch == 1 ? a.Signs[0] * branch * sx * sz : 0, |
| | 3297 | 128 | | ref positive, ref negative, ref sink); |
| | | 129 | | } |
| | 39033 | 130 | | return found; |
| | | 131 | | } |
| | | 132 | | |
| | | 133 | | private static bool AccumulateAxialCardinal(in ConePlaneRayEventSource source, in ConePlaneRayFrame frame, |
| | | 134 | | int quadrant, int branch, scoped ref ConePlaneRaySelection positive, scoped ref ConePlaneRaySelection negative, |
| | | 135 | | scoped ref ConePlaneRayEventSink sink) |
| | | 136 | | { |
| | 369 | 137 | | Signed832 sectionHeight = frame.Normal.Y.Sign < 0 |
| | 369 | 138 | | ? WideArithmetic.SubtractSigned832(default, frame.PlaneConstant) : frame.PlaneConstant; |
| | 369 | 139 | | if (sectionHeight.Sign < 0 || WideArithmetic.SubtractSigned832(sectionHeight, |
| | 369 | 140 | | Signed832.ExtendValue(Extend(frame.Finite.FullHeight))).Sign > 0) |
| | 82 | 141 | | return false; |
| | 287 | 142 | | Span<ulong> pointValues = stackalloc ulong[frame.PointStorageWords]; |
| | 287 | 143 | | Span<int> pointSigns = stackalloc int[ConePlaneRayPoint.SignCount]; |
| | 574 | 144 | | var point = new ConePlaneRayPoint(pointValues, pointSigns, frame.FieldWords); point.Set(default(WideAxis3)); |
| | 574 | 145 | | Span<ulong> values = stackalloc ulong[2 * frame.FieldWords]; Span<int> signs = stackalloc int[2]; |
| | 287 | 146 | | ContactQuadratic height = At(values, signs, 0); |
| | | 147 | | // A primitive axial normal has Ny=+/-1, hence s=c*sign(Ny). |
| | | 148 | | // With positive homogeneous denominator Hraw, a cardinal generator |
| | | 149 | | // section is (X,Y,Z)=(+/-s*Rraw,s*Hraw,0), or its Z counterpart. |
| | | 150 | | // s<198 bits and raw dimensions<63 keep products below261 bits. |
| | | 151 | | // Plane scope and the primitive axial normal are established by the |
| | | 152 | | // chart dispatcher. These coordinates satisfy the plane and zero cone |
| | | 153 | | // polynomial exactly; the closed s range proves remaining admission. |
| | | 154 | | // Radius zero or s=0 remains an axis point for the shared exit owner. |
| | 287 | 155 | | height.Set(sectionHeight); |
| | 287 | 156 | | Signed576 rawHeight = Extend(Signed320.ExtendValue(Signed192.Raw(frame.Height))); |
| | 287 | 157 | | ContactQuadratic.Scale(height, rawHeight, point.Y); |
| | 287 | 158 | | ContactQuadratic radial = branch == 0 ? point.X : point.Z; |
| | 287 | 159 | | ContactQuadratic.Scale(height, Extend(Signed320.ExtendValue(Signed192.Raw(frame.Radius))), radial); |
| | 287 | 160 | | radial.MultiplySign((quadrant & (branch == 0 ? 1 : 2)) == 0 ? 1 : -1); |
| | 287 | 161 | | point.Denominator.Set(rawHeight); |
| | 287 | 162 | | var descriptor = new ConePlaneRayEvent(ConePlaneRayEventKind.LowerCircle, 8, quadrant, branch, branch); |
| | 287 | 163 | | SetEvent(source, descriptor, ref positive, ref negative); |
| | 287 | 164 | | if (!sink.PointOnly) |
| | | 165 | | { |
| | 265 | 166 | | if (sink.RequestedOrientation >= 0) |
| | | 167 | | { |
| | 243 | 168 | | bool found = ConePlaneRayPointExits.TryAccumulateAxialOrientation(point, frame, |
| | 243 | 169 | | ReadOnlySpan<ulong>.Empty, 1, sidePoint: true, ref positive); |
| | | 170 | | System.Diagnostics.Debug.Assert(found); |
| | | 171 | | } |
| | 265 | 172 | | if (sink.RequestedOrientation <= 0) |
| | | 173 | | { |
| | 243 | 174 | | bool found = ConePlaneRayPointExits.TryAccumulateAxialOrientation(point, frame, |
| | 243 | 175 | | ReadOnlySpan<ulong>.Empty, -1, sidePoint: true, ref negative); |
| | | 176 | | System.Diagnostics.Debug.Assert(found); |
| | | 177 | | } |
| | | 178 | | } |
| | 287 | 179 | | sink.Keep(descriptor, point, ReadOnlySpan<ulong>.Empty, positive, negative); |
| | 287 | 180 | | return true; |
| | | 181 | | } |
| | | 182 | | |
| | | 183 | | // Returns x*cos(theta)+z*sin(theta)+constant. Scaling the symbolic line |
| | | 184 | | // before chart substitution keeps its defining coefficients below1248bits. |
| | | 185 | | private static void GetCircleLine(in ConePlaneRayEventSource source, in ConePlaneRayFrame frame, |
| | | 186 | | bool upper, int line, scoped ContactQuadratic x, scoped ContactQuadratic z, scoped ContactQuadratic constant, sc |
| | | 187 | | { |
| | 38742 | 188 | | Span<ulong> values = stackalloc ulong[14 * frame.CircleWords]; |
| | 38742 | 189 | | Span<int> signs = stackalloc int[14]; |
| | 116226 | 190 | | ContactQuadratic lx = ContactQuadratic.At(values, signs, 0, frame.CircleWords), ly = ContactQuadratic.At(values, |
| | 77484 | 191 | | ContactQuadratic offset = ContactQuadratic.At(values, signs, 3, frame.CircleWords), A = ContactQuadratic.At(valu |
| | 77484 | 192 | | ContactQuadratic term = ContactQuadratic.At(values, signs, 5, frame.CircleWords), scalar = ContactQuadratic.At(v |
| | 154968 | 193 | | lx.Clear(); ly.Clear(); lz.Clear(); offset.Clear(); |
| | 38742 | 194 | | if (source.IsSegment) |
| | | 195 | | { |
| | | 196 | | Signed576 wx, wy, wz; Signed832 w; |
| | 9207 | 197 | | if (sink.NativeSegment.IsEmpty) |
| | 9195 | 198 | | frame.GetEdgeLine(new FixedSegment(source.A, source.B), out wx, out wy, out wz, out w); |
| | | 199 | | else |
| | | 200 | | { |
| | 12 | 201 | | sink.GetSegment(source, frame, out WideAxis3 a, out WideAxis3 b); |
| | 12 | 202 | | WideDirection3d edge = CurvedPrismContact.Difference(new WideDirection3d(b), new WideDirection3d(a)); |
| | 12 | 203 | | WideDirection3d wall = WideDirection3d.Cross(frame.Normal, edge); |
| | 36 | 204 | | wx = wall.X; wy = wall.Y; wz = wall.Z; |
| | 12 | 205 | | w = WideDirection3d.Dot(wall, a); |
| | | 206 | | } |
| | 27621 | 207 | | lx.Set(wx); ly.Set(wy); lz.Set(wz); |
| | 9207 | 208 | | offset.Set(WideArithmetic.SubtractSigned832(default, w)); |
| | | 209 | | } |
| | 29535 | 210 | | else if (line == (upper ? 7 : 5)) |
| | | 211 | | { |
| | 13128 | 212 | | lx.Set(Extend(frame.Normal.Z)); lz.Set(Extend(WideArithmetic.SubtractSigned576(default, frame.Normal.X))); |
| | | 213 | | } |
| | 22971 | 214 | | else if (upper && (line == 4 || line == 5)) |
| | | 215 | | { |
| | 6432 | 216 | | scalar.Set(Extend(frame.Normal.Y)); |
| | 12864 | 217 | | ContactQuadratic.Scale(scalar, Extend(frame.Normal.X), lx); lx.MultiplySign(-1); |
| | 12864 | 218 | | ContactQuadratic.Scale(scalar, Extend(frame.Normal.Y), ly); ly.MultiplySign(-1); |
| | 12864 | 219 | | ContactQuadratic.Scale(scalar, Extend(frame.Normal.Z), lz); lz.MultiplySign(-1); |
| | 6432 | 220 | | ContactQuadratic.Scale(scalar, frame.PlaneConstant, offset); |
| | 6432 | 221 | | if (line == 4) |
| | | 222 | | { |
| | 3212 | 223 | | scalar.Set(frame.NormalSquared); |
| | 6424 | 224 | | ContactQuadratic.Scale(scalar, Extend(frame.Finite.FullHeight), term); offset.Add(term); |
| | | 225 | | } |
| | | 226 | | } |
| | 16539 | 227 | | else if (upper && line == 6 && frame.Normal.Y.IsZero) |
| | | 228 | | { |
| | | 229 | | // For a radial plane A=H²*|N|². Cancel that strictly positive |
| | | 230 | | // common factor from the line before chart substitution; its |
| | | 231 | | // roots, branch labels and projected witnesses are unchanged. |
| | 101 | 232 | | lx.Set(Extend(WideArithmetic.SubtractSigned576(default, frame.Normal.X))); |
| | 101 | 233 | | lz.Set(Extend(WideArithmetic.SubtractSigned576(default, frame.Normal.Z))); |
| | 101 | 234 | | offset.Set(WideArithmetic.SubtractSigned832(default, frame.PlaneConstant)); |
| | | 235 | | } |
| | 16438 | 236 | | else if (upper && line == 6) |
| | | 237 | | { |
| | 2552 | 238 | | Product(frame.Normal, frame.Normal, frame, A); |
| | 2552 | 239 | | Signed576 h2 = Extend(WideArithmetic.MultiplySigned192(Signed192.Raw(frame.Height), Signed192.Raw(frame.Heig |
| | 2552 | 240 | | Signed576 r2 = Extend(WideArithmetic.MultiplySigned192(Signed192.Raw(frame.Radius), Signed192.Raw(frame.Radi |
| | 2552 | 241 | | scalar.Set(frame.NormalSquared); |
| | 5104 | 242 | | ContactQuadratic.Scale(scalar, h2, term); term.Add(term); |
| | 7656 | 243 | | A.CopyTo(lx); lx.Add(term, -1); lx.CopyTo(lz); |
| | 5104 | 244 | | ContactQuadratic.Scale(scalar, r2, term); term.Add(term); |
| | 5104 | 245 | | A.CopyTo(ly); ly.Add(term); |
| | 5104 | 246 | | ContactQuadratic.Scale(lx, Extend(frame.Normal.X), term); term.CopyTo(lx); |
| | 5104 | 247 | | ContactQuadratic.Scale(ly, Extend(frame.Normal.Y), term); term.CopyTo(ly); |
| | 5104 | 248 | | ContactQuadratic.Scale(lz, Extend(frame.Normal.Z), term); term.CopyTo(lz); |
| | 5104 | 249 | | ContactQuadratic.Scale(A, frame.PlaneConstant, offset); offset.MultiplySign(-1); |
| | | 250 | | } |
| | | 251 | | else |
| | | 252 | | { |
| | 41658 | 253 | | lx.Set(Extend(frame.Normal.X)); ly.Set(Extend(frame.Normal.Y)); lz.Set(Extend(frame.Normal.Z)); |
| | 13886 | 254 | | if (upper || line == 1) |
| | 3864 | 255 | | offset.Set(WideArithmetic.SubtractSigned832(default, frame.PlaneConstant)); |
| | | 256 | | } |
| | 38742 | 257 | | Signed320 radius = Signed320.NarrowValue(WideArithmetic.MultiplySigned320( |
| | 38742 | 258 | | frame.Finite.ShapeFrame.CoordinateDenominator, frame.Finite.ShapeFrame.Radius)); |
| | 38742 | 259 | | ContactQuadratic.Scale(lx, Extend(radius), x); |
| | 38742 | 260 | | ContactQuadratic.Scale(lz, Extend(radius), z); |
| | 77484 | 261 | | ContactQuadratic.Scale(ly, Extend(frame.Finite.FullHeight), constant); constant.Add(offset); |
| | 38742 | 262 | | } |
| | | 263 | | |
| | | 264 | | private static bool AccumulateCircleParameter(in ConePlaneRayEventSource source, in ConePlaneRayFrame frame, |
| | | 265 | | bool upper, int sx, int sz, scoped ContactQuadratic n, scoped ContactQuadratic d, scoped ReadOnlySpan<ulong> roo |
| | | 266 | | ConePlaneRayEvent descriptor, scoped ContactQuadratic lineX, scoped ContactQuadratic lineZ, |
| | | 267 | | scoped ContactQuadratic lineConstant, int circleRootSign, |
| | | 268 | | scoped ref ConePlaneRaySelection positive, scoped ref ConePlaneRaySelection negative, scoped ref ConePlaneRayEve |
| | | 269 | | { |
| | 10157 | 270 | | Span<ulong> values = stackalloc ulong[20 * frame.FieldWords]; |
| | 10157 | 271 | | Span<int> signs = stackalloc int[20]; |
| | 10157 | 272 | | Span<ulong> pointValues = stackalloc ulong[frame.PointStorageWords]; |
| | 10157 | 273 | | Span<int> pointSigns = stackalloc int[8]; |
| | 10157 | 274 | | var point = new ConePlaneRayPoint(pointValues, pointSigns, frame.FieldWords); |
| | 30471 | 275 | | ContactQuadratic nn = At(values, signs, 0), dd = At(values, signs, 1), gx = At(values, signs, 2); |
| | 30471 | 276 | | ContactQuadratic gy = At(values, signs, 3), gz = At(values, signs, 4), gd = At(values, signs, 5); |
| | 20314 | 277 | | ContactQuadratic dot = At(values, signs, 6), term = At(values, signs, 7); |
| | 20314 | 278 | | ContactQuadratic t = At(values, signs, 8), td = At(values, signs, 9); |
| | 10157 | 279 | | Signed320 radius = Signed320.NarrowValue(WideArithmetic.MultiplySigned320( |
| | 10157 | 280 | | frame.Finite.ShapeFrame.CoordinateDenominator, frame.Finite.ShapeFrame.Radius)); |
| | 10157 | 281 | | if (circleRootSign != 0) |
| | | 282 | | { |
| | | 283 | | // x*cos+z*sin+c=0 gives (cos,sin)=(-c*x+e*z*sqrt(D), |
| | | 284 | | // -c*z-e*x*sqrt(D))/(x*x+z*z), D=x*x+z*z-c*c. |
| | | 285 | | // The existing quadrant root has this same D and e=sign(a)* |
| | | 286 | | // branch*sx*sz. Linear charts retain their rational parameter |
| | | 287 | | // because their root bank is zero. Defining-line coefficients <1118 |
| | | 288 | | // bits keep projected homogeneous fields <2960 bits in the existing bank. |
| | 3223 | 289 | | ContactQuadratic.Multiply(lineX, lineX, root, gd); |
| | 6446 | 290 | | ContactQuadratic.Multiply(lineZ, lineZ, root, term); gd.Add(term); |
| | 6446 | 291 | | ContactQuadratic.Multiply(lineConstant, lineX, root, gx); gx.MultiplySign(-1); |
| | 6446 | 292 | | ContactQuadratic.MultiplyRoot(lineZ, root, term); gx.Add(term, circleRootSign); |
| | 6446 | 293 | | ContactQuadratic.Multiply(lineConstant, lineZ, root, gz); gz.MultiplySign(-1); |
| | 6446 | 294 | | ContactQuadratic.MultiplyRoot(lineX, root, term); gz.Add(term, -circleRootSign); |
| | | 295 | | } |
| | | 296 | | else |
| | | 297 | | { |
| | 13868 | 298 | | ContactQuadratic.Multiply(n, n, root, nn); ContactQuadratic.Multiply(d, d, root, dd); |
| | 13868 | 299 | | dd.CopyTo(gd); gd.Add(nn); |
| | 20802 | 300 | | dd.CopyTo(gx); gx.Add(nn, -1); gx.MultiplySign(sx); |
| | 20802 | 301 | | ContactQuadratic.Multiply(n, d, root, gz); gz.Add(gz); gz.MultiplySign(sz); |
| | | 302 | | } |
| | 20314 | 303 | | ContactQuadratic.Scale(gx, Extend(radius), term); term.CopyTo(gx); |
| | 10157 | 304 | | ContactQuadratic.Scale(gd, Extend(frame.Finite.FullHeight), gy); |
| | 20314 | 305 | | ContactQuadratic.Scale(gz, Extend(radius), term); term.CopyTo(gz); |
| | 10157 | 306 | | ContactQuadratic.Scale(gx, Extend(frame.Normal.X), dot); |
| | 20314 | 307 | | ContactQuadratic.Scale(gy, Extend(frame.Normal.Y), term); dot.Add(term); |
| | 20314 | 308 | | ContactQuadratic.Scale(gz, Extend(frame.Normal.Z), term); dot.Add(term); |
| | 10157 | 309 | | if (!upper) |
| | | 310 | | { |
| | 6847 | 311 | | int divisorSign = dot.Sign(root); |
| | 6847 | 312 | | if (divisorSign == 0) |
| | 997 | 313 | | return retainGenerator && frame.PlaneConstant.IsZero && AccumulateGenerator(source, frame, gx, gy, gz, g |
| | 997 | 314 | | descriptor, ref positive, ref negative, ref sink); |
| | 8970 | 315 | | if (!sink.Wants(descriptor)) return false; |
| | 5460 | 316 | | ContactQuadratic.Scale(gx, frame.PlaneConstant, point.X); point.X.MultiplySign(divisorSign); |
| | 5460 | 317 | | ContactQuadratic.Scale(gy, frame.PlaneConstant, point.Y); point.Y.MultiplySign(divisorSign); |
| | 5460 | 318 | | ContactQuadratic.Scale(gz, frame.PlaneConstant, point.Z); point.Z.MultiplySign(divisorSign); |
| | 2730 | 319 | | dot.CopyTo(point.Denominator, divisorSign); |
| | 2730 | 320 | | SetEvent(source, descriptor, ref positive, ref negative); |
| | 2730 | 321 | | bool admitted = ConePlaneRayPointExits.AccumulateSidePoint(source, frame, point, root, ref positive, ref neg |
| | 4873 | 322 | | if (admitted) sink.Keep(descriptor, point, root, positive, negative); |
| | 2730 | 323 | | return admitted; |
| | | 324 | | } |
| | 9930 | 325 | | dot.CopyTo(t); ContactQuadratic.Scale(gd, frame.PlaneConstant, term); t.Add(term, -1); |
| | 3310 | 326 | | ContactQuadratic.Scale(gd, frame.NormalSquared, td); |
| | 3310 | 327 | | ContactQuadratic.Scale(gx, frame.NormalSquared, point.X); |
| | 6620 | 328 | | ContactQuadratic.Scale(t, Extend(frame.Normal.X), term); point.X.Add(term, -1); |
| | 3310 | 329 | | ContactQuadratic.Scale(gy, frame.NormalSquared, point.Y); |
| | 6620 | 330 | | ContactQuadratic.Scale(t, Extend(frame.Normal.Y), term); point.Y.Add(term, -1); |
| | 3310 | 331 | | ContactQuadratic.Scale(gz, frame.NormalSquared, point.Z); |
| | 6620 | 332 | | ContactQuadratic.Scale(t, Extend(frame.Normal.Z), term); point.Z.Add(term, -1); |
| | 3310 | 333 | | td.CopyTo(point.Denominator); |
| | 3310 | 334 | | int orientation = t.Sign(root); |
| | 3310 | 335 | | descriptor = new ConePlaneRayEvent(descriptor.Kind, descriptor.Feature, descriptor.Quadrant, |
| | 3310 | 336 | | descriptor.Branch, descriptor.Endpoint, orientation); |
| | 3310 | 337 | | SetEvent(source, descriptor, ref positive, ref negative); |
| | 3310 | 338 | | if (orientation == 0) |
| | | 339 | | { |
| | 527 | 340 | | bool sideAdmitted = ConePlaneRayPointExits.AccumulateSidePoint(source, frame, point, root, ref positive, ref |
| | 1052 | 341 | | if (sideAdmitted) sink.Keep(descriptor, point, root, positive, negative); |
| | 527 | 342 | | return sideAdmitted; |
| | | 343 | | } |
| | 2783 | 344 | | t.MultiplySign(orientation); |
| | | 345 | | // For projected rim line 6, F(P)=T*(A*T-2*|N|²*B(G)). A valid |
| | | 346 | | // chart root makes the second factor zero. Other projections and |
| | | 347 | | // malformed targeted branches retain full solid admission. |
| | 2783 | 348 | | bool sideCertificate = source.Scope == ConePlaneRayEventScope.Plane && descriptor.Feature == 6 |
| | 2783 | 349 | | && (descriptor.Branch == -1 || descriptor.Branch == 1); |
| | 2783 | 350 | | bool certified = ConePlaneRayPointExits.AccumulateCertifiedExit(source, frame, point, root, t, td, |
| | 2783 | 351 | | orientation, ref positive, ref negative, sink.PointOnly, sideCertificate); |
| | 4080 | 352 | | if (certified) sink.Keep(descriptor, point, root, positive, negative); |
| | 2783 | 353 | | return certified; |
| | | 354 | | } |
| | | 355 | | |
| | | 356 | | private static bool AccumulateGenerator(in ConePlaneRayEventSource source, in ConePlaneRayFrame frame, |
| | | 357 | | scoped ContactQuadratic gx, scoped ContactQuadratic gy, scoped ContactQuadratic gz, scoped ContactQuadratic gd, |
| | | 358 | | scoped ReadOnlySpan<ulong> root, ConePlaneRayEvent generator, |
| | | 359 | | scoped ref ConePlaneRaySelection positive, scoped ref ConePlaneRaySelection negative, |
| | | 360 | | scoped ref ConePlaneRayEventSink sink) |
| | | 361 | | { |
| | 278 | 362 | | if (!sink.Wants(ConePlaneRayEventKind.Generator) || !sink.IsStreaming && (uint)sink.Target.Endpoint > 1) return |
| | 154 | 363 | | Span<ulong> pointValues = stackalloc ulong[frame.PointStorageWords]; |
| | 154 | 364 | | Span<int> pointSigns = stackalloc int[ConePlaneRayPoint.SignCount]; |
| | 154 | 365 | | var point = new ConePlaneRayPoint(pointValues, pointSigns, frame.FieldWords); |
| | | 366 | | // Only the cone's generator endpoints are intrinsic. Its intersection |
| | | 367 | | // with a finite boundary is already that segment's side root/endpoint. |
| | 644 | 368 | | for (int endpoint = sink.IsStreaming ? 0 : sink.Target.Endpoint; endpoint <= (sink.IsStreaming ? 1 : sink.Target |
| | | 369 | | { |
| | 252 | 370 | | if (endpoint == 0) point.Set(default(WideAxis3)); |
| | 336 | 371 | | else { gx.CopyTo(point.X); gy.CopyTo(point.Y); gz.CopyTo(point.Z); gd.CopyTo(point.Denominator); } |
| | 168 | 372 | | var descriptor = new ConePlaneRayEvent(ConePlaneRayEventKind.Generator, generator.Feature, |
| | 168 | 373 | | generator.Quadrant, generator.Branch, endpoint); |
| | 168 | 374 | | SetEvent(source, descriptor, ref positive, ref negative); |
| | 168 | 375 | | bool admitted = ConePlaneRayPointExits.AccumulateSidePoint(source, frame, point, root, ref positive, ref neg |
| | | 376 | | // D=N.g=0 and c=0 already certify this intrinsic generator's plane. |
| | | 377 | | // Its apex has denominator 1; its base-rim endpoint has denominator |
| | | 378 | | // d*d+n*n>0 and satisfies the finite cone exactly, including R=0. |
| | | 379 | | System.Diagnostics.Debug.Assert(admitted); |
| | 168 | 380 | | sink.Keep(descriptor, point, root, positive, negative); |
| | | 381 | | } |
| | 154 | 382 | | return true; |
| | | 383 | | } |
| | | 384 | | } |