use std::iter;
use std::ptr;
use na;
use super::{TriMesh, Polyline};
use math::{Point, Vect};
#[doc(hidden)]
pub fn bezier_curve_at<P>(control_points: &[P], t: <P::Vect as Vect>::Scalar, cache: &mut Vec<P>) -> P
where P: Point {
if control_points.len() > cache.len() {
let diff = control_points.len() - cache.len();
cache.extend(iter::repeat(na::orig::<P>()).take(diff))
}
let cache = &mut cache[..];
let _1: <P::Vect as Vect>::Scalar = na::cast(1.0);
let t_1 = _1 - t;
unsafe {
ptr::copy_nonoverlapping(control_points.as_ptr(), cache.as_mut_ptr(), control_points.len());
}
for i in 1usize .. control_points.len() {
for j in 0usize .. control_points.len() - i {
cache[j] = cache[j] * t_1 + *cache[j + 1].as_vec() * t;
}
}
cache[0].clone()
}
#[doc(hidden)]
pub fn bezier_surface_at<P>(
control_points: &[P],
nupoints: usize,
nvpoints: usize,
u: <P::Vect as Vect>::Scalar,
v: <P::Vect as Vect>::Scalar,
ucache: &mut Vec<P>,
vcache: &mut Vec<P>)
-> P
where P: Point {
if vcache.len() < nvpoints {
let diff = nvpoints - vcache.len();
vcache.extend(iter::repeat(na::orig::<P>()).take(diff));
}
let vcache = &mut vcache[..];
for i in 0 .. nvpoints {
let start = i * nupoints;
let end = start + nupoints;
vcache[i] = bezier_curve_at(&control_points[start .. end], u, ucache);
}
bezier_curve_at(&vcache[0 .. nvpoints], v, ucache)
}
pub fn bezier_curve<P>(control_points: &[P], nsubdivs: usize) -> Polyline<P>
where P: Point {
let mut coords = Vec::with_capacity(nsubdivs);
let mut cache = Vec::new();
let tstep = na::cast(1.0 / (nsubdivs as f64));
let mut t = na::zero::<<P::Vect as Vect>::Scalar>();
while t <= na::one() {
coords.push(bezier_curve_at(control_points, t, &mut cache));
t = t + tstep;
}
Polyline::new(coords, None)
}
pub fn bezier_surface<P>(
control_points: &[P],
nupoints: usize,
nvpoints: usize,
usubdivs: usize,
vsubdivs: usize)
-> TriMesh<P>
where P: Point {
assert!(nupoints * nvpoints == control_points.len());
let mut surface = super::unit_quad::<P>(usubdivs, vsubdivs);
{
let uvs = &surface.uvs.as_ref().unwrap()[..];
let coords = &mut surface.coords[..];
let mut ucache = Vec::new();
let mut vcache = Vec::new();
for j in 0 .. vsubdivs + 1 {
for i in 0 .. usubdivs + 1 {
let id = i + j * (usubdivs + 1);
coords[id] = bezier_surface_at(control_points,
nupoints,
nvpoints,
uvs[id].x,
uvs[id].y,
&mut ucache,
&mut vcache)
}
}
surface.normals = None;
}
surface
}