pub fn constraints_param_bspline(
degree: i64,
up_knots: i64,
up_cp: i64,
knot_mult: &[i64],
knots: &[f64],
) -> bool {
if knot_mult.is_empty() || (up_knots as usize) > knot_mult.len() {
return false;
}
let sum: i64 = knot_mult.iter().take(up_knots as usize).sum();
if degree < 1 || up_knots < 2 || up_cp < degree || sum != degree + up_cp + 2 {
return false;
}
let k = knot_mult[0];
if k < 1 || k > degree + 1 {
return false;
}
for i in 2..=up_knots {
let idx = (i - 1) as usize;
if idx >= knot_mult.len() || idx >= knots.len() {
return false;
}
if knot_mult[idx] < 1 || knots[idx] <= knots[idx - 1] {
return false;
}
let k = knot_mult[idx];
if i < up_knots && k > degree {
return false;
}
if i == up_knots && k > degree + 1 {
return false;
}
}
true
}
pub fn consecutive_segments(segments: &[Vec<i64>]) -> bool {
for pair in segments.windows(2) {
let (a, b) = (&pair[0], &pair[1]);
let (Some(end), Some(start)) = (a.last(), b.first()) else {
return false;
};
if end != start {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_clamped_cubic_is_a_valid_parametrisation() {
assert!(constraints_param_bspline(3, 2, 3, &[4, 4], &[0.0, 1.0]));
}
#[test]
fn the_multiplicity_sum_must_equal_degree_plus_upcp_plus_two() {
assert!(!constraints_param_bspline(3, 2, 3, &[4, 3], &[0.0, 1.0]));
}
#[test]
fn knots_must_strictly_increase() {
assert!(!constraints_param_bspline(3, 2, 3, &[4, 4], &[1.0, 1.0]));
}
#[test]
fn an_interior_multiplicity_may_not_exceed_the_degree() {
assert!(!constraints_param_bspline(
3,
3,
5,
&[3, 4, 3],
&[0.0, 0.5, 1.0]
));
}
#[test]
fn the_first_multiplicity_may_not_exceed_degree_plus_one() {
assert!(!constraints_param_bspline(2, 2, 3, &[4, 3], &[0.0, 1.0]));
}
#[test]
fn degree_zero_and_a_single_knot_are_rejected() {
assert!(!constraints_param_bspline(0, 2, 3, &[4, 4], &[0.0, 1.0]));
assert!(!constraints_param_bspline(3, 1, 3, &[4], &[0.0]));
}
#[test]
fn fewer_control_points_than_the_degree_is_rejected() {
assert!(!constraints_param_bspline(3, 2, 2, &[4, 3], &[0.0, 1.0]));
}
#[test]
fn segments_join_end_to_start() {
assert!(consecutive_segments(&[vec![1, 2, 3], vec![3, 4]]));
assert!(!consecutive_segments(&[vec![1, 2, 3], vec![9, 4]]));
assert!(consecutive_segments(&[vec![1, 2]]));
}
}