| | | 1 | | //======================================================================= |
| | | 2 | | // ConePrismBoundaryContacts.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>Streams existing exact cone events on one retained native boundary line.</summary> |
| | | 13 | | internal sealed class ConePrismBoundaryContacts |
| | | 14 | | { |
| | | 15 | | private readonly ConePlaneRayEventVisitor _visit; |
| | 11 | 16 | | private ulong[] _values = Array.Empty<ulong>(); |
| | 11 | 17 | | private readonly int[] _signs = new int[ConePlaneRaySelection.SignCount]; |
| | | 18 | | private bool _hasValue; |
| | | 19 | | private ConePlaneRayFrame _frame; |
| | | 20 | | private ConePlaneRayEventSource _source; |
| | | 21 | | private ConePlaneRayEvent _event; |
| | | 22 | | |
| | 33 | 23 | | internal ConePrismBoundaryContacts() { _visit = Visit; } |
| | | 24 | | |
| | | 25 | | // Side lines are horizontal or have a singleton tangent parameter. |
| | | 26 | | // A cap line identifies its original footprint edge explicitly. |
| | | 27 | | internal bool TryGetContact(in CurvedPrismContactGeometry geometry, int face, |
| | | 28 | | in RationalSegmentInterval clip, Fixed64 lower, Fixed64 upper, |
| | | 29 | | Vector3d center, FixedQuaternion rotation, Vector3d prismOrigin, |
| | | 30 | | out FixedContactAnchors contact, int boundaryEdge = -1, scoped CurvedPrismContactValue native = default, |
| | | 31 | | PrismCurvedBoundaryContactVisitor? visitor = null, int ordinal = 0) |
| | | 32 | | { |
| | 22 | 33 | | contact = default; _hasValue = false; |
| | 11 | 34 | | CurvedPrismContact.GetFacePlane(geometry, face, out WideDirection3d normal, out _, out _); |
| | | 35 | | System.Diagnostics.Debug.Assert(!normal.IsZero); |
| | 11 | 36 | | Span<Signed576> direction = stackalloc Signed576[3] { normal.X, normal.Y, normal.Z }; |
| | 22 | 37 | | WideArithmetic.ReduceCommonScale(direction); normal = new(direction[0], direction[1], direction[2]); |
| | 15 | 38 | | if (face < 0) lower = upper = face == -1 ? -geometry.PrismHalfThickness : geometry.PrismHalfThickness; |
| | 11 | 39 | | CurvedPrismContact.GetNativeBoundarySegment(geometry, face >= 0 ? face : boundaryEdge, clip, lower, upper, |
| | 11 | 40 | | out WideAxis3 a, out WideAxis3 b, out Signed320 coordinateD, out var retained); |
| | 11 | 41 | | var frame = new ConePlaneRayFrame(geometry.HeightValue, geometry.RadiusValue, coordinateD, normal, WideDirection |
| | 11 | 42 | | a = new(a.X, WideArithmetic.SubtractSigned320(frame.Finite.ShapeFrame.Cap, a.Y), a.Z); |
| | 11 | 43 | | b = new(b.X, WideArithmetic.SubtractSigned320(frame.Finite.ShapeFrame.Cap, b.Y), b.Z); |
| | | 44 | | // Same exact original segment/clip owns construction and admission. |
| | | 45 | | // Its complete domain is already on the admitted native surface; |
| | | 46 | | // a plane cohort or second prism-halfplane inventory adds no points. |
| | 11 | 47 | | ConePlaneRayEvents.VisitNativeSegment(frame, a, b, retained, _visit); |
| | 12 | 48 | | if (!_hasValue) return false; |
| | 20 | 49 | | var maximum = Borrow(); var physicalFrame = _frame.WithConePose(center, rotation); |
| | 10 | 50 | | if (!maximum.TryGetRoundedMaximumDepth(out Fixed64 depth) |
| | 12 | 51 | | || !ConePlaneRayPointMaterialization.TryGetPointInFrame(physicalFrame, maximum.Point, maximum.Root, prismOri |
| | | 52 | | // An admitted finite exit has |X|,|Z|<=R and |Y|<=H/2 in this |
| | | 53 | | // centered local chart, so nearest rounding cannot exceed Fixed64. |
| | 8 | 54 | | bool exitRepresented = ConePlaneRayPointMaterialization.TryGetExitConeLocalPoint(physicalFrame, maximum.Point, m |
| | 8 | 55 | | ContactQuadratic.At(maximum.Values, maximum.Signs, 0, _frame.FieldWords), |
| | 8 | 56 | | ContactQuadratic.At(maximum.Values, maximum.Signs, 1, _frame.FieldWords), 1, out Vector3d exitPoint); |
| | | 57 | | System.Diagnostics.Debug.Assert(exitRepresented); |
| | 8 | 58 | | contact = new(new FixedPointAnchor(center, rotation, exitPoint), new FixedPointAnchor(prismOrigin, FixedQuaterni |
| | 8 | 59 | | normal.Transform(geometry.WorldBasis).GetNormal(false), depth, false); |
| | 12 | 60 | | if (visitor != null) visitor(ordinal, contact, native.CompareRootRatio(geometry, |
| | 4 | 61 | | ContactQuadratic.At(maximum.Values, maximum.Signs, 0, _frame.FieldWords), |
| | 4 | 62 | | ContactQuadratic.At(maximum.Values, maximum.Signs, 1, _frame.FieldWords), maximum.Root, |
| | 4 | 63 | | _frame.NormalSquared, WideArithmetic.AddSigned320(_frame.Finite.ShapeFrame.CoordinateDenominator, _frame.Fin |
| | 8 | 64 | | return true; |
| | | 65 | | } |
| | | 66 | | |
| | | 67 | | private ConePlaneRaySelection Borrow() |
| | | 68 | | { |
| | 10 | 69 | | var result = new ConePlaneRaySelection(_values, _signs, _frame.FieldWords, _frame.RootWords); |
| | 20 | 70 | | result.Restore(_frame, _source, _event); return result; |
| | | 71 | | } |
| | | 72 | | |
| | | 73 | | private void Visit(in ConePlaneRayEventSource source, in ConePlaneRayFrame frame, ConePlaneRayEvent descriptor, |
| | | 74 | | scoped ConePlaneRayPoint point, scoped ReadOnlySpan<ulong> root, |
| | | 75 | | scoped in ConePlaneRaySelection positive, scoped in ConePlaneRaySelection negative) |
| | | 76 | | { |
| | 33 | 77 | | if (!positive.HasValue) return; |
| | 35 | 78 | | if (_values.Length < frame.SelectionStorageWords) Array.Resize(ref _values, frame.SelectionStorageWords); |
| | 25 | 79 | | var maximum = new ConePlaneRaySelection(_values, _signs, frame.FieldWords, frame.RootWords); |
| | 40 | 80 | | if (_hasValue) maximum.Restore(_frame, _source, _event); |
| | 35 | 81 | | if (!maximum.KeepEvaluated(positive, frame)) return; |
| | 60 | 82 | | _hasValue = true; _frame = frame; _source = maximum.MaximumSource; _event = maximum.MaximumEvent; |
| | 15 | 83 | | } |
| | | 84 | | } |