| | | 1 | | //======================================================================= |
| | | 2 | | // ConePlaneRayCharts.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>Borrowed quadratic parameters for finite cone plane-section event charts.</summary> |
| | | 13 | | internal static class ConePlaneRayCharts |
| | | 14 | | { |
| | | 15 | | internal const int Words = 64; |
| | | 16 | | internal const int RootWords = 40; |
| | | 17 | | |
| | | 18 | | // Apex coordinates use F(p)=H²(px²+pz²)-R²py² and plane N.p=c. |
| | | 19 | | // For one normal orientation, F(p+tN)=A*t²+2B(p)*t+C(p). |
| | | 20 | | // |
| | | 21 | | // Candidate completeness is a boundary-stratum argument. A maximum |
| | | 22 | | // strictly inside the admitted section projects a global support face. |
| | | 23 | | // Otherwise p is on a triangle edge, the lower cone side, or lower base. |
| | | 24 | | // A switch between upper side and upper base is on the projected upper |
| | | 25 | | // rim; every remaining boundary is an endpoint of one of these strata. |
| | | 26 | | // |
| | | 27 | | // On the LOWER side C(p)=0. Its nonzero side root is -2B(p)/A; |
| | | 28 | | // admission of that root additionally requires B(p)<=0 (the derivative |
| | | 29 | | // at the upper root is -2B(p)). Choosing the other crossing would leave |
| | | 30 | | // the finite cone immediately and later enter the opposite nappe. |
| | | 31 | | // With g=(Rcos(theta),H,Rsin(theta)), D=N.g and p=c*g/D, |
| | | 32 | | // E=N^T M g=H²D-H(H²+R²)Ny. The side depth is -2cE/(AD). |
| | | 33 | | // Its derivative vanishes at D'=0 or is constant. Triangle, axial, |
| | | 34 | | // and switch boundaries are lines on this generator circle. c=0 and |
| | | 35 | | // D=0 retain finite generator intervals rather than dividing by zero. |
| | | 36 | | // |
| | | 37 | | // On the UPPER rim q=g, t=(N.q-c)/|N|² and p=q-tN. Triangle walls W |
| | | 38 | | // have W.N=0, so W.p=W.q. Lower-cone admission factors as |
| | | 39 | | // F(p)=t*(A*t-2B(q)); t>0 makes the remaining circle predicate linear. |
| | | 40 | | // t=0 is admitted separately before that division. Interior stationary |
| | | 41 | | // rim values are exactly global rim supports; clipping endpoints and |
| | | 42 | | // rim/apex tangencies supply the remaining extrema. |
| | | 43 | | // |
| | | 44 | | // The four signed quadrant charts use u in [0,1]: |
| | | 45 | | // cos=sx*(1-u²)/(1+u²), sin=sz*2u/(1+u²). |
| | | 46 | | // Every admitted circle line is therefore a quadratic in u. On a |
| | | 47 | | // clipped straight edge G(s,t)=a*s²+2(b+m*t)*s+c+2n*t+A*t²; |
| | | 48 | | // eliminating stationary s gives a*(c+2n*t+A*t²)-(b+m*t)²=0. |
| | | 49 | | // Axial endpoints are rational. Lower-side edge roots need only the |
| | | 50 | | // original quadratic followed by t=-2B/A. No nested radical is needed. |
| | | 51 | | // |
| | | 52 | | // Full-domain ledger for the shared unreduced exact rigid frame: |
| | | 53 | | // point/edge/apex differences <198 bits, N <260, |N|² <522, c <460; |
| | | 54 | | // raw H/R squares <126, scaled base H/R <193. Authored normal-cross- |
| | | 55 | | // edge walls transformed without narrowing are <326; wall offsets |
| | | 56 | | // <526. Lower-circle triangle lines <990 and projected-rim lower-cone |
| | | 57 | | // lines <1118. Edge stationary polynomial coefficients <1184. Thus |
| | | 58 | | // face-stratum defining coefficients <1248 and discriminants <2498 bits. |
| | | 59 | | // Rational axis/generator point rays additionally have A<1302, B<1241, |
| | | 60 | | // C<1181; their discriminants remain <2485 bits. |
| | | 61 | | // Forty words retain the complete discriminant; sixty-four words retain |
| | | 62 | | // the unreduced evaluated depth numerator/denominator (<4096 bits). |
| | | 63 | | // Admission products and depth comparisons use larger transient storage; |
| | | 64 | | // these bounds do not authorize materializing those products in a field. |
| | | 65 | | |
| | | 66 | | // Affine near-field ingress retains scaled local coordinates below 110 |
| | | 67 | | // bits. Exact relative coordinates are <306, normals <351, normal² <704, |
| | | 68 | | // plane constants <659, wall coefficients <463 and wall offsets <771. |
| | | 69 | | // Independently retained cross-frame clips can use 832-bit parameters |
| | | 70 | | // instead of rigid 319-bit parameters. Every independent input bound is |
| | | 71 | | // therefore below three times the rigid ledger above; these same fixed- |
| | | 72 | | // degree polynomials fit three times its retained/transient capacities. |
| | | 73 | | // Only affine frames select those banks. |
| | | 74 | | |
| | | 75 | | /// <summary>Tests a positive-denominator parameter against the closed or open unit interval.</summary> |
| | | 76 | | internal static bool IsUnitParameter(ContactQuadratic numerator, ContactQuadratic denominator, |
| | | 77 | | ReadOnlySpan<ulong> root, bool includeEndpoints = true) |
| | | 78 | | { |
| | 18429 | 79 | | int sign = numerator.Sign(root); |
| | 18429 | 80 | | if (includeEndpoints ? sign < 0 : sign <= 0) |
| | 8659 | 81 | | return false; |
| | 9770 | 82 | | int words = numerator.FieldWords; |
| | 9770 | 83 | | Span<ulong> work = stackalloc ulong[2 * words]; |
| | 9770 | 84 | | Span<int> signs = stackalloc int[2]; |
| | 9770 | 85 | | ContactQuadratic difference = ContactQuadratic.At(work, signs, 0, words); |
| | 19540 | 86 | | numerator.CopyTo(difference); difference.Add(denominator, -1); |
| | 9770 | 87 | | sign = difference.Sign(root); |
| | 9770 | 88 | | return includeEndpoints ? sign <= 0 : sign < 0; |
| | | 89 | | } |
| | | 90 | | } |