Skip to main content

axiolid_mesh_compile_contract/
polyline.rs

1//! Curves compiled to polylines, with a certified chord bound (#311).
2//!
3//! A curve node has no area, so it has no triangle mesh: compiling it with
4//! [`crate::MeshCompiler::compile_mesh`] is refused. It is a separate
5//! operation instead of a second output kind of `compile_mesh`, so the mesh
6//! path's signature and every consumer of it stay as they are.
7
8use axiolid_contracts::{Backend, ExecutionOptions, GeomResult};
9use axiolid_core::{Point3, Scalar};
10use axiolid_model::{GeometryGraph, NodeId};
11
12/// How far the exact curve may lie from its polyline, reported the way a
13/// compiled mesh's deviation is: the chord budget asked for, and a
14/// certified upper bound on the distance from every point of the exact
15/// curve to the nearest point of the polyline, both in world units.
16///
17/// The bound is one-sided, exact curve to polyline, and it is always
18/// present: a compiler that cannot bound a curve refuses it rather than
19/// returning a polyline with no bound.
20#[non_exhaustive]
21#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct ChordDeviation {
23    /// The chord budget the compilation was asked for:
24    /// `ExecutionOptions::chord_error`, else the linear tolerance.
25    pub requested: Scalar,
26    /// An upper bound on the distance from every point of the exact curve
27    /// to the polyline.
28    pub bound: Scalar,
29}
30
31impl ChordDeviation {
32    /// A bound against the budget it was asked for.
33    #[must_use]
34    pub const fn new(requested: Scalar, bound: Scalar) -> Self {
35        Self { requested, bound }
36    }
37
38    /// Whether the polyline is certified within the requested budget.
39    ///
40    /// `false` means "not certified", not "missed": the bound is an upper
41    /// bound, and the true deviation may still be below the budget.
42    #[must_use]
43    pub fn meets_requested(&self) -> bool {
44        self.bound <= self.requested
45    }
46}
47
48/// A curve compiled to a 3D polyline.
49#[non_exhaustive]
50#[derive(Debug, Clone, PartialEq)]
51pub struct PolylineOutcome {
52    /// The vertices in the curve's direction of travel. A closed curve
53    /// ends on the vertex it starts on.
54    pub points: Vec<Point3>,
55    /// Whether the curve is closed: the last vertex is the first.
56    pub closed: bool,
57    /// How far the exact curve may lie from the polyline.
58    pub deviation: ChordDeviation,
59}
60
61impl PolylineOutcome {
62    /// A polyline and its deviation. `closed` is read from the points: at
63    /// least three of them, the last equal to the first.
64    #[must_use]
65    pub fn new(points: Vec<Point3>, deviation: ChordDeviation) -> Self {
66        let closed = points.len() > 2 && points.first() == points.last();
67        Self {
68            points,
69            closed,
70            deviation,
71        }
72    }
73}
74
75/// Backend that compiles a curve node of a graph to a 3D polyline.
76///
77/// Implementations must return [`axiolid_contracts::GeomError::Unsupported`]
78/// or [`axiolid_contracts::GeomError::UnsupportedInput`] for a node they
79/// cannot compile or cannot bound, naming it; they never return a polyline
80/// whose [`ChordDeviation::bound`] is not an upper bound.
81pub trait PolylineCompiler: Backend {
82    /// Compile one root to a polyline within the options' chord budget.
83    fn compile_polyline(
84        &self,
85        graph: &GeometryGraph,
86        root: NodeId,
87        options: &ExecutionOptions,
88    ) -> GeomResult<PolylineOutcome>;
89}