597 lines
21 KiB
Rust
597 lines
21 KiB
Rust
use super::*;
|
|
use crate::errors::Error;
|
|
use std::slice::SliceIndex;
|
|
use std::vec::Vec;
|
|
|
|
impl KnotVec {
|
|
/// empty constructor
|
|
pub const fn new() -> KnotVec { KnotVec(Vec::new()) }
|
|
|
|
/// Returns the length of range.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// let knot_vec = KnotVec::from(vec![0.0, 6.0]);
|
|
/// assert_eq!(knot_vec.range_length(), 6.0);
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn range_length(&self) -> f64 {
|
|
match self.is_empty() {
|
|
true => 0.0,
|
|
false => self[self.len() - 1] - self[0],
|
|
}
|
|
}
|
|
|
|
/// Returns whether two knot vectors have the same range.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// let knot_vec0 = KnotVec::new(); // empty knot vector
|
|
/// let knot_vec1 = KnotVec::from(vec![0.0, 0.0, 1.0, 1.0]);
|
|
/// let knot_vec2 = KnotVec::from(vec![0.0, 0.5, 1.0]);
|
|
/// let knot_vec3 = KnotVec::from(vec![0.0, 0.0, 2.0, 2.0]);
|
|
/// assert!(knot_vec0.same_range(&KnotVec::new())); // both empty knot vector
|
|
/// assert!(!knot_vec0.same_range(&knot_vec1));
|
|
/// assert!(knot_vec1.same_range(&knot_vec2)); // the range of both knot vector is [0, 1].
|
|
/// assert!(!knot_vec1.same_range(&knot_vec3));
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn same_range(&self, other: &KnotVec) -> bool {
|
|
match (self.is_empty(), other.is_empty()) {
|
|
(false, false) => {
|
|
self[0].near(&other[0]) && self.range_length().near(&other.range_length())
|
|
}
|
|
(true, true) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Removes one item.
|
|
#[inline(always)]
|
|
pub fn remove(&mut self, idx: usize) -> f64 { self.0.remove(idx) }
|
|
|
|
/// Returns the maximum index `i` of `self[i] <= x`
|
|
/// Return `None` if `x < self[0] or self.len() == 0`.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec = KnotVec::from(vec![0.0, 0.0, 1.0, 2.0, 3.0, 3.0]);
|
|
/// let idx = knot_vec.floor(1.5).unwrap();
|
|
/// assert_eq!(idx, 2);
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn floor(&self, x: f64) -> Option<usize> { self.iter().rposition(|t| *t <= x) }
|
|
|
|
/// Returns the multiplicity of the `i`th knot
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let knot_vec = KnotVec::from(vec![0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0]);
|
|
/// assert_eq!(knot_vec.multiplicity(5), 3);
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn multiplicity(&self, i: usize) -> usize {
|
|
self.iter().filter(|u| self[i].near(u)).count()
|
|
}
|
|
|
|
/// Adds a knot and return the index of the added knot.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec = KnotVec::from(vec![0.0, 0.0, 1.0, 2.0, 3.0, 3.0]);
|
|
/// let idx0 = knot_vec.add_knot(1.5);
|
|
/// assert_eq!(idx0, 3);
|
|
/// let idx1 = knot_vec.add_knot(-1.0);
|
|
/// assert_eq!(idx1, 0);
|
|
/// let ansvec = KnotVec::from(vec![-1.0, 0.0, 0.0, 1.0, 1.5, 2.0, 3.0, 3.0]);
|
|
/// assert_eq!(knot_vec, ansvec);
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn add_knot(&mut self, knot: f64) -> usize {
|
|
match self.floor(knot) {
|
|
Some(idx) => {
|
|
self.0.insert(idx + 1, knot);
|
|
idx + 1
|
|
}
|
|
None => {
|
|
self.0.insert(0, knot);
|
|
0
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Calculates B-spline basis functions at `t` with degree `degree`.
|
|
/// # Panics
|
|
/// If the length of `self` is not more than `degree`, panic occurs.
|
|
/// # Remarks
|
|
/// In this package, the B-spline basis function is based on the characteristic function of
|
|
/// the right-open intervals [s, t). So, the value corresponding to the end point t = t_n is always 0.0.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// const N : usize = 100; // sample size in tests
|
|
///
|
|
/// // B-spline basis functions is a partition of unity in (t_k, t_{n - k}).
|
|
/// let vec = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
|
/// let knot_vec = KnotVec::from(vec);
|
|
/// let degree = 2;
|
|
/// for i in 0..N {
|
|
/// let t = 2.0 + 4.0 / (N as f64) * (i as f64);
|
|
/// let res = knot_vec.bspline_basis_functions(degree, t);
|
|
/// let sum = res.iter().fold(0.0, |sum, a| sum + a);
|
|
/// assert_near2!(sum, 1.0);
|
|
/// }
|
|
/// ```
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// const N : usize = 100; // sample size in tests
|
|
///
|
|
/// // In some case, B-spline basis functions coincide with Bernstein polynomials.
|
|
/// let vec = vec![0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0];
|
|
/// let knot_vec = KnotVec::from(vec);
|
|
/// let degree = 3;
|
|
/// for i in 0..=N {
|
|
/// let t = 1.0 / (N as f64) * (i as f64);
|
|
/// let res = knot_vec.bspline_basis_functions(degree, t);
|
|
/// let ans = [
|
|
/// 1.0 * (1.0 - t) * (1.0 - t) * (1.0 - t),
|
|
/// 3.0 * t * (1.0 - t) * (1.0 - t),
|
|
/// 3.0 * t * t * (1.0 - t),
|
|
/// 1.0 * t * t * t,
|
|
/// ];
|
|
/// for i in 0..4 { assert_near2!(res[i], ans[i]); }
|
|
/// }
|
|
/// ```
|
|
pub fn bspline_basis_functions(&self, degree: usize, t: f64) -> Vec<f64> {
|
|
match self.try_bspline_basis_functions(degree, t) {
|
|
Ok(got) => got,
|
|
Err(error) => panic!("{}", error),
|
|
}
|
|
}
|
|
|
|
/// Calculates B-spline basis functions at `t` with degree `degree`.
|
|
/// # Failures
|
|
/// - If the range of the knot vector is zero, returns [`Error::ZeroRange`].
|
|
/// - If the length of `self` is not more than `degree`, returns [`Error::TooLargeDegree`].
|
|
/// # Remarks
|
|
/// In this package, the B-spline basis function is based on the characteristic function of
|
|
/// the right-open intervals [s, t). So, the value corresponding to the end point t = t_n is always 0.0.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// const N : usize = 100; // sample size in tests
|
|
///
|
|
/// // B-spline basis functions is a partition of unity in (t_k, t_{n - k}).
|
|
/// let vec = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
|
/// let knot_vec = KnotVec::from(vec);
|
|
/// let degree = 2;
|
|
/// for i in 0..N {
|
|
/// let t = 2.0 + 4.0 / (N as f64) * (i as f64);
|
|
/// let res = knot_vec.try_bspline_basis_functions(degree, t).unwrap();
|
|
/// let sum = res.iter().fold(0.0, |sum, a| sum + a);
|
|
/// assert_near2!(sum, 1.0);
|
|
/// }
|
|
/// ```
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// const N : usize = 100; // sample size in tests
|
|
///
|
|
/// // In some case, B-spline basis functions coincide with Bernstein polynomials.
|
|
/// let vec = vec![0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0];
|
|
/// let knot_vec = KnotVec::from(vec);
|
|
/// let degree = 3;
|
|
/// for i in 0..=N {
|
|
/// let t = i as f64 / N as f64;
|
|
/// let res = knot_vec.try_bspline_basis_functions(degree, t).unwrap();
|
|
/// let ans = [
|
|
/// 1.0 * (1.0 - t) * (1.0 - t) * (1.0 - t),
|
|
/// 3.0 * t * (1.0 - t) * (1.0 - t),
|
|
/// 3.0 * t * t * (1.0 - t),
|
|
/// 1.0 * t * t * t,
|
|
/// ];
|
|
/// for i in 0..4 { assert_near2!(res[i], ans[i]); }
|
|
/// }
|
|
/// ```
|
|
pub fn try_bspline_basis_functions(&self, degree: usize, t: f64) -> Result<Vec<f64>> {
|
|
let n = self.len() - 1;
|
|
if self[0].near(&self[n]) {
|
|
return Err(Error::ZeroRange);
|
|
} else if n < degree {
|
|
return Err(Error::TooLargeDegree(n + 1, degree));
|
|
}
|
|
|
|
let idx = {
|
|
let idx = self
|
|
.floor(t)
|
|
.unwrap_or_else(|| self.floor(self[0]).unwrap());
|
|
if idx == n {
|
|
n - self.multiplicity(n)
|
|
} else {
|
|
idx
|
|
}
|
|
};
|
|
let mut res = vec![0.0; n];
|
|
res[idx] = 1.0;
|
|
|
|
for k in 1..=degree {
|
|
let base = if idx < k { 0 } else { idx - k };
|
|
let delta = self[base + k] - self[base];
|
|
let max = if idx + k < n { idx } else { n - k - 1 };
|
|
let mut a = inv_or_zero(delta) * (t - self[base]);
|
|
for i in base..=max {
|
|
let delta = self[i + k + 1] - self[i + 1];
|
|
let b = inv_or_zero(delta) * (self[i + k + 1] - t);
|
|
res[i] = a * res[i] + b * res[i + 1];
|
|
a = 1.0 - b;
|
|
}
|
|
}
|
|
|
|
res.truncate(n - degree);
|
|
Ok(res)
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
pub fn maximum_points(&self, degree: usize) -> Vec<f64> {
|
|
let n = self.len();
|
|
let m = n - degree - 1;
|
|
let range = self.range_length();
|
|
const N: i32 = 100;
|
|
|
|
let mut res = vec![0.0; m];
|
|
let mut max = vec![0.0; m];
|
|
for i in 1..N {
|
|
let t = self[0] + range * (i as f64) / (N as f64);
|
|
let vals = self.try_bspline_basis_functions(degree, t).unwrap();
|
|
for j in 0..m {
|
|
if max[j] < vals[j] {
|
|
max[j] = vals[j];
|
|
res[j] = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
res
|
|
}
|
|
|
|
/// Transforms the knot vector
|
|
/// # Examples
|
|
/// ```
|
|
/// use std::vec::Vec;
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec = KnotVec::from(vec![1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 5.0]);
|
|
/// knot_vec.transform(2.0, 3.0);
|
|
/// let res : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(res, vec![5.0, 5.0, 7.0, 9.0, 11.0, 13.0, 13.0]);
|
|
/// ```
|
|
/// # Panics
|
|
/// Panic occurs if `scalar` is not positive.
|
|
pub fn transform(&mut self, scalar: f64, r#move: f64) -> &mut Self {
|
|
assert!(scalar > 0.0, "The scalar {scalar} is not positive.");
|
|
self.0
|
|
.iter_mut()
|
|
.for_each(move |vec| *vec = *vec * scalar + r#move);
|
|
self
|
|
}
|
|
|
|
/// Normalizes the knot vector i.e. makes the first value 0 and the last value 1.
|
|
/// # Failures
|
|
/// Returns [`Error::ZeroRange`] if the range of the knot vector is so small.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec = KnotVec::from(vec![1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 5.0]);
|
|
/// knot_vec.try_normalize().unwrap();
|
|
/// let res : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(res, vec![0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0]);
|
|
/// ```
|
|
pub fn try_normalize(&mut self) -> Result<&mut Self> {
|
|
let range = self.range_length();
|
|
if range.so_small() {
|
|
return Err(Error::ZeroRange);
|
|
}
|
|
Ok(self.transform(1.0 / range, -self[0] / range))
|
|
}
|
|
|
|
/// Normalizes the knot vector i.e. makes the first value 0 and the last value 1.
|
|
/// # Panics
|
|
/// Panic occurs if the range of the knot vector is so small.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec = KnotVec::from(vec![1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 5.0]);
|
|
/// knot_vec.normalize();
|
|
/// let res : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(res, vec![0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0]);
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn normalize(&mut self) -> &mut Self {
|
|
self.try_normalize()
|
|
.unwrap_or_else(|error| panic!("{}", error))
|
|
}
|
|
|
|
/// Translates the knot vector
|
|
/// # Example
|
|
/// ```
|
|
/// use std::vec::Vec;
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec = KnotVec::from(vec![1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 5.0]);
|
|
/// knot_vec.translate(3.0);
|
|
/// let res : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(res, vec![4.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0]);
|
|
/// ```
|
|
pub fn translate(&mut self, x: f64) -> &mut Self { self.transform(1.0, x) }
|
|
|
|
/// Inverts the knot vector
|
|
/// # Example
|
|
/// ```
|
|
/// use std::vec::Vec;
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec = KnotVec::from(vec![1.0, 1.0, 1.0, 3.0, 5.0, 6.0]);
|
|
/// knot_vec.invert();
|
|
/// let res : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(res, vec![1.0, 2.0, 4.0, 6.0, 6.0, 6.0]);
|
|
///
|
|
/// ```
|
|
pub fn invert(&mut self) -> &mut Self {
|
|
let n = self.len();
|
|
if n == 0 {
|
|
return self;
|
|
}
|
|
let range = self[0] + self[n - 1];
|
|
let clone = self.0.clone();
|
|
for (knot1, knot0) in clone.iter().rev().zip(&mut self.0) {
|
|
*knot0 = range - knot1;
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Determines the knot vector is clamped for the given degree.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let knot_vec = KnotVec::from(vec![0.0, 0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0, 1.0]);
|
|
/// assert!(knot_vec.is_clamped(2));
|
|
/// assert!(!knot_vec.is_clamped(3));
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn is_clamped(&self, degree: usize) -> bool {
|
|
self.multiplicity(0) > degree && self.multiplicity(self.len() - 1) > degree
|
|
}
|
|
|
|
/// Concats two knot vectors.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec0 = KnotVec::from(vec![0.0, 0.0, 0.0, 1.0, 1.0, 1.0]);
|
|
/// let knot_vec1 = KnotVec::from(vec![1.0, 1.0, 1.0, 2.0, 2.0, 2.0]);
|
|
/// knot_vec0.try_concat(&knot_vec1, 2).unwrap();
|
|
/// assert_eq!(knot_vec0.as_slice(), &[0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0]);
|
|
/// ```
|
|
/// # Failures
|
|
/// - If at least one of `self` or `other` is not clamped, returns [`Error::NotClampedKnotVector`]
|
|
/// - If the last knot of `self` and the first knot of `other` are different, returns [`Error::DifferentBackFront`].
|
|
pub fn try_concat(&mut self, other: &KnotVec, degree: usize) -> Result<&mut Self> {
|
|
if !self.is_clamped(degree) || !other.is_clamped(degree) {
|
|
return Err(Error::NotClampedKnotVector);
|
|
}
|
|
let back = self.0.last().unwrap();
|
|
let front = other.0.first().unwrap();
|
|
if front < back || !front.near(back) {
|
|
return Err(Error::DifferentBackFront(*back, *front));
|
|
}
|
|
|
|
self.0.truncate(self.len() - degree - 1);
|
|
for knot in &other.0 {
|
|
self.0.push(*knot);
|
|
}
|
|
|
|
Ok(self)
|
|
}
|
|
|
|
/// Concats two knot vectors.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let mut knot_vec0 = KnotVec::from(vec![0.0, 0.0, 0.0, 1.0, 1.0, 1.0]);
|
|
/// let knot_vec1 = KnotVec::from(vec![1.0, 1.0, 1.0, 2.0, 2.0, 2.0]);
|
|
/// knot_vec0.concat(&knot_vec1, 2);
|
|
/// assert_eq!(knot_vec0.as_slice(), &[0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0]);
|
|
/// ```
|
|
/// # Panics
|
|
/// Panic occurs if:
|
|
/// - at least one of `self` or `other` is not clamped.
|
|
/// - the last knot of `self` and the first knot of `other` are different.
|
|
#[inline(always)]
|
|
pub fn concat(&mut self, other: &KnotVec, degree: usize) -> &mut Self {
|
|
self.try_concat(other, degree)
|
|
.unwrap_or_else(|error| panic!("{}", error))
|
|
}
|
|
|
|
/// Returns trimmed vector by the specified range.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// let knot_vec = KnotVec::from(vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
|
|
/// let sub_vec = knot_vec.sub_vec(1..3);
|
|
/// assert_eq!(sub_vec, KnotVec::from(vec![1.0, 2.0]));
|
|
/// ```
|
|
#[inline(always)]
|
|
pub fn sub_vec<I: SliceIndex<[f64], Output = [f64]>>(&self, range: I) -> KnotVec {
|
|
KnotVec(Vec::from(&self.0[range]))
|
|
}
|
|
|
|
/// To single-multi description. i.e. decompose the unique vector of knots and the vector of
|
|
/// multiplicity of knots.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let knot_vec = KnotVec::from(vec![0.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0]);
|
|
/// let (knots, mults) = knot_vec.to_single_multi();
|
|
/// assert_eq!(knots, vec![0.0, 1.0, 2.0, 3.0]);
|
|
/// assert_eq!(mults, vec![3, 1, 4, 2]);
|
|
/// ```
|
|
pub fn to_single_multi(&self) -> (Vec<f64>, Vec<usize>) {
|
|
let mut knots = Vec::new();
|
|
let mut mults = Vec::new();
|
|
|
|
let mut iter = self.as_slice().iter().peekable();
|
|
let mut mult = 1;
|
|
while let Some(knot) = iter.next() {
|
|
if let Some(next) = iter.peek() {
|
|
if knot.near(next) {
|
|
mult += 1;
|
|
} else {
|
|
knots.push(*knot);
|
|
mults.push(mult);
|
|
mult = 1;
|
|
}
|
|
} else {
|
|
knots.push(*knot);
|
|
mults.push(mult);
|
|
}
|
|
}
|
|
(knots, mults)
|
|
}
|
|
|
|
/// Constructs from single-multi description.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let knots = vec![0.0, 1.0, 2.0, 3.0];
|
|
/// let mults = vec![3, 1, 4, 2];
|
|
/// let knot_vec = KnotVec::from_single_multi(knots, mults).unwrap();
|
|
/// assert_eq!(knot_vec, KnotVec::from(vec![0.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0]));
|
|
/// ```
|
|
pub fn from_single_multi(knots: Vec<f64>, mults: Vec<usize>) -> Result<KnotVec> {
|
|
for i in 1..knots.len() {
|
|
if knots[i - 1] > knots[i] {
|
|
return Err(Error::NotSortedVector);
|
|
}
|
|
}
|
|
|
|
let mut vec = Vec::new();
|
|
for i in 0..knots.len() {
|
|
for _ in 0..mults[i] {
|
|
vec.push(knots[i]);
|
|
}
|
|
}
|
|
Ok(KnotVec(vec))
|
|
}
|
|
/// Constructs from `Vec<f64>`. do not sort, only check sorted.
|
|
pub fn try_from(vec: Vec<f64>) -> Result<KnotVec> {
|
|
for i in 1..vec.len() {
|
|
if vec[i - 1] > vec[i] {
|
|
return Err(Error::NotSortedVector);
|
|
}
|
|
}
|
|
Ok(KnotVec(vec))
|
|
}
|
|
|
|
/// Constructs the knot vector for the bezier spline.
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// assert_eq!(
|
|
/// *KnotVec::bezier_knot(3),
|
|
/// vec![0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0],
|
|
/// );
|
|
/// ```
|
|
pub fn bezier_knot(degree: usize) -> KnotVec {
|
|
let mut vec = vec![0.0; degree + 1];
|
|
vec.extend(std::iter::repeat(1.0).take(degree + 1));
|
|
KnotVec(vec)
|
|
}
|
|
|
|
/// Constructs the uniform knot vector
|
|
/// # Examples
|
|
/// ```
|
|
/// use truck_geometry::prelude::*;
|
|
/// assert_eq!(
|
|
/// *KnotVec::uniform_knot(2, 5),
|
|
/// vec![0.0, 0.0, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.0, 1.0],
|
|
/// );
|
|
/// ```
|
|
pub fn uniform_knot(degree: usize, division: usize) -> KnotVec {
|
|
let mut vec = vec![0.0; degree + 1];
|
|
vec.extend((1..division).map(|i| (i as f64) / (division as f64)));
|
|
vec.extend(std::iter::repeat(1.0).take(degree + 1));
|
|
KnotVec(vec)
|
|
}
|
|
}
|
|
|
|
impl From<Vec<f64>> for KnotVec {
|
|
/// constructs from `Vec<f64>`. The vector will sorted by the order.
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let knot_vec = KnotVec::from(vec![1.0, 0.0, 3.0, 2.0]);
|
|
/// let arr : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(arr, vec![0.0, 1.0, 2.0, 3.0]);
|
|
/// ```
|
|
fn from(mut vec: Vec<f64>) -> KnotVec {
|
|
vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
|
KnotVec(vec)
|
|
}
|
|
}
|
|
|
|
impl From<&Vec<f64>> for KnotVec {
|
|
/// Constructs by the reference of vector. The clone of vector is sorted by the order.
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let knot_vec = KnotVec::from(&vec![1.0, 0.0, 3.0, 2.0]);
|
|
/// let arr : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(arr, vec![0.0, 1.0, 2.0, 3.0]);
|
|
/// ```
|
|
#[inline(always)]
|
|
fn from(vec: &Vec<f64>) -> KnotVec {
|
|
let mut copy_vec = vec.clone();
|
|
copy_vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
|
KnotVec(copy_vec)
|
|
}
|
|
}
|
|
|
|
impl From<KnotVec> for Vec<f64> {
|
|
/// `KnotVec` into `Vec<f64>`
|
|
/// ```
|
|
/// use truck_geometry::prelude::KnotVec;
|
|
/// let vec = vec![0.0, 1.0, 2.0, 3.0];
|
|
/// let knot_vec = KnotVec::from(&vec);
|
|
/// let vec0 : Vec<f64> = knot_vec.into();
|
|
/// assert_eq!(vec, vec0);
|
|
/// ```
|
|
#[inline(always)]
|
|
fn from(knotvec: KnotVec) -> Vec<f64> { knotvec.0 }
|
|
}
|
|
|
|
impl FromIterator<f64> for KnotVec {
|
|
#[inline(always)]
|
|
fn from_iter<I: IntoIterator<Item = f64>>(iter: I) -> KnotVec {
|
|
KnotVec::try_from(iter.into_iter().collect::<Vec<_>>()).unwrap()
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a KnotVec {
|
|
type Item = &'a f64;
|
|
type IntoIter = std::slice::Iter<'a, f64>;
|
|
#[inline(always)]
|
|
fn into_iter(self) -> Self::IntoIter { self.0.iter() }
|
|
}
|
|
|
|
impl std::ops::Deref for KnotVec {
|
|
type Target = Vec<f64>;
|
|
#[inline(always)]
|
|
fn deref(&self) -> &Vec<f64> { &self.0 }
|
|
}
|
|
|
|
impl AsRef<[f64]> for KnotVec {
|
|
#[inline(always)]
|
|
fn as_ref(&self) -> &[f64] { &self.0 }
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for KnotVec {
|
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
|
where D: serde::Deserializer<'de> {
|
|
let vec = Vec::<f64>::deserialize(deserializer)?;
|
|
Self::try_from(vec).map_err(serde::de::Error::custom)
|
|
}
|
|
}
|