Mathematical properties of B-spline
Introduction
B-spline is a mathematical object, and it has a lot of application(e.g. NURBS, IGA).
In this page, we'll explain the mathematical definition and property of B-spline with Julia code.
Before running the following code, do not forget importing the package:
using BasicBSpline
Notice
- A book "Geometric Modeling with Splines" by Elaine Cohen, Richard F. Riesenfeld, Gershon Elber is really recommended.
- Some of notations in this page are my original, but these are well-considered results.
Knot vector
A finite sequence
\[k = (k_1, \dots, k_l)\]
is called knot vector if the sequence is broad monotonic increase, i.e. $k_{i} \le k_{i+1}$.
[fig]
BasicBSpline.Knots
— MethodConstruct knot vector from given array.
\[k=(k_1,\dots,k_l)\]
Examples
julia> k = Knots([1,2,3])
Knots([1.0, 2.0, 3.0])
julia> k = Knots(1:3)
Knots([1.0, 2.0, 3.0])
BasicBSpline.Knots
— MethodConstruct knot vector from given real numbers.
Examples
julia> k = Knots(1,2,3)
Knots([1.0, 2.0, 3.0])
julia> k = Knots()
Knots([])
Base.length
— MethodLength of knot vector
Examples
julia> k = Knots(1,2,3,5);
julia> length(k)
4
We introduce additional operator $+$.
Base.:+
— MethodSum of knot vectors
\[\begin{aligned} k^{(1)}+k^{(2)} &=(k^{(1)}_1, \dots, k^{(1)}_{l^{(1)}}) + (k^{(2)}_1, \dots, k^{(2)}_{l^{(2)}}) \\ &=(\text{sort of union of} \ k^{(1)} \ \text{and} \ k^{(2)} \text{)} \end{aligned}\]
For example, $(1,2,3,5)+(4,5,8)=(1,2,3,4,5,5,8)$.
Examples
julia> k1 = Knots(1,2,3,5);
julia> k2 = Knots(4,5,8);
julia> k1 + k2
Knots([1.0, 2.0, 3.0, 4.0, 5.0, 5.0, 8.0])
And product operator $\cdot$.
Base.:*
— MethodProduct of integer and knot vector
\[\begin{aligned} m\cdot k&=\underbrace{k+\cdots+k}_{m} \end{aligned}\]
For example, $2\cdot (1,2,2,5)=(1,1,2,2,2,2,5,5)$.
Examples
julia> k = Knots(1,2,2,5);
julia> 2 * k
Knots([1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 5.0, 5.0])
Base.unique
— MethodUnique elements of knot vector.
\[\begin{aligned} \widehat{k} &=(\text{remove duplicates of} \ k) \\ \end{aligned}\]
For example, $\widehat{(1,2,2,3)}=(1,2,3)$.
Examples
julia> k = Knots([1,2,2,3]);
julia> unique(k)
Knots([1.0, 2.0, 3.0])
BasicBSpline.𝔫
— MethodFor Given knot vector $k$, the following function $\mathfrak{n}_k:\mathbb{R}\to\mathbb{Z}$ represents the number of knots that duplicate the knot vector $k$.
\[\mathfrak{n}_k(t) = \sharp\{i \mid k_i=t \}\]
For example, if $k=(1,2,2,3)$, then $\mathfrak{n}_k(0.3)=0$, $\mathfrak{n}_k(1)=1$, $\mathfrak{n}_k(2)=2$.
julia> k = Knots([1,2,2,3]);
julia> 𝔫(k,0.3)
0
julia> 𝔫(k,1.0)
1
julia> 𝔫(k,2.0)
2
B-spline space
Before defining B-spline space, we'll define polynomial space with degree $p$.
Polynomial space with degree $p$.
\[\mathcal{P}[p] =\left\{f:\mathbb{R}\to\mathbb{R}\ ;\ t\mapsto a_0+a_1t^1+\cdots+a_pt^p \ \left| \ % a_i\in \mathbb{R} \right. \right\}\]
This space $\mathcal{P}[p]$ is a $p+1$-dimensional linear space.
Note that $\{t\mapsto t^i\}_{0 \le i \le p}$ is a basis of $\mathcal{P}[p]$, and also the set of Bernstein polynomial $\{B_{(i,p)}\}_i$ is a basis of $\mathcal{P}[p]$.
\[\begin{aligned} B_{(i,p)}(t) &=\binom{p}{i-1}t^{i-1}(1-t)^{p-i+1} &(i=1, \dots, p+1) \end{aligned}\]
Where $\binom{p}{i-1}$ is a binomial coefficient.
For given polynomial degree $p\ge 0$ and knot vector $k=(k_1,\dots,k_l)$, B-spline space $\mathcal{P}[p,k]$ is defined as follows:
\[\mathcal{P}[p,k] =\left\{f:\mathbb{R}\to\mathbb{R} \ \left| \ % \begin{gathered} \operatorname{supp}(f)\subseteq [k_1, k_l] \\ \exists \tilde{f}\in\mathcal{P}[p], f|_{[k_{i}, k_{i+1})} = \tilde{f}|_{[k_{i}, k_{i+1})} \\ \forall t \in \mathbb{R}, \exists \delta > 0, f|_{(t-\delta,t+\delta)}\in C^{p-\mathfrak{n}_k(t)} \end{gathered} \right. \right\}\]
Note that a element of the space $\mathcal{P}[p,k]$ is piecewise polynomial.
[fig]
julia> using BasicBSpline
julia> p = 2
2
julia> k = Knots([1,3,5,6,8,9])
Knots([1.0, 3.0, 5.0, 6.0, 8.0, 9.0])
julia> BSplineSpace(p,k)
BSplineSpace(2, Knots([1.0, 3.0, 5.0, 6.0, 8.0, 9.0]))
A B-spline space is said to be proper if its degree and knots satisfies following property:
\[\begin{aligned} k_{i}&<k_{i+p+1} & (1 \le i \le l-p-1) \end{aligned}\]
julia> using BasicBSpline
julia> isproper(BSplineSpace(2,Knots([1,3,5,6,8,9]))) # true
true
julia> isproper(BSplineSpace(1,Knots([1,3,3,3,8,9]))) # false
false
The B-spline space is linear space, and if a B-spline space is proper, its dimension is calculated by:
\[\dim(\mathcal{P}[p,k])=\sharp k -p -1\]
julia> using BasicBSpline
julia> dim(BSplineSpace(2,Knots([1,3,5,6,8,9]))) # 3
3
B-spline basis function
B-spline basis function is defined by Cox–de Boor recursion formula.
\[\begin{aligned} {B}_{(i,p,k)}(t) &= \frac{t-k_{i}}{k_{i+p}-k_{i}}{B}_{(i,p-1,k)}(t) +\frac{k_{i+p+1}-t}{k_{i+p+1}-k_{i+1}}{B}_{(i+1,p-1,k)}(t) \\ {B}_{(i,0,k)}(t) &= \begin{cases} &1\quad (k_{i}\le t< k_{i+1})\\ &0\quad (\text{otherwise}) \end{cases} \end{aligned}\]
If the denominator is ..
The set of functions $\{B_{(i,p,k)}\}_i$ is a basis of B-spline space $\mathcal{P}[p,k]$.
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> p = 2
2
julia> k = Knots(1:8)
Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
julia> P = BSplineSpace(p,k)
BSplineSpace(2, Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]))
julia> plot([t->bsplinebasis₊₀(i,P,t) for i in 1:dim(P)], 1, 8, ylims=(0,1.05))
ERROR: UndefVarError: plot not defined
You can choose the first terms in different ways.
\[\begin{aligned} {B}_{(i,0,k)}(t) &= \begin{cases} &1\quad (k_{i} < t \le k_{i+1}) \\ &0\quad (\text{otherwise}) \end{cases} \end{aligned}\]
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> p = 2
2
julia> k = Knots(1:8)
Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
julia> P = BSplineSpace(p,k)
BSplineSpace(2, Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]))
julia> plot([t->bsplinebasis₋₀(i,P,t) for i in 1:dim(P)], 1, 8, ylims=(0,1.05))
ERROR: UndefVarError: plot not defined
Support of B-spline basis function
If a B-spline space$\mathcal{P}[p,k]$ is proper, the support of its basis function is calculated as follows:
\[\operatorname{supp}(B_{(i,p,k)})=[k_{i},k_{i+p+1}]\]
[fig]
julia> using BasicBSpline
julia> i = 2
2
julia> k = Knots([5,12,13,13,14])
Knots([5.0, 12.0, 13.0, 13.0, 14.0])
julia> p = 2
2
julia> P = BSplineSpace(p,k)
BSplineSpace(2, Knots([5.0, 12.0, 13.0, 13.0, 14.0]))
julia> bsplinesupport(P) # [5..13, 12..14]
2-element Array{IntervalSets.Interval{:closed,:closed,Float64},1}:
5.0..13.0
12.0..14.0
julia> bsplinesupport(i,P) # 12..14
12.0..14.0
Derivative of B-spline basis function
The derivative of B-spline basis function can be expressed as follows:
\[\begin{aligned} \dot{B}_{(i,p,k)}(t) &=\frac{d}{dt}B_{(i,p,k)}(t) \\ &=p\left(\frac{1}{k_{i+p}-k_{i}}B_{(i,p-1,k)}(t)-\frac{1}{k_{i+p+1}-k_{i+1}}B_{(i+1,p-1,k)}(t)\right) \end{aligned}\]
Note that $\dot{B}_{(i,p,k)}\in\mathcal{P}[p-1,k]$.
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> p = 2
2
julia> k = Knots(1:8)
Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
julia> P = BSplineSpace(p,k)
BSplineSpace(2, Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]))
julia> plot([t->bsplinebasis′₊₀(i,P,t) for i in 1:dim(P)], 1, 8, ylims=(0,1.05))
ERROR: UndefVarError: plot not defined
Partition of unity
\[\begin{aligned} \sum_{i}B_{(i,p,k)}(t) &= 1 & (k_{p+1} \le t < k_{l-p}) \\ 0 \le B_{(i,p,k)}(t) &\le 1 \end{aligned}\]
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> p = 2
2
julia> k = Knots(1:8)
Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
julia> P = BSplineSpace(p,k)
BSplineSpace(2, Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]))
julia> plot(t->sum(bsplinebasis₊₀(i,P,t) for i in 1:dim(P)), 1, 8, ylims=(0,1.05))
ERROR: UndefVarError: plot not defined
To satisfy the partition of unity on whole interval $[1,8]$, sometimes more knots will be inserted to the endpoints of the interval.
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> p = 2
2
julia> k = Knots(1:8) + p * Knots([1,8])
Knots([1.0, 1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 8.0])
julia> P = BSplineSpace(p,k)
BSplineSpace(2, Knots([1.0, 1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 8.0]))
julia> plot(t->sum(bsplinebasis₊₀(i,P,t) for i in 1:dim(P)), 1, 8, ylims=(0,1.05))
ERROR: UndefVarError: plot not defined
But, the sum $\sum_{i} B_{(i,p,k)}(t)$ is not equal to $1$ if $t=8$. Therefore, to satisfy partition of unity on closed interval $[k_{p+1}, k_{l-p}]$, the definition of first terms of B-spline basis functions are sometimes replaced:
\[\begin{aligned} {B}_{(i,0,k)}(t) &= \begin{cases} &1\quad (k_{i} \le t<k_{i+1})\\ &1\quad (k_{i} < t = k_{i+1}=k_{l})\\ &0\quad (\text{otherwise}) \end{cases} \end{aligned}\]
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> p = 2
2
julia> k = Knots(1:8) + p * Knots([1,8])
Knots([1.0, 1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 8.0])
julia> P = BSplineSpace(p,k)
BSplineSpace(2, Knots([1.0, 1.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 8.0, 8.0]))
julia> plot(t->sum(bsplinebasis(i,P,t) for i in 1:dim(P)), 1, 8, ylims=(0,1.05))
ERROR: UndefVarError: plot not defined
Inclusion relation between B-spline spaces
For proper B-spline spaces, the following relationship holds.
\[\mathcal{P}[p,k] \subseteq \mathcal{P}[p',k'] \Leftrightarrow (m=p'-p \ge 0 \ \text{and} \ k+m\widehat{k}\subseteq k')\]
(as linear subspace..)
julia> using BasicBSpline
julia> P1 = BSplineSpace(1,Knots([1,3,5,8]))
BSplineSpace(1, Knots([1.0, 3.0, 5.0, 8.0]))
julia> P2 = BSplineSpace(1,Knots([1,3,5,6,8,9]))
BSplineSpace(1, Knots([1.0, 3.0, 5.0, 6.0, 8.0, 9.0]))
julia> P3 = BSplineSpace(2,Knots([1,1,3,3,5,5,8,8]))
BSplineSpace(2, Knots([1.0, 1.0, 3.0, 3.0, 5.0, 5.0, 8.0, 8.0]))
julia> P1 ⊆ P2 # true
true
julia> P1 ⊆ P3 # true
true
julia> P2 ⊆ P3 # false
false
julia> P2 ⊈ P3 # true
true
Here are plots of the B-spline basis functions of the spaces P1
, P2
, P3
.
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> P1 = BSplineSpace(1,Knots([1,3,5,8]))
BSplineSpace(1, Knots([1.0, 3.0, 5.0, 8.0]))
julia> P2 = BSplineSpace(1,Knots([1,3,5,6,8,9]))
BSplineSpace(1, Knots([1.0, 3.0, 5.0, 6.0, 8.0, 9.0]))
julia> P3 = BSplineSpace(2,Knots([1,1,3,3,5,5,8,8]))
BSplineSpace(2, Knots([1.0, 1.0, 3.0, 3.0, 5.0, 5.0, 8.0, 8.0]))
julia> plot(
plot([t->bsplinebasis₊₀(i,P1,t) for i in 1:dim(P1)], 1, 9, ylims=(0,1.05), legend=false),
plot([t->bsplinebasis₊₀(i,P2,t) for i in 1:dim(P2)], 1, 9, ylims=(0,1.05), legend=false),
plot([t->bsplinebasis₊₀(i,P3,t) for i in 1:dim(P3)], 1, 9, ylims=(0,1.05), legend=false),
layout=(3,1),
link=:x
)
ERROR: UndefVarError: plot not defined
This means, there exists a $n \times n'$ matrix $A$ which holds:
\[\begin{aligned} B_{(i,p,k)} &=\sum_{j}A_{ij} B_{(j,p',k')} \\ n&=\dim(\mathcal{P}[p,k]) \\ n'&=\dim(\mathcal{P}[p',k']) \end{aligned}\]
You can calculate the change of basis matrix $A$ with changebasis
.
julia> using BasicBSpline
julia> A12 = changebasis(P1,P2)
ERROR: UndefVarError: P1 not defined
julia> A13 = changebasis(P1,P3)
ERROR: UndefVarError: P1 not defined
julia> using BasicBSpline
julia> using Plots
ERROR: ArgumentError: Package Plots not found in current path:
- Run `import Pkg; Pkg.add("Plots")` to install the Plots package.
julia> gr()
ERROR: UndefVarError: gr not defined
julia> plot(
plot([t->bsplinebasis₊₀(i,P1,t) for i in 1:dim(P1)], 1, 9, ylims=(0,1.05), legend=false),
plot([t->sum(A12[i,j]*bsplinebasis₊₀(j,P2,t) for j in 1:dim(P2)) for i in 1:dim(P1)], 1, 9, ylims=(0,1.05), legend=false),
plot([t->sum(A13[i,j]*bsplinebasis₊₀(j,P3,t) for j in 1:dim(P3)) for i in 1:dim(P1)], 1, 9, ylims=(0,1.05), legend=false),
layout=(3,1),
link=:x
)
ERROR: UndefVarError: P1 not defined
Multi-dimensional B-spline
tensor product
\[B_{i^1,\dots,i^d}(t^1,\dots,t^d) =B_{(i^1,p^1,k^1)}(t^1)\cdots B_{(i^d,p^d,k^d)}(t^d)\]
B-spline manifold
B-spline manifold is a parametric representation of a shape.
For given $d$-dimensional B-spline basis functions $B_{i^1,\dots,i^d}$ and given points $\bm{a}_{i^1,\dots,i^d} \in \mathbb{R}^{\hat{d}}$, B-spline manifold is defined by following equality:
\[\bm{p}(t^1,\dots,t^d;\bm{a}_{i^1,\dots,i^d}) =\sum_{i^1,\dots,i^d}B_{i^1,\dots,i^d}(t^1,\dots,t^d) \bm{a}_{i^1,\dots,i^d}\]
Where $\bm{a}_{i^1,\dots,i^d}$ are called control points.
We will also write $\bm{p}(t^1,\dots,t^d; \bm{a})$, $\bm{p}(t^1,\dots,t^d)$, $\bm{p}(t; \bm{a})$ or $\bm{p}(t)$ for simplicity.
julia> using BasicBSpline
julia> P1 = BSplineSpace(1,Knots([0,0,1,1]))
BSplineSpace(1, Knots([0.0, 0.0, 1.0, 1.0]))
julia> P2 = BSplineSpace(1,Knots([1,1,2,3,3]))
BSplineSpace(1, Knots([1.0, 1.0, 2.0, 3.0, 3.0]))
julia> n1 = dim(P1) # 2
2
julia> n2 = dim(P2) # 3
3
julia> a = [[i, j] for i in 1:n1, j in 1:n2] # n1 × n2 array of d̂ array.
2×3 Array{Array{Int64,1},2}:
[1, 1] [1, 2] [1, 3]
[2, 1] [2, 2] [2, 3]
julia> M = BSplineManifold([P1, P2], a)
BSplineManifold(BSplineSpace[BSplineSpace(1, Knots([0.0, 0.0, 1.0, 1.0])), BSplineSpace(1, Knots([1.0, 1.0, 2.0, 3.0, 3.0]))], [1.0 1.0 1.0; 2.0 2.0 2.0]
[1.0 2.0 3.0; 1.0 2.0 3.0])
B-spline curve
julia> using BasicBSpline
julia> ## 1-dim B-spline manifold
p = 2 # degree of polynomial
2
julia> k = Knots(1:12) # knot vector
Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0])
julia> P = FastBSplineSpace(p, k) # B-spline space
FastBSplineSpace{2}(Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0]))
julia> a = [[i-5, 3*sin(i^2)] for i in 1:dim(P)] # control points
9-element Array{Array{Float64,1},1}:
[-4.0, 2.5244129544236893]
[-3.0, -2.2704074859237844]
[-2.0, 1.2363554557252698]
[-1.0, -0.8637099499951959]
[0.0, -0.3970552502933191]
[1.0, -2.9753365603293473]
[2.0, -2.8612579582784154]
[3.0, 2.7600781145903723]
[4.0, -1.8896639828233615]
julia> M = BSplineCurve([P], a) # Define B-spline manifold
BSplineCurve{2}(FastBSplineSpace{2}(Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0])), [-4.0 2.5244129544236893; -3.0 -2.2704074859237844; … ; 3.0 2.7600781145903723; 4.0 -1.8896639828233615])
julia> save_png("1dim.png", M, unitlength = 50)
ERROR: UndefVarError: save_png not defined
B-spline surface
julia> using BasicBSpline
julia> p = 2 # degree of polynomial
2
julia> k = Knots(1:8) # knot vector
Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
julia> P = BSplineSpace(p,k) # B-spline space
BSplineSpace(2, Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]))
julia> rand_a = [rand(2) for i in 1:dim(P), j in 1:dim(P)]
5×5 Array{Array{Float64,1},2}:
[0.665467, 0.950264] [0.13225, 0.108266] … [0.433038, 0.870414]
[0.265854, 0.641049] [0.727666, 0.783977] [0.956978, 0.0559619]
[0.487372, 0.0712722] [0.303491, 0.40227] [0.915077, 0.562117]
[0.575628, 0.364919] [0.859327, 0.863418] [0.656645, 0.840722]
[0.971151, 0.931314] [0.0157416, 0.582234] [0.277908, 0.00702128]
julia> a = [[2*i-6.5,2*j-6.5] for i in 1:dim(P), j in 1:dim(P)] + rand_a # random generated control points
5×5 Array{Array{Float64,1},2}:
[-3.83453, -3.54974] [-4.36775, -2.39173] … [-4.06696, 4.37041]
[-2.23415, -3.85895] [-1.77233, -1.71602] [-1.54302, 3.55596]
[-0.0126278, -4.42873] [-0.196509, -2.09773] [0.415077, 4.06212]
[2.07563, -4.13508] [2.35933, -1.63658] [2.15664, 4.34072]
[4.47115, -3.56869] [3.51574, -1.91777] [3.77791, 3.50702]
julia> M = BSplineManifold([P,P],a) # Define B-spline manifold
BSplineManifold(BSplineSpace[BSplineSpace(2, Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])), BSplineSpace(2, Knots([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]))], [-3.834533400557688 -4.36775027228349 … -3.975774735503546 -4.066962024279169; -2.2341463059667532 -1.7723336730318717 … -1.6923919003861327 -1.5430217595069884; … ; 2.0756281664078067 2.359326782719653 … 1.6569628078817478 2.1566446051968806; 4.471151295594472 3.5157415510318497 … 3.831208790528937 3.77790807651739]
[-3.549735800093252 -2.391734100762558 … 1.710260602396351 4.370413648179999; -3.858951104958521 -1.716022664351171 … 1.5773959499107877 3.555961947446934; … ; -4.135080818325989 -1.6365815393723342 … 1.9573560331038744 4.3407215159519295; -3.568685796753395 -1.917766050745985 … 2.309555039549645 3.507021277798343])
julia> save_png("2dim.png", M) # save image
ERROR: UndefVarError: save_png not defined
julia> using BasicBSpline
julia> points = [M([u,v]) for u in range(3.0,6.0,length=50), v in range(3.0,6.0,length=50)]
ERROR: UndefVarError: M not defined
julia> X = [point[1] for point in points]
ERROR: UndefVarError: points not defined
julia> Y = [point[2] for point in points]
ERROR: UndefVarError: points not defined
julia> scene = Scene(resolution=(1000,1000))
ERROR: UndefVarError: Scene not defined
julia> Makie.surface!(X,Y)
ERROR: UndefVarError: Makie not defined
julia> Makie.xlims!(-5,5)
ERROR: UndefVarError: Makie not defined
julia> Makie.ylims!(-5,5)
ERROR: UndefVarError: Makie not defined
julia> save("2dim_makie.png", scene)
ERROR: UndefVarError: save not defined
Affine commutativity
If $T$ is a affine transform $\mathbb{R}^d\to\mathbb{R}^d$, then the following equality holds.
\[T(\bm{p}(t; \bm{a})) =\bm{p}(t; T(\bm{a}))\]
Refinement
h-refinemnet
Insert additional knots to knot vector.
julia> using BasicBSpline
julia> k₊=[Knots(3.3,4.2),Knots(3.8,3.2,5.3)] # additional knots
2-element Array{Knots,1}:
Knots([3.3, 4.2])
Knots([3.2, 3.8, 5.3])
julia> M′ = refinement(M,k₊=k₊) # refinement of B-spline manifold
ERROR: UndefVarError: M not defined
julia> save_png("2dim_h-refinement.png", M′) # save image
ERROR: UndefVarError: save_png not defined
Note that this shape and the last shape are identical.
p-refinemnet
Increase the polynomial degree of B-spline manifold.
julia> using BasicBSpline
julia> p₊=[1,2] # additional degrees
2-element Array{Int64,1}:
1
2
julia> M′ = refinement(M,p₊=p₊) # refinement of B-spline manifold
ERROR: UndefVarError: M not defined
julia> save_png("2dim_p-refinement.png", M′) # save image
ERROR: UndefVarError: save_png not defined
Note that this shape and the last shape are identical.
Fitting
Least squares method.
Try on Desmos graphing graphing calculator!
julia> using BasicBSpline
julia> p1 = 2
2
julia> p2 = 2
2
julia> k1 = Knots(-10:10)+p1*Knots(-10,10)
Knots([-10.0, -10.0, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 10.0, 10.0])
julia> k2 = Knots(-10:10)+p2*Knots(-10,10)
Knots([-10.0, -10.0, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 10.0, 10.0])
julia> P1 = FastBSplineSpace(p1, k1)
FastBSplineSpace{2}(Knots([-10.0, -10.0, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 10.0, 10.0]))
julia> P2 = FastBSplineSpace(p2, k2)
FastBSplineSpace{2}(Knots([-10.0, -10.0, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 10.0, 10.0]))
julia> f(u1, u2) = [2u1+sin(u1)+cos(u2)+u2/2, 3u2+sin(u2)+sin(u1)/2+u1^2/6]/5
f (generic function with 1 method)
julia> a = fittingcontrolpoints(f, P1, P2)
22×22 Array{Array{Float64,1},2}:
[-5.05588, -2.50058] [-5.06536, -2.29557] … [-3.05588, 9.27797]
[-4.95087, -2.88141] [-4.96035, -2.6764] [-2.95087, 8.89714]
[-4.74724, -3.57959] [-4.75671, -3.37459] [-2.74724, 8.19896]
[-4.37961, -4.12911] [-4.38909, -3.92411] [-2.37961, 7.64944]
[-3.81531, -4.51363] [-3.82479, -4.30862] [-1.81531, 7.26492]
[-3.20658, -4.80927] [-3.21606, -4.60426] … [-1.20658, 6.96928]
[-2.74482, -5.11172] [-2.7543, -4.90671] [-0.744824, 6.66683]
[-2.48703, -5.44949] [-2.49651, -5.24448] [-0.487032, 6.32906]
[-2.30237, -5.75716] [-2.31185, -5.55215] [-0.302368, 6.02139]
[-1.99289, -5.93575] [-2.00237, -5.73075] [0.00710538, 5.84279]
⋮ ⋱
[-0.0308372, -5.62139] [-0.0403156, -5.41639] [1.96916, 6.15716]
[0.153827, -5.52906] [0.144348, -5.32405] [2.15383, 6.24949]
[0.411619, -5.3335] [0.40214, -5.12849] … [2.41162, 6.44505]
[0.87338, -4.96928] [0.863901, -4.76428] [2.87338, 6.80927]
[1.48211, -4.46492] [1.47263, -4.25992] [3.48211, 7.31363]
[2.04641, -3.9161] [2.03693, -3.7111] [4.04641, 7.86245]
[2.41403, -3.39896] [2.40455, -3.19395] [4.41403, 8.37959]
[2.61767, -2.89714] [2.60819, -2.69213] … [4.61767, 8.88141]
[2.72267, -2.6113] [2.71319, -2.4063] [4.72267, 9.16724]
julia> M = BSplineManifold([P1,P2],a)
BSplineManifold(BSplineSpace[BSplineSpace(2, Knots([-10.0, -10.0, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 10.0, 10.0])), BSplineSpace(2, Knots([-10.0, -10.0, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 10.0, 10.0]))], [-5.0558768288980005 -5.06535522394003 … -3.165355223940022 -3.055876828897993; -4.950871885089599 -4.960350280131599 … -3.060350280131626 -2.950871885089592; … ; 2.6176667013971398 2.6081883063551174 … 4.508188306355132 4.617666701397136; 2.722671645205536 2.7131932501634983 … 4.6131932501635 4.7226716452055335]
[-2.5005780222443037 -2.295573078435923 … 9.072965508050826 9.277970451859208; -2.881408883673448 -2.676403939865049 … 8.692134646621662 8.897139590430095; … ; -2.897139590430078 -2.692134646621685 … 8.676403939865049 8.881408883673444; -2.6113037851925505 -2.4062988413841615 … 8.962239745102586 9.16724468891097])
julia> save_png("fitting.png", M, unitlength=50, up=10, down=-10, left=-10, right=10)
ERROR: UndefVarError: save_png not defined