Skip to content

Commit 3e9ab01

Browse files
committed
Merge branch 'feature/docs'
2 parents 6f2cf58 + dce95a2 commit 3e9ab01

27 files changed

+2395
-644
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ target/
55
# files
66
.DS_Store
77
**/*.rs.bk
8+
*.aux
9+
*.log
10+
*.gz
11+
*.pdf
812

913
#/target
1014
/Cargo.lock

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Bsplines
1+
# `bsplines` Rust Library
22

33
[![Crates.io](https://img.shields.io/crates/v/bsplines)](https://crates.io/crates/bsplines)
44
[![Docs.rs](https://docs.rs/bsplines/badge.svg)](https://docs.rs/bsplines)

doc-images/equations/_preamble.tex

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
\documentclass[tikz]{standalone}
2+
\usepackage{amssymb}
3+
\usepackage{amsmath}
4+
\usepackage{oubraces}
5+
\usepackage[customcolors]{hf-tikz}
6+
\definecolor{docsrscolor}{HTML}{f5f5f5}
7+
8+
\newcommand{\myeqs}[1]{
9+
\Huge
10+
\begin{tikzpicture}
11+
\node[
12+
fill=docsrscolor,
13+
rounded corners=8mm,
14+
inner sep=8mm
15+
]{$#1$};
16+
\end{tikzpicture}
17+
}

doc-images/equations/basis-function-zero.svg

Lines changed: 262 additions & 0 deletions
Loading
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
\input{_preamble}
2+
\begin{document}
3+
\myeqs{
4+
\mathcal{N}_{i,0}^{\boldsymbol{U}^{(k)}}(u)
5+
=
6+
\begin{cases}
7+
1, & u\in\left[u_i^{(k)},u_{i+1}^{(k)}\right)\,\lor\, (i=n-k \land u=1)\\
8+
0, & \text{else}
9+
\end{cases}
10+
}
11+
\end{document}

doc-images/equations/basis-function.svg

Lines changed: 309 additions & 0 deletions
Loading
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
\input{_preamble}
2+
\begin{document}
3+
\myeqs{
4+
\myeqs{
5+
\mathcal{N}_{i,p-k}^{\boldsymbol{U}^{(k)}}(u)
6+
= \mu_{i,p-k-1}^{(k)}(u)\, \mathcal{N}_{i,p-k-1}^{\boldsymbol{U}^{(k)}}(u)
7+
+ \left[1-\mu_{i+1,p-k-1}^{(k)}(u)\right]\, \mathcal{N}_{i+1,p-k-1}^{\boldsymbol{U}^{(k)}}(u)
8+
}
9+
}
10+
\end{document}

doc-images/equations/basis-prefactor.svg

Lines changed: 282 additions & 0 deletions
Loading
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
\input{_preamble}
2+
\begin{document}
3+
\myeqs{
4+
\mu_{g,h}^{(k)}(u)
5+
=
6+
\begin{cases}
7+
0 & \text{if}\quad u_{g+h+1}^{(k)} = u_{g}^{(k)}\\[2mm]
8+
\frac{u-u_g^{(k)}}{u_{g+h+1}^{(k)}-u_g^{(k)}} & \text{else}
9+
\end{cases}
10+
}
11+
\end{document}

doc-images/equations/control-points.svg

Lines changed: 388 additions & 0 deletions
Loading
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
\input{_preamble}
2+
\begin{document}
3+
\myeqs{
4+
\boldsymbol{P}_i^{(k)}=
5+
\begin{cases}
6+
\boldsymbol{P}_{i}^{(0)} & k=0 \\[2mm]
7+
\left.\begin{cases}
8+
\boldsymbol{0} & u_{i+p+1}^{{(0)}} = u_{i+k}^{{(0)}}\\[2mm]
9+
\frac{p-k+1}{u_{i+p+1}^{{(0)}} - u_{i+k}^{{(0)}}} \left( \boldsymbol{P}_{i+1}^{(k-1)} - \boldsymbol{P}_{i}^{(k-1)} \right) & \text{else}%u_{i+p+1} \neq u_{i+k}\\[2mm]
10+
\end{cases}\right|& k>0 \\
11+
\end{cases}
12+
}
13+
\end{document}

doc-images/equations/curve-deriv.tex

Lines changed: 0 additions & 13 deletions
This file was deleted.

doc-images/equations/curve-deriv.tex.svg

Lines changed: 0 additions & 268 deletions
This file was deleted.

doc-images/equations/curve.svg

Lines changed: 314 additions & 0 deletions
Loading

doc-images/equations/curve.tex

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
\documentclass[10pt]{article}
2-
\usepackage[usenames]{color}
3-
\usepackage{amssymb}
4-
\usepackage{amsmath}
5-
\usepackage{nicefrac}
6-
\definecolor{mygreen}{rgb}{0.454,0.824,0.208}
7-
\definecolor{myred}{rgb}{0.8,0.173,0.137}
8-
9-
\usepackage[utf8]{inputenc}
10-
\begin{equation}\nonumber
11-
\mathcal{C}(u) = \sum_{i=0}^{n} \mathcal{N}_{i,p}^{\boldsymbol{U}} (u)\, \boldsymbol{P}_i,\quad u \in [u_p,u_{n+1}]
12-
\end{equation}
1+
\input{_preamble}
2+
\begin{document}
3+
\myeqs{
4+
\mathcal{C}^{(k)}(u) = \frac{\partial^k\mathcal{C}^{(0)}(u)}{\partial u^k} = \sum_{i=0}^{n-k} \mathcal{N}_{i,p-k}^{\boldsymbol{U^{(k)}}} (u)\, \boldsymbol{P}^{(k)}_i,\quad
5+
u \in [u_{p-k},u_{n+1-k}],\quad
6+
n > p
7+
}
138
\end{document}

doc-images/equations/curve.tex.svg

Lines changed: 0 additions & 152 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
for file in *.tex; do
2+
if [ "$file" != "_preamble.tex" ]; then
3+
echo "Processing file: $file"
4+
pdflatex -shell-escape -synctex=1 "$file" | grep '^!.*' -A200
5+
pdf2svg "${file%.tex}.pdf" "${file%.tex}.svg"
6+
rm -f "${file%.tex}.aux" "${file%.tex}.log" "${file%.tex}.pdf" "${file%.tex}.pdf" "${file%.tex}.synctex.gz"
7+
else
8+
echo "Excluded file: $file"
9+
fi
10+
done

doc-images/equations/knots.svg

Lines changed: 474 additions & 0 deletions
Loading

doc-images/equations/knots.tex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
\input{_preamble}
2+
\begin{document}
3+
\myeqs{
4+
\boldsymbol{U}^{(k)}
5+
=\left\{u_i^{(k)}\right\}
6+
\equiv\overunderbraces{
7+
&&\br{5}{\text{domain}\vphantom{p}}
8+
}{
9+
\left\{\vphantom{u^{(k)}}\right.
10+
&u_{0}^{(k)},\dots,&u_{p-k}^{(k)}&,
11+
&u_{p-k+1}^{(k)}, \dots,u_{n-k}^{(k)}
12+
&,&u_{n+1-k}^{(k)}&,\dots,u_{n+p+1-2k}^{(k)}& \left.\vphantom{u^{(k)}}\right\}}
13+
{&\br{2}{p-k+1}&&\br{1}{n-p}&&\br{2}{1+p-k}},\quad
14+
u_{i}^{(k)}\leq u_{i+1}^{(k)},
15+
}
16+
\end{document}

src/curve/basis/mod.rs

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#![cfg_attr(feature = "doc-images",
2+
cfg_attr(all(),
3+
doc = ::embed_doc_image::embed_image!("eq-basis-function", "doc-images/equations/basis-function.svg"),
4+
doc = ::embed_doc_image::embed_image!("eq-basis-prefactor", "doc-images/equations/basis-prefactor.svg"),
5+
doc = ::embed_doc_image::embed_image!("eq-basis-function-zero", "doc-images/equations/basis-function-zero.svg")))]
6+
//! Evaluates the basis spline functions using the Cox-de Boor-Mansfield recurrence relation
7+
//!
8+
//! ![The Cox-de Boor-Mansfield recurrence relation][eq-basis-function]
9+
//!
10+
//! with the basis functions of degree `p = 0`
11+
//!
12+
//! ![Basis function of degree zero][eq-basis-function-zero]
13+
//!
14+
//! where the conditional `⋁ (i = n - k ⋀ u = U_{n+1-k)` closes the last interval
15+
//! and the pre-factors
16+
//!
17+
//! ![Pre-factors][eq-basis-prefactor]
18+
19+
use crate::types::VecD;
20+
21+
/// Evaluates the `i`-th basis spline function of degree `p`
22+
///
23+
/// ## Arguments
24+
///
25+
/// - `i` the index with `i ∈ {0, 1, ..., n}`
26+
/// - `p` the spline degree
27+
/// - `k` the derivative order
28+
/// - `U` the knot vector
29+
pub fn basis(Uk: &VecD, i: usize, p: usize, k: usize, n: usize, u: f64) -> f64 {
30+
if p == 0 {
31+
if (Uk[i] <= u && u < Uk[i + 1]) || (i == n - k && u == Uk[n + 1 - k]) {
32+
return 1.0;
33+
}
34+
return 0.0;
35+
}
36+
37+
let summand1 = if Uk[i + p] == Uk[i] {
38+
0.0
39+
} else {
40+
let g = i;
41+
let h = p - 1;
42+
(u - Uk[g]) / (Uk[g + h + 1] - Uk[g]) * basis(Uk, i, h, k, n, u)
43+
};
44+
45+
let summand2 = if Uk[i + 1 + p] == Uk[i + 1] {
46+
0.0
47+
} else {
48+
let g = i + 1;
49+
let h = p - 1;
50+
51+
// The following equation is numerically more stable than
52+
// `(1.0 - ((u - Uk[g]) / (Uk[g + h + 1] - Uk[g]))) * self.evaluate(k, g, h, u)`
53+
(Uk[g + p] - u) / (Uk[g + h + 1] - Uk[g]) * basis(Uk, g, h, k, n, u)
54+
};
55+
56+
summand1 + summand2
57+
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use approx::assert_relative_eq;
62+
use nalgebra::dvector;
63+
64+
use crate::curve::knots::Knots;
65+
66+
const SEGMENTS: usize = 4;
67+
68+
#[test]
69+
fn basis_func_degree3() {
70+
let k = 0;
71+
let p = 3;
72+
let knots = Knots::new(p, dvector![0., 0., 0., 0., 1. / 3., 2. / 3., 1., 1., 1., 1.]);
73+
74+
// Basis function i = 0
75+
let mut i = 0;
76+
assert_eq!(knots.evaluate(k, i, p, 0.0), 1.0);
77+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 1. / 8.);
78+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 0.0);
79+
assert_eq!(knots.evaluate(k, i, p, 1. / 2.), 0.0);
80+
assert_eq!(knots.evaluate(k, i, p, 2. / 3.), 0.0);
81+
assert_eq!(knots.evaluate(k, i, p, 5. / 6.), 0.0);
82+
assert_eq!(knots.evaluate(k, i, p, 1.), 0.0);
83+
84+
i = 1;
85+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
86+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 19. / 32.);
87+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 1. / 4.);
88+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 1. / 32., epsilon = f64::EPSILON.sqrt());
89+
assert_eq!(knots.evaluate(k, i, p, 2. / 3.), 0.0);
90+
assert_eq!(knots.evaluate(k, i, p, 5. / 6.), 0.0);
91+
assert_eq!(knots.evaluate(k, i, p, 1.), 0.0);
92+
93+
i = 2;
94+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
95+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 25. / 96.);
96+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 7. / 12.);
97+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 15. / 32., epsilon = f64::EPSILON.sqrt());
98+
assert_relative_eq!(knots.evaluate(k, i, p, 2. / 3.), 1. / 6., epsilon = f64::EPSILON.sqrt());
99+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 1. / 48., epsilon = f64::EPSILON.sqrt());
100+
assert_eq!(knots.evaluate(k, i, p, 1.0), 0.0);
101+
102+
i = 3;
103+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
104+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 1. / 48.);
105+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 1. / 6.);
106+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 15. / 32., epsilon = f64::EPSILON.sqrt());
107+
assert_relative_eq!(knots.evaluate(k, i, p, 2. / 3.), 7. / 12., epsilon = f64::EPSILON.sqrt());
108+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 25. / 96., epsilon = f64::EPSILON.sqrt());
109+
assert_eq!(knots.evaluate(k, i, p, 1.0), 0.0);
110+
111+
i = 4;
112+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
113+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 0.0);
114+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 0.0);
115+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 1. / 32., epsilon = f64::EPSILON.sqrt());
116+
assert_relative_eq!(knots.evaluate(k, i, p, 2. / 3.), 1. / 4., epsilon = f64::EPSILON.sqrt());
117+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 19. / 32., epsilon = f64::EPSILON.sqrt());
118+
assert_eq!(knots.evaluate(k, i, p, 1.0), 0.0);
119+
120+
i = 5;
121+
assert_eq!(knots.evaluate(k, i, p, 0.0), 0.0);
122+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 0.);
123+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 0.0);
124+
assert_eq!(knots.evaluate(k, i, p, 1. / 2.), 0.0);
125+
assert_eq!(knots.evaluate(k, i, p, 2. / 3.), 0.0);
126+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 1. / 8., epsilon = f64::EPSILON.sqrt());
127+
assert_eq!(knots.evaluate(k, i, p, 1.), 1.0);
128+
}
129+
130+
#[test]
131+
fn basis_func_degree4() {
132+
let k = 1;
133+
let p = 4;
134+
let knots = Knots::new(p, dvector![0., 0., 0., 0., 0., 1. / 3., 2. / 3., 1., 1., 1., 1., 1.]);
135+
136+
// Basis function i = 0
137+
let mut i = 0;
138+
assert_eq!(knots.evaluate(k, i, p, 0.0), 1.0);
139+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 1. / 8.);
140+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 0.0);
141+
assert_eq!(knots.evaluate(k, i, p, 1. / 2.), 0.0);
142+
assert_eq!(knots.evaluate(k, i, p, 2. / 3.), 0.0);
143+
assert_eq!(knots.evaluate(k, i, p, 5. / 6.), 0.0);
144+
assert_eq!(knots.evaluate(k, i, p, 1.), 0.0);
145+
146+
i = 1;
147+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
148+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 19. / 32.);
149+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 1. / 4.);
150+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 1. / 32., epsilon = f64::EPSILON.sqrt());
151+
assert_eq!(knots.evaluate(k, i, p, 2. / 3.), 0.0);
152+
assert_eq!(knots.evaluate(k, i, p, 5. / 6.), 0.0);
153+
assert_eq!(knots.evaluate(k, i, p, 1.), 0.0);
154+
155+
i = 2;
156+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
157+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 25. / 96.);
158+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 7. / 12.);
159+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 15. / 32., epsilon = f64::EPSILON.sqrt());
160+
assert_relative_eq!(knots.evaluate(k, i, p, 2. / 3.), 1. / 6., epsilon = f64::EPSILON.sqrt());
161+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 1. / 48., epsilon = f64::EPSILON.sqrt());
162+
assert_eq!(knots.evaluate(k, i, p, 1.0), 0.0);
163+
164+
i = 3;
165+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
166+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 1. / 48.);
167+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 1. / 6.);
168+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 15. / 32., epsilon = f64::EPSILON.sqrt());
169+
assert_relative_eq!(knots.evaluate(k, i, p, 2. / 3.), 7. / 12., epsilon = f64::EPSILON.sqrt());
170+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 25. / 96., epsilon = f64::EPSILON.sqrt());
171+
assert_eq!(knots.evaluate(k, i, p, 1.0), 0.0);
172+
173+
i = 4;
174+
assert_eq!(knots.evaluate(k, i, p, 0.), 0.0);
175+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 0.0);
176+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 0.0);
177+
assert_relative_eq!(knots.evaluate(k, i, p, 1. / 2.), 1. / 32., epsilon = f64::EPSILON.sqrt());
178+
assert_relative_eq!(knots.evaluate(k, i, p, 2. / 3.), 1. / 4., epsilon = f64::EPSILON.sqrt());
179+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 19. / 32., epsilon = f64::EPSILON.sqrt());
180+
assert_eq!(knots.evaluate(k, i, p, 1.0), 0.0);
181+
182+
i = 5;
183+
assert_eq!(knots.evaluate(k, i, p, 0.0), 0.0);
184+
assert_eq!(knots.evaluate(k, i, p, 1. / 6.), 0.);
185+
assert_eq!(knots.evaluate(k, i, p, 1. / 3.), 0.0);
186+
assert_eq!(knots.evaluate(k, i, p, 1. / 2.), 0.0);
187+
assert_eq!(knots.evaluate(k, i, p, 2. / 3.), 0.0);
188+
assert_relative_eq!(knots.evaluate(k, i, p, 5. / 6.), 1. / 8., epsilon = f64::EPSILON.sqrt());
189+
assert_eq!(knots.evaluate(k, i, p, 1.), 1.0);
190+
}
191+
}

0 commit comments

Comments
 (0)