ncollide3d/procedural/
bezier.rs

1#[cfg(feature = "dim3")]
2use super::TriMesh;
3use crate::math::Point;
4use na::{self, RealField};
5use std::iter;
6use std::ptr;
7
8// De-Casteljau algorithm.
9// Evaluates the bezier curve with control points `control_points`.
10#[doc(hidden)]
11pub fn bezier_curve_at<N: RealField + Copy>(
12    control_points: &[Point<N>],
13    t: N,
14    cache: &mut Vec<Point<N>>,
15) -> Point<N> {
16    if control_points.len() > cache.len() {
17        let diff = control_points.len() - cache.len();
18        cache.extend(iter::repeat(Point::origin()).take(diff))
19    }
20
21    let cache = &mut cache[..];
22
23    let _1: N = na::convert(1.0);
24    let t_1 = _1 - t;
25
26    // XXX: not good if the objects are not POD.
27    unsafe {
28        ptr::copy_nonoverlapping(
29            control_points.as_ptr(),
30            cache.as_mut_ptr(),
31            control_points.len(),
32        );
33    }
34
35    for i in 1usize..control_points.len() {
36        for j in 0usize..control_points.len() - i {
37            cache[j] = cache[j] * t_1 + cache[j + 1].coords * t;
38        }
39    }
40
41    cache[0].clone()
42}
43
44// Evaluates the bezier curve with control points `control_points`.
45#[cfg(feature = "dim3")]
46#[doc(hidden)]
47pub fn bezier_surface_at<N: RealField + Copy>(
48    control_points: &[Point<N>],
49    nupoints: usize,
50    nvpoints: usize,
51    u: N,
52    v: N,
53    ucache: &mut Vec<Point<N>>,
54    vcache: &mut Vec<Point<N>>,
55) -> Point<N>
56where
57    N: RealField + Copy,
58{
59    if vcache.len() < nvpoints {
60        let diff = nvpoints - vcache.len();
61        vcache.extend(iter::repeat(Point::origin()).take(diff));
62    }
63
64    // FIXME: start with u or v, depending on which dimension has more control points.
65    let vcache = &mut vcache[..];
66
67    for i in 0..nvpoints {
68        let start = i * nupoints;
69        let end = start + nupoints;
70
71        vcache[i] = bezier_curve_at(&control_points[start..end], u, ucache);
72    }
73
74    bezier_curve_at(&vcache[0..nvpoints], v, ucache)
75}
76
77/// Given a set of control points, generates a (non-rational) Bezier curve.
78pub fn bezier_curve<N: RealField + Copy>(
79    control_points: &[Point<N>],
80    nsubdivs: usize,
81) -> Vec<Point<N>> {
82    let mut coords = Vec::with_capacity(nsubdivs);
83    let mut cache = Vec::new();
84    let tstep = na::convert(1.0 / (nsubdivs as f64));
85    let mut t = na::zero::<N>();
86
87    while t <= na::one() {
88        coords.push(bezier_curve_at(control_points, t, &mut cache));
89        t = t + tstep;
90    }
91
92    coords
93}
94
95/// Given a set of control points, generates a (non-rational) Bezier surface.
96#[cfg(feature = "dim3")]
97pub fn bezier_surface<N: RealField + Copy>(
98    control_points: &[Point<N>],
99    nupoints: usize,
100    nvpoints: usize,
101    usubdivs: usize,
102    vsubdivs: usize,
103) -> TriMesh<N>
104where
105    N: RealField + Copy,
106{
107    assert!(nupoints * nvpoints == control_points.len());
108
109    let mut surface = super::unit_quad(usubdivs, vsubdivs);
110
111    {
112        let uvs = &surface.uvs.as_ref().unwrap()[..];
113        let coords = &mut surface.coords[..];
114
115        let mut ucache = Vec::new();
116        let mut vcache = Vec::new();
117
118        for j in 0..vsubdivs + 1 {
119            for i in 0..usubdivs + 1 {
120                let id = i + j * (usubdivs + 1);
121                coords[id] = bezier_surface_at(
122                    control_points,
123                    nupoints,
124                    nvpoints,
125                    uvs[id].x,
126                    uvs[id].y,
127                    &mut ucache,
128                    &mut vcache,
129                )
130            }
131        }
132
133        // XXX: compute the normals manually.
134        surface.normals = None;
135    }
136
137    surface
138}