axiolid_evaluate/station/frame_bound/
error.rs1use axiolid_core::Scalar;
5use axiolid_curve::SeamSide;
6
7use super::{FrameLaw, FrameReading, LawBasis, Slot};
8use crate::arc_parameter::ARC_LENGTH_TOLERANCE;
9use crate::bound::ROUNDING;
10use crate::station::certify::{AXIS_ROUNDING, POINT_ROUNDING};
11use crate::station::offset::OffsetTable;
12use crate::station::{admitted, StationCurve, StationPiece};
13
14#[non_exhaustive]
17#[derive(Debug, Clone, Copy, PartialEq)]
18pub struct StationFrameError {
19 pub point: Scalar,
22 pub axis: Scalar,
25}
26
27fn slack(distance: Scalar) -> Scalar {
29 ARC_LENGTH_TOLERANCE * distance.abs().max(1.0)
30}
31
32#[derive(Clone, Copy)]
35struct Stand {
36 at: Scalar,
37 into: Scalar,
38 side: SeamSide,
39 reading: FrameReading,
40}
41
42fn data_scale(piece: &StationPiece<'_>) -> Scalar {
48 use axiolid_curve::{Curve2, Curve3};
49 fn plan(curve: &Curve2) -> Scalar {
50 match curve {
51 Curve2::Line(l) => l.origin.abs().max_element() + l.direction.abs().max_element(),
52 Curve2::Circle(c) => c.frame.origin.abs().max_element() + c.radius.abs(),
53 Curve2::Polyline(p) => p
54 .points
55 .iter()
56 .map(|q| q.abs().max_element())
57 .fold(0.0, Scalar::max),
58 _ => Scalar::INFINITY,
59 }
60 }
61 let curve = match piece.curve {
62 StationCurve::Two(c) => plan(c),
63 StationCurve::Three(c) => match c {
64 Curve3::Line(l) => l.origin.abs().max_element() + l.direction.abs().max_element(),
65 Curve3::Circle(c) => c.frame.origin.abs().max_element() + c.radius.abs(),
66 Curve3::Polyline(p) => p
67 .points
68 .iter()
69 .map(|q| q.abs().max_element())
70 .fold(0.0, Scalar::max),
71 Curve3::Elevated(e) => plan(&e.plan),
72 Curve3::Banked(b) => plan(&b.base.plan),
73 _ => Scalar::INFINITY,
74 },
75 StationCurve::Offset(_) => Scalar::INFINITY,
76 };
77 let moved = piece
78 .placement
79 .map_or(0.0, |rigid| rigid.translation.abs().max_element());
80 curve.max(moved)
81}
82
83impl<'c> FrameLaw<'_, 'c> {
84 #[must_use]
89 pub fn error_at(
90 &self,
91 distance: Scalar,
92 side: SeamSide,
93 reading: FrameReading,
94 ) -> Option<StationFrameError> {
95 match &self.basis {
96 LawBasis::Atomic {
97 curve,
98 placement,
99 placement_exact,
100 whole,
101 } => {
102 let owned;
103 let (piece, length) = match whole {
104 Some(whole) => (whole, Some(whole.length())),
105 None => {
106 let (mut piece, length) = match StationPiece::whole(*curve) {
109 Ok(whole) => (whole, Some(whole.length())),
110 Err(_) => {
111 let lo = (distance - 1.0).max(0.0);
112 (
113 StationPiece::between(*curve, lo, distance + 1.0).ok()?,
114 None,
115 )
116 }
117 };
118 if let Some(rigid) = placement {
119 piece = piece.placed(*rigid, *placement_exact);
120 }
121 owned = piece;
122 (&owned, length)
123 }
124 };
125 if !piece.placement_exact {
126 return None;
127 }
128 let at = admitted(distance, length).ok()?;
129 let stand = Stand {
130 at,
131 into: at - piece.start,
132 side,
133 reading,
134 };
135 self.error_in(0, piece, None, stand, (0.0, [false; 2]))
136 }
137 LawBasis::Composite(composite) => {
138 let at = admitted(distance, Some(composite.length())).ok()?;
139 let (k, into) = composite.locate_admitted(distance, side).ok()?;
140 let piece = &composite.pieces()[k];
141 if !piece.placement_exact {
142 return None;
143 }
144 let joint = self.joint_error(composite, k)?;
145 let ends = [k > 0, k + 1 < composite.pieces().len()];
146 let table = composite.tables()[k].as_ref();
147 let stand = Stand {
148 at,
149 into,
150 side,
151 reading,
152 };
153 self.error_in(k, piece, table, stand, (joint, ends))
154 }
155 }
156 }
157
158 fn error_in(
162 &self,
163 k: usize,
164 piece: &StationPiece<'c>,
165 table: Option<&OffsetTable>,
166 stand: Stand,
167 (joint, ends): (Scalar, [bool; 2]),
168 ) -> Option<StationFrameError> {
169 let Stand {
170 at, side, reading, ..
171 } = stand;
172 let length = piece.length();
173 let u = stand.into.clamp(0.0, length);
174 let here = self.read(k, piece, u, u, table, reading)?;
175 let (e_p, e_a) = match self.slot(k, piece, table) {
176 Slot::Closed => {
177 let point = piece.section_with(u, side, table).ok()?.point;
178 let scale = point.abs().max_element().max(data_scale(piece)).max(1.0);
181 (POINT_ROUNDING * scale, AXIS_ROUNDING)
182 }
183 Slot::Intrinsic(_) => (here.point_error, here.axis_error),
184 Slot::Read(_) | Slot::Unbounded => return None,
185 };
186 let delta = here.measure + joint;
187 let (mut s_point, mut s_axis) = (0.0, 0.0);
188 if delta > 0.0 {
189 let (mut lo, mut hi) = (u - 2.0 * delta, u + 2.0 * delta);
190 let (measure, _) = piece.read_at(u, side);
191 let on = slack(at).min(slack(measure));
192 let mut seams: Vec<Scalar> = piece.seams().ok()?.iter().map(|s| s.distance).collect();
193 if ends[0] {
194 seams.push(0.0);
195 }
196 if ends[1] {
197 seams.push(length);
198 }
199 for c in seams {
200 if (c - u).abs() <= on {
201 match side {
203 SeamSide::Incoming => hi = hi.min(c),
204 _ => lo = lo.max(c),
205 }
206 } else if (c - u).abs() <= 2.0 * delta {
207 return None;
208 }
209 }
210 let span = self.read(k, piece, lo.max(0.0), hi.min(length), table, reading)?;
211 s_point = span.point[0];
212 s_axis = span.tangent[0].max(span.lateral[0]).max(span.up[0]);
213 }
214 let grow = 1.0 + ROUNDING;
215 let out = StationFrameError {
216 point: (e_p + delta * s_point) * grow,
217 axis: (e_a + delta * s_axis) * grow,
218 };
219 (out.point.is_finite() && out.axis.is_finite()).then_some(out)
220 }
221}