axiolid_nurbs/
surface_transform.rs

1//! Exact tensor-product NURBS surface transformations.
2
3use axiolid_contracts::GeomResult;
4use axiolid_core::{Point3, Scalar};
5use axiolid_curve::BSplineCurve;
6use axiolid_evaluate::surface::bspline_jet;
7use axiolid_surface::BSplineSurface;
8
9use crate::{axis::reverse_axis, transform::insert_knot3};
10
11/// Insert one U-axis knot into every homogeneous control-net column.
12///
13/// The represented tensor-product surface is unchanged.
14pub fn insert_surface_knot_u(
15    surface: &BSplineSurface,
16    parameter: Scalar,
17) -> GeomResult<BSplineSurface> {
18    bspline_jet(surface, parameter, 0.0)?;
19    let columns = (0..surface.control_points[0].len())
20        .map(|v| insert_knot3(&u_curve(surface, v), parameter))
21        .collect::<GeomResult<Vec<_>>>()?;
22    let u_count = columns[0].control_points.len();
23    let control_points = (0..u_count)
24        .map(|u| {
25            columns
26                .iter()
27                .map(|column| column.control_points[u])
28                .collect()
29        })
30        .collect();
31    let weights = surface.weights.as_ref().map(|_| {
32        (0..u_count)
33            .map(|u| {
34                columns
35                    .iter()
36                    .map(|column| column.weights.as_ref().expect("source was rational")[u])
37                    .collect()
38            })
39            .collect()
40    });
41    Ok(BSplineSurface {
42        u_degree: surface.u_degree,
43        v_degree: surface.v_degree,
44        control_points,
45        u_knots: columns[0].knots.clone(),
46        u_multiplicities: columns[0].multiplicities.clone(),
47        v_knots: surface.v_knots.clone(),
48        v_multiplicities: surface.v_multiplicities.clone(),
49        weights,
50        knot_spec: surface.knot_spec,
51        u_closed: surface.u_closed,
52        v_closed: surface.v_closed,
53        self_intersect: surface.self_intersect,
54    })
55}
56
57/// Insert one V-axis knot into every homogeneous control-net row.
58///
59/// The represented tensor-product surface is unchanged.
60pub fn insert_surface_knot_v(
61    surface: &BSplineSurface,
62    parameter: Scalar,
63) -> GeomResult<BSplineSurface> {
64    bspline_jet(surface, 0.0, parameter)?;
65    let rows = (0..surface.control_points.len())
66        .map(|u| insert_knot3(&v_curve(surface, u), parameter))
67        .collect::<GeomResult<Vec<_>>>()?;
68    let control_points = rows.iter().map(|row| row.control_points.clone()).collect();
69    let weights = surface.weights.as_ref().map(|_| {
70        rows.iter()
71            .map(|row| row.weights.as_ref().expect("source was rational").clone())
72            .collect()
73    });
74    Ok(BSplineSurface {
75        u_degree: surface.u_degree,
76        v_degree: surface.v_degree,
77        control_points,
78        u_knots: surface.u_knots.clone(),
79        u_multiplicities: surface.u_multiplicities.clone(),
80        v_knots: rows[0].knots.clone(),
81        v_multiplicities: rows[0].multiplicities.clone(),
82        weights,
83        knot_spec: surface.knot_spec,
84        u_closed: surface.u_closed,
85        v_closed: surface.v_closed,
86        self_intersect: surface.self_intersect,
87    })
88}
89
90/// Reverse the surface's U parameter without changing its image.
91pub fn reverse_surface_u(surface: &BSplineSurface) -> GeomResult<BSplineSurface> {
92    bspline_jet(surface, 0.0, 0.0)?;
93    let mut result = surface.clone();
94    result.control_points.reverse();
95    if let Some(weights) = &mut result.weights {
96        weights.reverse();
97    }
98    (result.u_knots, result.u_multiplicities) =
99        reverse_axis(&surface.u_knots, &surface.u_multiplicities)?;
100    Ok(result)
101}
102
103/// Reverse the surface's V parameter without changing its image.
104pub fn reverse_surface_v(surface: &BSplineSurface) -> GeomResult<BSplineSurface> {
105    bspline_jet(surface, 0.0, 0.0)?;
106    let mut result = surface.clone();
107    for row in &mut result.control_points {
108        row.reverse();
109    }
110    if let Some(weights) = &mut result.weights {
111        for row in weights {
112            row.reverse();
113        }
114    }
115    (result.v_knots, result.v_multiplicities) =
116        reverse_axis(&surface.v_knots, &surface.v_multiplicities)?;
117    Ok(result)
118}
119
120pub(crate) fn u_curve(surface: &BSplineSurface, v: usize) -> BSplineCurve<Point3> {
121    BSplineCurve {
122        degree: surface.u_degree,
123        control_points: surface.control_points.iter().map(|row| row[v]).collect(),
124        knots: surface.u_knots.clone(),
125        multiplicities: surface.u_multiplicities.clone(),
126        weights: surface
127            .weights
128            .as_ref()
129            .map(|rows| rows.iter().map(|row| row[v]).collect()),
130        knot_spec: surface.knot_spec,
131        closed: surface.u_closed,
132        self_intersect: surface.self_intersect,
133    }
134}
135
136fn v_curve(surface: &BSplineSurface, u: usize) -> BSplineCurve<Point3> {
137    BSplineCurve {
138        degree: surface.v_degree,
139        control_points: surface.control_points[u].clone(),
140        knots: surface.v_knots.clone(),
141        multiplicities: surface.v_multiplicities.clone(),
142        weights: surface.weights.as_ref().map(|rows| rows[u].clone()),
143        knot_spec: surface.knot_spec,
144        closed: surface.v_closed,
145        self_intersect: surface.self_intersect,
146    }
147}