ifc_lite_landxml/alignment/numeric/
render.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Bounded display sampling for analytic alignment primitives.
6
7use super::{
8    diagnostic, geometry::evaluate_segment, geometry::validate_segment, LandXmlNumericDiagnostic,
9};
10use crate::{
11    alignment::{LandXmlAlignmentPrimitive, LandXmlAlignmentSegment, LandXmlPlanPoint},
12    LandXmlSourceId,
13};
14
15/// A bounded display polyline for one analytic source primitive. Numeric
16/// probes remain authoritative; these samples exist only for overlay/picking.
17#[derive(Clone, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
18pub struct LandXmlAlignmentRenderSpan {
19    pub source_id: LandXmlSourceId,
20    pub points: Vec<LandXmlPlanPoint>,
21}
22
23impl LandXmlAlignmentSegment {
24    /// Sample supported analytic primitives through the canonical evaluator.
25    /// Authored lines/polylines are already retained verbatim by the source
26    /// document and unsupported transitions deliberately return no geometry.
27    pub fn render_span(
28        &self,
29        point_count: usize,
30    ) -> Result<Option<LandXmlAlignmentRenderSpan>, LandXmlNumericDiagnostic> {
31        match &self.primitive {
32            LandXmlAlignmentPrimitive::Curve(_) | LandXmlAlignmentPrimitive::Spiral(_) => {}
33            LandXmlAlignmentPrimitive::Line(_)
34            | LandXmlAlignmentPrimitive::IrregularLine(_)
35            | LandXmlAlignmentPrimitive::UnsupportedSpiral(_) => return Ok(None),
36        }
37        if point_count < 2 {
38            return Err(diagnostic(
39                &self.source_id,
40                "LXMLA231",
41                "render sampling requires at least two points",
42            ));
43        }
44        let length = validate_segment(self)?;
45        let mut points = Vec::with_capacity(point_count);
46        for index in 0..point_count {
47            let distance = length * index as f64 / (point_count - 1) as f64;
48            points.push(evaluate_segment(self, distance)?.0);
49        }
50        Ok(Some(LandXmlAlignmentRenderSpan {
51            source_id: self.source_id.clone(),
52            points,
53        }))
54    }
55}