use crate::bspline::{BSpline, ExtrapolateMode};
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::numeric::{Float, FromPrimitive};
use std::fmt::{Debug, Display};
use std::ops::{Add, Div, Mul, Sub};
pub trait NurbsFloat:
Float
+ FromPrimitive
+ Debug
+ Display
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::RemAssign
+ scirs2_core::ndarray::ScalarOperand
+ Copy
{
}
impl<T> NurbsFloat for T where
T: Float
+ FromPrimitive
+ Debug
+ Display
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::ops::MulAssign
+ std::ops::DivAssign
+ std::ops::RemAssign
+ scirs2_core::ndarray::ScalarOperand
+ Copy
{
}
#[derive(Debug, Clone)]
pub struct NurbsCurve<T: NurbsFloat> {
pub(crate) control_points: Array2<T>,
pub(crate) weights: Array1<T>,
pub(crate) bspline: BSpline<T>,
pub(crate) dimension: usize,
}
#[derive(Debug, Clone)]
pub struct NurbsSurface<T: NurbsFloat> {
pub(crate) control_points: Array2<T>,
pub(crate) weights: Array1<T>,
pub(crate) nu: usize,
pub(crate) nv: usize,
pub(crate) knotsu: Array1<T>,
pub(crate) knotsv: Array1<T>,
pub(crate) degreeu: usize,
pub(crate) degreev: usize,
pub(crate) dimension: usize,
pub(crate) extrapolate: ExtrapolateMode,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ValidationResult {
Valid,
DimensionMismatch,
InvalidKnots,
InvalidDegree,
InvalidWeights,
InvalidControlPoints,
}
impl ValidationResult {
pub fn is_valid(&self) -> bool {
matches!(self, ValidationResult::Valid)
}
pub fn to_error_message(&self) -> &'static str {
match self {
ValidationResult::Valid => "Input parameters are valid",
ValidationResult::DimensionMismatch => {
"Control points and weights have mismatched dimensions"
}
ValidationResult::InvalidKnots => "Knot vector is invalid",
ValidationResult::InvalidDegree => {
"Degree is too high for the number of control points"
}
ValidationResult::InvalidWeights => "Weights contain non-positive values",
ValidationResult::InvalidControlPoints => "Control points are empty or malformed",
}
}
}
pub struct NurbsValidator;
impl NurbsValidator {
pub fn validate_curve_inputs<T: NurbsFloat>(
control_points: &Array2<T>,
weights: &Array1<T>,
knots: &Array1<T>,
degree: usize,
) -> ValidationResult {
if control_points.is_empty() || control_points.nrows() == 0 {
return ValidationResult::InvalidControlPoints;
}
if control_points.nrows() != weights.len() {
return ValidationResult::DimensionMismatch;
}
for &weight in weights.iter() {
if weight <= T::zero() {
return ValidationResult::InvalidWeights;
}
}
if degree >= control_points.nrows() {
return ValidationResult::InvalidDegree;
}
if knots.len() != control_points.nrows() + degree + 1 {
return ValidationResult::InvalidKnots;
}
for i in 1..knots.len() {
if knots[i] < knots[i - 1] {
return ValidationResult::InvalidKnots;
}
}
ValidationResult::Valid
}
pub fn validate_surface_inputs<T: NurbsFloat>(
control_points: &Array2<T>,
weights: &Array1<T>,
nu: usize,
nv: usize,
knotsu: &Array1<T>,
knotsv: &Array1<T>,
degreeu: usize,
degreev: usize,
) -> ValidationResult {
if nu == 0 || nv == 0 {
return ValidationResult::InvalidControlPoints;
}
if control_points.nrows() != nu * nv {
return ValidationResult::DimensionMismatch;
}
if weights.len() != nu * nv {
return ValidationResult::DimensionMismatch;
}
for &weight in weights.iter() {
if weight <= T::zero() {
return ValidationResult::InvalidWeights;
}
}
if degreeu >= nu || degreev >= nv {
return ValidationResult::InvalidDegree;
}
if knotsu.len() != nu + degreeu + 1 {
return ValidationResult::InvalidKnots;
}
if knotsv.len() != nv + degreev + 1 {
return ValidationResult::InvalidKnots;
}
for i in 1..knotsu.len() {
if knotsu[i] < knotsu[i - 1] {
return ValidationResult::InvalidKnots;
}
}
for i in 1..knotsv.len() {
if knotsv[i] < knotsv[i - 1] {
return ValidationResult::InvalidKnots;
}
}
ValidationResult::Valid
}
}