axiolid_nurbs/
projection.rs

1//! Explicit policy and honest outcomes for bounded inverse queries.
2
3use axiolid_contracts::{GeomError, GeomResult};
4use axiolid_core::{Point2, Point3, Scalar, Tolerance};
5
6#[derive(Debug, Clone, Copy, PartialEq)]
7/// Explicit work and convergence policy for bounded projection.
8///
9/// The search samples every active knot span, then runs Newton refinement from
10/// each start until one of these budgets is reached. There is deliberately no
11/// context-free `Default`.
12pub struct ProjectionOptions {
13    tolerance: Tolerance,
14    samples_per_span: u16,
15    max_iterations: u16,
16    max_starts: u32,
17}
18
19impl ProjectionOptions {
20    /// Construct a non-vacuous projection policy.
21    ///
22    /// Returns an error when any budget is zero.
23    pub fn new(
24        tolerance: Tolerance,
25        samples_per_span: u16,
26        max_iterations: u16,
27        max_starts: u32,
28    ) -> GeomResult<Self> {
29        if samples_per_span == 0 || max_iterations == 0 || max_starts == 0 {
30            return Err(GeomError::InvalidInput(
31                "projection budgets must be non-zero".to_owned(),
32            ));
33        }
34        Ok(Self {
35            tolerance,
36            samples_per_span,
37            max_iterations,
38            max_starts,
39        })
40    }
41    /// Linear/angular convergence tolerance.
42    pub const fn tolerance(self) -> Tolerance {
43        self.tolerance
44    }
45    /// Uniform subdivisions used to seed each active knot span.
46    pub const fn samples_per_span(self) -> u16 {
47        self.samples_per_span
48    }
49    /// Maximum Newton updates for one start.
50    pub const fn max_iterations(self) -> u16 {
51        self.max_iterations
52    }
53    /// Aggregate start limit across all spans (or span pairs).
54    pub const fn max_starts(self) -> u32 {
55        self.max_starts
56    }
57}
58
59#[non_exhaustive]
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61/// Outcome of the selected bounded local solve.
62///
63/// This status does not certify global optimality.
64pub enum ProjectionStatus {
65    /// First-order stationarity or tolerance-sized movement was reached.
66    Converged,
67    /// The selected candidate remained best when its iteration budget ended.
68    BudgetExhausted,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq)]
72/// Best planar-curve projection candidate found within the supplied budget.
73pub struct CurveProjection2 {
74    /// Native curve parameter.
75    pub parameter: Scalar,
76    /// Evaluated point at `parameter`.
77    pub point: Point2,
78    /// Euclidean distance from the target.
79    pub distance: Scalar,
80    /// Newton updates used by the selected start.
81    pub iterations: u16,
82    /// Whether the selected parameter tuple touches its active domain boundary.
83    pub on_boundary: bool,
84    /// Local-solve outcome; not a global-optimum certificate.
85    pub status: ProjectionStatus,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq)]
89/// Best spatial-curve projection candidate found within the supplied budget.
90pub struct CurveProjection3 {
91    /// Native curve parameter.
92    pub parameter: Scalar,
93    /// Evaluated point at `parameter`.
94    pub point: Point3,
95    /// Euclidean distance from the target.
96    pub distance: Scalar,
97    /// Newton updates used by the selected start.
98    pub iterations: u16,
99    /// Whether the selected parameter tuple touches its active domain boundary.
100    pub on_boundary: bool,
101    /// Local-solve outcome; not a global-optimum certificate.
102    pub status: ProjectionStatus,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq)]
106/// Best surface projection candidate found within the supplied budget.
107pub struct SurfaceProjection {
108    /// Native first-axis surface parameter.
109    pub u: Scalar,
110    /// Native second-axis surface parameter.
111    pub v: Scalar,
112    /// Evaluated surface point at `(u, v)`.
113    pub point: Point3,
114    /// Euclidean distance from the target.
115    pub distance: Scalar,
116    /// Newton updates used by the selected start.
117    pub iterations: u16,
118    /// Whether the selected parameter tuple touches its active domain boundary.
119    pub on_boundary: bool,
120    /// Local-solve outcome; not a global-optimum certificate.
121    pub status: ProjectionStatus,
122}