ifc_lite_processing/analytic_export/
metrics.rs1use ifc_lite_geometry::analytic::AnalyticCurveSegment;
8use serde::Serialize;
9
10#[derive(Debug, Clone, Serialize)]
12#[non_exhaustive]
13pub struct DirectrixSegmentMetrics {
14 pub segment_index: usize,
16 pub length: f64,
18 pub bend_angle: Option<f64>,
21}
22
23#[derive(Debug, Clone, Serialize)]
25#[non_exhaustive]
26pub struct DirectrixMetrics {
27 pub total_length: f64,
29 pub segments: Vec<DirectrixSegmentMetrics>,
30}
31
32impl DirectrixMetrics {
33 pub(super) fn from_segments(segments: &[AnalyticCurveSegment]) -> Result<Self, &'static str> {
34 let mut result = Self {
35 total_length: 0.0,
36 segments: Vec::with_capacity(segments.len()),
37 };
38 for (segment_index, segment) in segments.iter().enumerate() {
39 let (length, bend_angle) = match segment {
40 AnalyticCurveSegment::Line { .. } =>
41 (segment.length().ok_or("directrix measurements are not finite")?, None),
42 AnalyticCurveSegment::Arc { radius, sweep_angle, .. } => {
43 if *radius <= 0.0 {
44 return Err("directrix arc radius is not positive");
45 }
46 (segment.length().ok_or("directrix measurements are not finite")?, Some(sweep_angle.abs()))
47 }
48 };
49 if !length.is_finite() || bend_angle.is_some_and(|angle| !angle.is_finite()) {
50 return Err("directrix measurements are not finite");
51 }
52 result.total_length += length;
53 if !result.total_length.is_finite() {
54 return Err("directrix total length is not finite");
55 }
56 result.segments.push(DirectrixSegmentMetrics { segment_index, length, bend_angle });
57 }
58 Ok(result)
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn negative_arc_sweep_reports_positive_bend_and_length() {
68 let arc = AnalyticCurveSegment::Arc {
69 center: [0.0; 3],
70 normal: [0.0, 0.0, 1.0],
71 x_axis: [1.0, 0.0, 0.0],
72 radius: 2.0,
73 start_angle: 0.0,
74 sweep_angle: -std::f64::consts::PI,
75 };
76 let metrics = DirectrixMetrics::from_segments(&[arc]).unwrap();
77 assert_eq!(metrics.total_length, 2.0 * std::f64::consts::PI);
78 assert_eq!(metrics.segments[0].bend_angle, Some(std::f64::consts::PI));
79 }
80
81 #[test]
82 fn overflowing_line_or_total_has_no_partial_result() {
83 let line = |start, end| AnalyticCurveSegment::Line {
84 start: [start, 0.0, 0.0],
85 end: [end, 0.0, 0.0],
86 };
87 assert!(DirectrixMetrics::from_segments(&[line(-1e308, 1e308)]).is_err());
88 assert!(DirectrixMetrics::from_segments(&[line(0.0, 1e308), line(0.0, 1e308)]).is_err());
89 }
90}