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

Def. 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.KnotsMethod

Construct 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])
source
BasicBSpline.KnotsMethod

Construct knot vector from given real numbers.

Examples

julia> k = Knots(1,2,3)
Knots([1.0, 2.0, 3.0])

julia> k = Knots()
Knots([])
source
Base.lengthMethod

Length of knot vector

Examples

julia> k = Knots(1,2,3,5);

julia> length(k)
4
source

We introduce additional operator $+$.

Base.:+Method

Sum 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])
source

And product operator $\cdot$.

Base.:*Method

Product 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])
source
Base.uniqueMethod

Unique 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])
source
BasicBSpline.𝔫Method

For 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
source

B-spline space

Before defining B-spline space, we'll define polynomial space with degree $p$.

Def. Polynomial space

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.

Def. B-spline space

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

Def. B-spline space

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 ..

Thm. Basis of B-spline space

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

Thm. 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

Thm. 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

Thm. 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

Thm. Support of B-spline basis function

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.

Def. B-spline manifold

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

Thm. 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