diff --git a/README.md b/README.md index 33bc6c5fa..4da957aee 100644 --- a/README.md +++ b/README.md @@ -38,16 +38,16 @@ If you have any thoughts, please comment in: * [JuliaPackageComparisons](https://juliapackagecomparisons.github.io/pages/bspline/) ## Installation -Install this package +Install this package via Julia REPL's package mode. -```julia +``` ]add BasicBSpline ``` ## Quick start ### B-spline basis function -The value of B-spline basis function $B_{(i,p,k)}$ can be obtained with `bsplinebasis₊₀`. ([example in Desmos](https://www.desmos.com/calculator/ql6jqgdabs)) +The value of B-spline basis function $B_{(i,p,k)}$ can be obtained with `bsplinebasis₊₀`. $$ \begin{aligned} @@ -64,6 +64,19 @@ $$ \end{aligned} $$ +```julia +julia> using BasicBSpline + +julia> P3 = BSplineSpace{3}(KnotVector([0.0, 1.5, 2.5, 5.5, 8.0, 9.0, 9.5, 10.0])) +BSplineSpace{3, Float64, KnotVector{Float64}}(KnotVector([0.0, 1.5, 2.5, 5.5, 8.0, 9.0, 9.5, 10.0])) + +julia> bsplinebasis₊₀(P3, 2, 7.5) +0.13786213786213783 +``` + +BasicBSpline.jl has many recipes based on [RecipesBase.jl](https://docs.juliaplots.org/dev/RecipesBase/), and `BSplineSpace` object can be visualized with its basis functions. +([Try B-spline basis functions in Desmos](https://www.desmos.com/calculator/ql6jqgdabs)) + ```julia using BasicBSpline using Plots @@ -73,15 +86,16 @@ P0 = BSplineSpace{0}(k) # 0th degree piecewise polynomial space P1 = BSplineSpace{1}(k) # 1st degree piecewise polynomial space P2 = BSplineSpace{2}(k) # 2nd degree piecewise polynomial space P3 = BSplineSpace{3}(k) # 3rd degree piecewise polynomial space + +gr() plot( - plot([t->bsplinebasis₊₀(P0,i,t) for i in 1:dim(P0)], 0, 10, ylims=(0,1), legend=false), - plot([t->bsplinebasis₊₀(P1,i,t) for i in 1:dim(P1)], 0, 10, ylims=(0,1), legend=false), - plot([t->bsplinebasis₊₀(P2,i,t) for i in 1:dim(P2)], 0, 10, ylims=(0,1), legend=false), - plot([t->bsplinebasis₊₀(P3,i,t) for i in 1:dim(P3)], 0, 10, ylims=(0,1), legend=false), + plot(P0; ylims=(0,1), label="P0"), + plot(P1; ylims=(0,1), label="P1"), + plot(P2; ylims=(0,1), label="P2"), + plot(P3; ylims=(0,1), label="P3"), layout=(2,2), ) ``` - ![](docs/src/img/cover.png) You can visualize the differentiability of B-spline basis function. See [Differentiability and knot duplications](https://hyrodium.github.io/BasicBSpline.jl/dev/math/bsplinebasis/#Differentiability-and-knot-duplications) for details. @@ -91,45 +105,78 @@ https://github.com/hyrodium/BasicBSpline.jl/assets/7488140/034cf6d0-62ea-44e0-a0 ### B-spline manifold ```julia using BasicBSpline -using BasicBSplineExporter using StaticArrays +using Plots +## 1-dim B-spline manifold p = 2 # degree of polynomial -k1 = KnotVector(1:8) # knot vector -k2 = KnotVector(rand(7))+(p+1)*KnotVector([1]) -P1 = BSplineSpace{p}(k1) # B-spline space -P2 = BSplineSpace{p}(k2) -n1 = dim(P1) # dimension of B-spline space -n2 = dim(P2) -a = [SVector(2i-6.5+rand(),1.5j-6.5+rand()) for i in 1:dim(P1), j in 1:dim(P2)] # random generated control points -M = BSplineManifold(a,(P1,P2)) # Define B-spline manifold -save_png("2dim.png", M) # save image +k = KnotVector(1:12) # knot vector +P = BSplineSpace{p}(k) # B-spline space +a = [SVector(i-5, 3*sin(i^2)) for i in 1:dim(P)] # control points +M = BSplineManifold(a, P) # Define B-spline manifold +gr(); plot(M) ``` -![](docs/src/img/2dim.png) +![](docs/src/img/bspline_curve.png) + +### Rational B-spline manifold (NURBS) + +```julia +using BasicBSpline +using LinearAlgebra +using StaticArrays +using Plots +plotly() + +R1 = 3 # major radius of torus +R2 = 1 # minor radius of torus + +p = 2 +k = KnotVector([0,0,0,1,1,2,2,3,3,4,4,4]) +P = BSplineSpace{p}(k) +a = [normalize(SVector(cosd(t), sind(t)), Inf) for t in 0:45:360] +w = [ifelse(isodd(i), √2, 1) for i in 1:9] + +a0 = push.(a, 0) +a1 = (R1+R2)*a0 +a5 = (R1-R2)*a0 +a2 = [p+R2*SVector(0,0,1) for p in a1] +a3 = [p+R2*SVector(0,0,1) for p in R1*a0] +a4 = [p+R2*SVector(0,0,1) for p in a5] +a6 = [p-R2*SVector(0,0,1) for p in a5] +a7 = [p-R2*SVector(0,0,1) for p in R1*a0] +a8 = [p-R2*SVector(0,0,1) for p in a1] + +M = RationalBSplineManifold(hcat(a1,a2,a3,a4,a5,a6,a7,a8,a1), w*w', P, P) +plot(M; controlpoints=(markersize=2,)) +``` +![](docs/src/img/rational_bspline_surface_plotly.png) ### Refinement #### h-refinement ```julia -k₊=(KnotVector([3.3,4.2]),KnotVector([0.3,0.5])) # additional knot vectors +k₊ = (KnotVector([3.1, 3.2, 3.3]), KnotVector([0.5, 0.8, 0.9])) # additional knot vectors M_h = refinement(M, k₊) # refinement of B-spline manifold -save_png("2dim_h-refinement.png", M_h) # save image +plot(M_h; controlpoints=(markersize=2,)) ``` -![](docs/src/img/2dim_h-refinement.png) +![](docs/src/img/rational_bspline_surface_href_plotly.png) Note that this shape and the last shape are equivalent. #### p-refinement ```julia -p₊=(Val(1),Val(2)) # additional degrees +p₊ = (Val(1), Val(2)) # additional degrees M_p = refinement(M, p₊) # refinement of B-spline manifold -save_png("2dim_p-refinement.png", M_p) # save image +plot(M_p; controlpoints=(markersize=2,)) ``` -![](docs/src/img/2dim_p-refinement.png) +![](docs/src/img/rational_bspline_surface_pref_plotly.png) Note that this shape and the last shape are equivalent. ### Fitting B-spline manifold -[Try on Desmos graphing calculator!](https://www.desmos.com/calculator/2hm3b1fbdf) +The next example shows the fitting for [the following graph on Desmos graphing calculator](https://www.desmos.com/calculator/2hm3b1fbdf)! + +![](docs/src/img/fitting_desmos.png) + ```julia using BasicBSplineFitting @@ -144,9 +191,9 @@ f(u1, u2) = SVector(2u1 + sin(u1) + cos(u2) + u2 / 2, 3u2 + sin(u2) + sin(u1) / a = fittingcontrolpoints(f, (P1, P2)) M = BSplineManifold(a, (P1, P2)) -save_png("fitting.png", M, unitlength=50, xlims=(-10,10), ylims=(-10,10)) +gr() +plot(M; aspectratio=1) ``` -![](docs/src/img/fitting_desmos.png) ![](docs/src/img/fitting.png) If the knot vector span is too coarse, the approximation will be coarse. @@ -162,22 +209,25 @@ f(u1, u2) = SVector(2u1 + sin(u1) + cos(u2) + u2 / 2, 3u2 + sin(u2) + sin(u1) / a = fittingcontrolpoints(f, (P1, P2)) M = BSplineManifold(a, (P1, P2)) -save_png("fitting_coarse.png", M, unitlength=50, xlims=(-10,10), ylims=(-10,10)) +plot(M; aspectratio=1) ``` ![](docs/src/img/fitting_coarse.png) ### Draw smooth vector graphics ```julia +using BasicBSpline +using BasicBSplineFitting +using BasicBSplineExporter p = 3 -k = KnotVector(range(-2π,2π,length=8))+p*KnotVector(-2π,2π) +k = KnotVector(range(-2π,2π,length=8))+p*KnotVector([-2π,2π]) P = BSplineSpace{p}(k) f(u) = SVector(u,sin(u)) a = fittingcontrolpoints(f, P) M = BSplineManifold(a, P) -save_svg("sine-curve.svg", M, unitlength=50, xlims=(-2,2), ylims=(-8,8)) -save_svg("sine-curve_no-points.svg", M, unitlength=50, xlims=(-2,2), ylims=(-8,8), points=false) +save_svg("sine-curve.svg", M, unitlength=50, xlims=(-8,8), ylims=(-2,2)) +save_svg("sine-curve_no-points.svg", M, unitlength=50, xlims=(-8,8), ylims=(-2,2), points=false) ``` ![](docs/src/img/sine-curve.svg) ![](docs/src/img/sine-curve_no-points.svg) diff --git a/docs/make.jl b/docs/make.jl index 4f7536a81..8aabd7d80 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -23,10 +23,16 @@ function generate_indexmd_from_readmemd() # generate text for index.md text_index = text_readme - text_index = replace(text_index, "![](docs/src/img" => "![](img") text_index = replace(text_index, r"\$\$((.|\n)*?)\$\$" => s"```math\1```") text_index = replace(text_index, "(https://hyrodium.github.io/BasicBSpline.jl/dev/math/bsplinebasis/#Differentiability-and-knot-duplications)" => "(@ref differentiability-and-knot-duplications)") text_index = replace(text_index, r"https://github.com/hyrodium/BasicBSpline\.jl/assets/.*" => "![](math/differentiability.mp4)") + text_index = replace(text_index, "```julia\njulia>" => "```julia-repl\njulia>") + text_index = replace(text_index, "```julia\n" => "```@example readme\n") + text_index = replace(text_index, r"```\n!\[\]\(docs/src/img/(.*?)plotly\.png\)" => s"savefig(\"readme-\1plotly.html\") # hide\nnothing # hide\n```\n```@raw html\n\n```") + text_index = replace(text_index, r"```\n!\[\]\(docs/src/img/(.*?)\.png\)" => s"savefig(\"readme-\1.png\") # hide\nnothing # hide\n```\n![](readme-\1.png)") + text_index = replace(text_index, "![](docs/src/img" => "![](img") + text_index = replace(text_index, "![](img/sine-curve.svg)" => "![](sine-curve.svg)") + text_index = replace(text_index, "![](img/sine-curve_no-points.svg)" => "![](sine-curve_no-points.svg)") text_index = """ ```@meta EditURL = "https://github.com/hyrodium/BasicBSpline.jl/blob/main/README.md" diff --git a/docs/src/api.md b/docs/src/api.md index 5e65c8806..0f6761878 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -1,57 +1,13 @@ # API -# Public -```@docs -KnotVector -UniformKnotVector -SubKnotVector -EmptyKnotVector -AbstractKnotVector -length(k::AbstractKnotVector) -Base.:+(k1::KnotVector{T}, k2::KnotVector{T}) where T -*(m::Integer, k::AbstractKnotVector) -Base.issubset(k::KnotVector, k′::KnotVector) -unique(k::AbstractKnotVector) -countknots(k::AbstractKnotVector, t::Real) -@knotvector_str -bsplinebasis₊₀ -bsplinebasis₋₀ -bsplinebasis -BasicBSpline.bsplinebasis₋₀I -intervalindex -bsplinebasisall -bsplinesupport -dim -exactdim_R(P::BSplineSpace) -exactdim_I(P::BSplineSpace) -BSplineSpace -isnondegenerate -isdegenerate(P::BSplineSpace) -BSplineManifold -unbounded_mapping -RationalBSplineManifold -fittingcontrolpoints +## BasicBSpline.jl +```@autodocs +Modules = [BasicBSpline] +Order = [:type, :function, :macro] ``` -```@docs -BasicBSpline.refinement_R -BasicBSpline.refinement_I -``` - -```@docs -BasicBSplineFitting.innerproduct_R -BasicBSplineFitting.innerproduct_I -``` - -## Private -Note that the following methods are considered private methods, and changes in their behavior are not considered breaking changes. - -```@docs -BasicBSpline.r_nomial -BasicBSpline._vec -BasicBSpline._lower_R -BasicBSpline._changebasis_R -BasicBSpline._changebasis_I -BasicBSpline.__changebasis_I -BasicBSpline._changebasis_sim +## BasicBSplineFitting.jl +```@autodocs +Modules = [BasicBSplineFitting] +Order = [:type, :function, :macro] ``` diff --git a/docs/src/img/2dim.png b/docs/src/img/2dim.png deleted file mode 100644 index 1fcd4863c..000000000 Binary files a/docs/src/img/2dim.png and /dev/null differ diff --git a/docs/src/img/2dim_h-refinement.png b/docs/src/img/2dim_h-refinement.png deleted file mode 100644 index c844b44f3..000000000 Binary files a/docs/src/img/2dim_h-refinement.png and /dev/null differ diff --git a/docs/src/img/2dim_p-refinement.png b/docs/src/img/2dim_p-refinement.png deleted file mode 100644 index 6b4a17e77..000000000 Binary files a/docs/src/img/2dim_p-refinement.png and /dev/null differ diff --git a/docs/src/img/bspline_curve.png b/docs/src/img/bspline_curve.png new file mode 100644 index 000000000..45c8bbd1d Binary files /dev/null and b/docs/src/img/bspline_curve.png differ diff --git a/docs/src/img/cover.png b/docs/src/img/cover.png index 333e9ee39..9abe7773d 100644 Binary files a/docs/src/img/cover.png and b/docs/src/img/cover.png differ diff --git a/docs/src/img/fitting.png b/docs/src/img/fitting.png index 03fe12036..3192cba33 100644 Binary files a/docs/src/img/fitting.png and b/docs/src/img/fitting.png differ diff --git a/docs/src/img/fitting_coarse.png b/docs/src/img/fitting_coarse.png index 8cc7dc518..5a6c3a6a9 100644 Binary files a/docs/src/img/fitting_coarse.png and b/docs/src/img/fitting_coarse.png differ diff --git a/docs/src/img/rational_bspline_surface_href_plotly.png b/docs/src/img/rational_bspline_surface_href_plotly.png new file mode 100644 index 000000000..f0bee0dde Binary files /dev/null and b/docs/src/img/rational_bspline_surface_href_plotly.png differ diff --git a/docs/src/img/rational_bspline_surface_plotly.png b/docs/src/img/rational_bspline_surface_plotly.png new file mode 100644 index 000000000..1a9546dbf Binary files /dev/null and b/docs/src/img/rational_bspline_surface_plotly.png differ diff --git a/docs/src/img/rational_bspline_surface_pref_plotly.png b/docs/src/img/rational_bspline_surface_pref_plotly.png new file mode 100644 index 000000000..665838b4b Binary files /dev/null and b/docs/src/img/rational_bspline_surface_pref_plotly.png differ diff --git a/docs/src/interpolations.md b/docs/src/interpolations.md index 7a2320e54..d88518529 100644 --- a/docs/src/interpolations.md +++ b/docs/src/interpolations.md @@ -2,11 +2,13 @@ Currently, BasicBSpline.jl doesn't have APIs for interpolations, but it is not hard to implement some basic interpolation algorithms with this package. -```@setup interpolation +## Setup + +```@example interpolation using BasicBSpline using IntervalSets using Random; Random.seed!(42) -using Plots; plotly() +using Plots ``` ## Interpolation with cubic B-spline @@ -40,6 +42,7 @@ fs = [1.3, 1.5, 2, 2.1, 1.9, 1.3] f = interpolate(xs,fs) # Plot +plotly() scatter(xs, fs) plot!(t->f(t)) savefig("interpolation_cubic.html") # hide diff --git a/docs/src/math/bsplinebasis.md b/docs/src/math/bsplinebasis.md index 5c42d2c85..d3eef1579 100644 --- a/docs/src/math/bsplinebasis.md +++ b/docs/src/math/bsplinebasis.md @@ -5,7 +5,7 @@ ```@example math_bsplinebasis using BasicBSpline using Random -using Plots; gr() +using Plots ``` ## Basic properties of B-spline basis function @@ -42,6 +42,7 @@ These B-spline basis functions can be calculated with [`bsplinebasis₊₀`](@re p = 2 k = KnotVector([0.0, 1.5, 2.5, 5.5, 8.0, 9.0, 9.5, 10.0]) P = BSplineSpace{p}(k) +gr() plot([t->bsplinebasis₊₀(P,i,t) for i in 1:dim(P)], 0, 10, ylims=(0,1), label=false) savefig("bsplinebasisplot.png") # hide nothing # hide @@ -250,7 +251,7 @@ for p in 1:3 plot(P, legend=:topleft, label="B-spline basis (p=1)") plot!(t->intervalindex(P,t),0,10, label="Interval index") plot!(t->sum(bsplinebasis(P,i,t) for i in 1:dim(P)),0,10, label="Sum of B-spline basis") - plot!(k, label="knot vector", legend=:inside) + plot!(k, label="knot vector") plot!([t->bsplinebasisall(P,1,t)[i] for i in 1:p+1],0,10, color=:black, label="bsplinebasisall (i=1)", ylim=(-1,8-2p)) savefig("bsplinebasisall-$(p).html") # hide nothing # hide diff --git a/docs/src/math/bsplinemanifold.md b/docs/src/math/bsplinemanifold.md index 11052ff55..8dead1a4b 100644 --- a/docs/src/math/bsplinemanifold.md +++ b/docs/src/math/bsplinemanifold.md @@ -5,7 +5,7 @@ using BasicBSpline using StaticArrays using StaticArrays -using Plots; plotly() +using Plots ``` ## Multi-dimensional B-spline @@ -36,6 +36,7 @@ i1 = 3 i2 = 4 # Visualize basis functions +plotly() plot(P1; plane=:xz, label="P1", color=:red) plot!(P2; plane=:yz, label="P2", color=:green) plot!(k1; plane=:xz, label="k1", color=:red, markersize=2) diff --git a/docs/src/math/bsplinespace.md b/docs/src/math/bsplinespace.md index 29b0673e9..c3a5d032c 100644 --- a/docs/src/math/bsplinespace.md +++ b/docs/src/math/bsplinespace.md @@ -4,9 +4,8 @@ ```@example math_bsplinespace using BasicBSpline -using BasicBSplineExporter using StaticArrays -using Plots; gr() +using Plots ``` ## Defnition @@ -84,6 +83,7 @@ dim(P3), exactdim_R(P3), exactdim_I(P3) Visualization: ```@example math_bsplinespace +gr() pl1 = plot(P1); plot!(pl1, knotvector(P1)) pl2 = plot(P2); plot!(pl2, knotvector(P2)) pl3 = plot(P3); plot!(pl3, knotvector(P3)) diff --git a/docs/src/math/derivative.md b/docs/src/math/derivative.md index 31f7ab3f7..8152fdaa8 100644 --- a/docs/src/math/derivative.md +++ b/docs/src/math/derivative.md @@ -1,10 +1,9 @@ # Derivative of B-spline -```@setup math +```@example math_derivative using BasicBSpline -using BasicBSplineExporter using StaticArrays -using Plots; plotly() +using Plots ``` !!! info "Thm. Derivative of B-spline basis function" @@ -18,9 +17,10 @@ using Plots; plotly() ``` Note that ``\dot{B}_{(i,p,k)}\in\mathcal{P}[p-1,k]``. -```@example math +```@example math_derivative k = KnotVector([0.0, 1.5, 2.5, 5.5, 8.0, 9.0, 9.5, 10.0]) P = BSplineSpace{3}(k) +plotly() plot( plot(BSplineDerivativeSpace{0}(P), label="0th derivative", color=:black), plot(BSplineDerivativeSpace{1}(P), label="1st derivative", color=:red), @@ -34,23 +34,3 @@ nothing # hide ```@raw html ``` - -```@docs -BSplineDerivativeSpace -``` - -```@docs -BasicBSpline.derivative -``` - -```@docs -bsplinebasis′₊₀ -``` - -```@docs -bsplinebasis′₋₀ -``` - -```@docs -bsplinebasis′ -``` diff --git a/docs/src/math/inclusive.md b/docs/src/math/inclusive.md index 39928ea09..10a458f0a 100644 --- a/docs/src/math/inclusive.md +++ b/docs/src/math/inclusive.md @@ -1,12 +1,15 @@ # Inclusive relation between B-spline spaces -```@setup math +## Setup + +```@example math_inclusive using BasicBSpline -using BasicBSplineExporter using StaticArrays -using Plots; plotly() +using Plots ``` +## Theorem on [`issubset`](@ref) + !!! info "Thm. Inclusive relation between B-spline spaces" For non-degenerate B-spline spaces, the following relationship holds. ```math @@ -15,32 +18,81 @@ using Plots; plotly() \Leftrightarrow (m=p'-p \ge 0 \ \text{and} \ k+m\widehat{k}\subseteq k') ``` -```@docs -Base.issubset(P::BSplineSpace{p}, P′::BSplineSpace{p′}) where {p, p′} +### Examples + +Here are plots of the B-spline basis functions of the spaces `P1`, `P2`, `P3`. + +```@example math_inclusive +P1 = BSplineSpace{1}(KnotVector([1,3,6,6])) +P2 = BSplineSpace{1}(KnotVector([1,3,5,6,6,8,9])) +P3 = BSplineSpace{2}(KnotVector([1,1,3,3,6,6,6,8,9])) +gr() +plot( + plot(P1; ylims=(0,1), label="P1"), + plot(P2; ylims=(0,1), label="P2"), + plot(P3; ylims=(0,1), label="P3"), + layout=(3,1), + link=:x +) +savefig("inclusive-issubset.png") # hide +nothing # hide ``` +![](inclusive-issubset.png) + +These spaces have the folllowing incusive relationships. + +```@repl math_inclusive +P1 ⊆ P2 +P1 ⊆ P3 +P2 ⊆ P3, P3 ⊆ P2, P2 ⊆ P1, P3 ⊆ P1 +``` + +## Definition on [`issqsubset`](@ref) + +!!! tip "Def. Inclusive relation between B-spline spaces" + For non-degenerate B-spline spaces, the following relationship holds. + ```math + \mathcal{P}[p,k] + \sqsubseteq\mathcal{P}[p',k'] + \Leftrightarrow + \mathcal{P}[p,k]|_{[k_{p+1},k_{l-p}]} + \subseteq\mathcal{P}[p',k']|_{[k'_{p'+1},k'_{l'-p'}]} + ``` + +### Examples + Here are plots of the B-spline basis functions of the spaces `P1`, `P2`, `P3`. -```@repl math -P1 = BSplineSpace{1}(KnotVector([1,3,5,8])) -P2 = BSplineSpace{1}(KnotVector([1,3,5,6,8,9])) -P3 = BSplineSpace{2}(KnotVector([1,1,3,3,5,5,8,8])) +```@example math_inclusive +P1 = BSplineSpace{1}(KnotVector([1,3,6,6])) # Save definition as above +P4 = BSplineSpace{1}(KnotVector([1,3,5,6,8])) +P5 = BSplineSpace{2}(KnotVector([1,1,3,3,6,6,7,9])) +gr() plot( - plot([t->bsplinebasis₊₀(P1,i,t) for i in 1:dim(P1)], 1, 9, ylims=(0,1), legend=false), - plot([t->bsplinebasis₊₀(P2,i,t) for i in 1:dim(P2)], 1, 9, ylims=(0,1), legend=false), - plot([t->bsplinebasis₊₀(P3,i,t) for i in 1:dim(P3)], 1, 9, ylims=(0,1), legend=false), + plot(P1; ylims=(0,1), label="P1"), + plot(P4; ylims=(0,1), label="P4"), + plot(P5; ylims=(0,1), label="P5"), layout=(3,1), link=:x ) -savefig("subbsplineplot.html") # hide +savefig("inclusive-issqsubset.png") # hide nothing # hide ``` -```@raw html - +![](inclusive-issqsubset.png) + +These spaces have the folllowing incusive relationships. + +```@repl math_inclusive +P1 ⊑ P4 +P1 ⊑ P5 +P4 ⊑ P5, P5 ⊑ P4, P4 ⊑ P1, P5 ⊑ P1 ``` -This means, there exists a ``n \times n'`` matrix ``A`` which holds: +## Change basis with a matrix + +If ``\mathcal{P}[p,k] \subseteq \mathcal{P}[p',k']`` (or ``\mathcal{P}[p,k] \sqsubseteq \mathcal{P}[p',k']``), there exists a ``n \times n'`` matrix ``A`` which holds: ```math \begin{aligned} @@ -51,14 +103,14 @@ n'&=\dim(\mathcal{P}[p',k']) \end{aligned} ``` -You can calculate the change of basis matrix ``A`` with `changebasis`. +You can calculate the change of basis matrix ``A`` with [`changebasis`](@ref). -```@repl math +```@repl math_inclusive A12 = changebasis(P1,P2) A13 = changebasis(P1,P3) ``` -```@repl math +```@example math_inclusive plot( plot([t->bsplinebasis₊₀(P1,i,t) for i in 1:dim(P1)], 1, 9, ylims=(0,1), legend=false), plot([t->sum(A12[i,j]*bsplinebasis₊₀(P2,j,t) for j in 1:dim(P2)) for i in 1:dim(P1)], 1, 9, ylims=(0,1), legend=false), @@ -66,34 +118,52 @@ plot( layout=(3,1), link=:x ) -savefig("subbsplineplot2.html") # hide +savefig("inclusive-issubset-matrix.png") # hide nothing # hide ``` -```@raw html - -``` +![](inclusive-issubset-matrix.png) -```@docs -changebasis_R +```@repl math_inclusive +A14 = changebasis(P1,P4) +A15 = changebasis(P1,P5) ``` -```@docs -changebasis_I +```@example math_inclusive +plot( + plot([t->bsplinebasis₊₀(P1,i,t) for i in 1:dim(P1)], 1, 9, ylims=(0,1), legend=false), + plot([t->sum(A14[i,j]*bsplinebasis₊₀(P4,j,t) for j in 1:dim(P4)) for i in 1:dim(P1)], 1, 9, ylims=(0,1), legend=false), + plot([t->sum(A15[i,j]*bsplinebasis₊₀(P5,j,t) for j in 1:dim(P5)) for i in 1:dim(P1)], 1, 9, ylims=(0,1), legend=false), + layout=(3,1), + link=:x +) +savefig("inclusive-issqsubset-matrix.png") # hide +nothing # hide ``` -```@docs -issqsubset -``` +![](inclusive-issqsubset-matrix.png) -```@docs -expandspace -``` +* [`changebasis_R`](@ref) + * Calculate the matrix based on ``P \subseteq P'`` +* [`changebasis_I`](@ref) + * Calculate the matrix based on ``P \sqsubseteq P'`` +* [`changebasis`](@ref) + * Return `changebasis_R` if ``P \subseteq P'``, otherwise return `changebasis_I` if ``P \sqsubseteq P'``. -```@docs -expandspace_R -``` +## Expand spaces with additional knots or polynomial degree + +There are some functions to expand spaces with additional knots or polynomial degree. + +* [`expandspace_R`](@ref) +* [`expandspace_I`](@ref) +* [`expandspace`](@ref) -```@docs -expandspace_I +```@repl math_inclusive +P = BSplineSpace{2}(knotvector"21 113") +P_R = expandspace_R(P, Val(1), KnotVector([3.4, 4.2])) +P_I = expandspace_I(P, Val(1), KnotVector([3.4, 4.2])) +P ⊆ P_R +P ⊆ P_I +P ⊑ P_R +P ⊑ P_I ``` diff --git a/docs/src/math/knotvector.md b/docs/src/math/knotvector.md index d9e91c9e7..4d853b32e 100644 --- a/docs/src/math/knotvector.md +++ b/docs/src/math/knotvector.md @@ -3,8 +3,8 @@ ## Setup ```@example math_knotvector using BasicBSpline +using Plots using InteractiveUtils # hide -using Plots; gr() ``` ## Definition @@ -44,8 +44,9 @@ A knot vector can be visualized with `Plots.plot`. ```@repl math_knotvector k1 = KnotVector([0.0, 1.5, 2.5, 5.5, 8.0, 9.0, 9.5, 10.0]) -plot(k1; label="k1") k2 = knotvector"1231123" +gr() +plot(k1; label="k1") plot!(k2; label="k2", offset=0.5, ylims=(-0.1,1), xticks=0:10) savefig("knotvector.png") # hide nothing # hide diff --git a/docs/src/math/rationalbsplinemanifold.md b/docs/src/math/rationalbsplinemanifold.md index 58968d911..e4c30a64f 100644 --- a/docs/src/math/rationalbsplinemanifold.md +++ b/docs/src/math/rationalbsplinemanifold.md @@ -6,7 +6,7 @@ using BasicBSpline using StaticArrays using LinearAlgebra -using Plots; plotly() +using Plots ``` [Non-uniform rational basis spline (NURBS)](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline) is also supported in BasicBSpline.jl package. @@ -52,6 +52,7 @@ M3 = BSplineManifold(a3.*w,(P)) R3 = RationalBSplineManifold(a3,w,(P,)) # Plot +plotly() pl2 = plot(R2, xlims=(-1,1), ylims=(-1,1); color=:blue, linewidth=3, aspectratio=1, label=false) pl3 = plot(R3; color=:blue, linewidth=3, controlpoints=(markersize=2,), label="Rational B-spline curve") plot!(pl3, M3; color=:red, linewidth=3, controlpoints=(markersize=2,), label="B-spline curve") diff --git a/docs/src/math/refinement.md b/docs/src/math/refinement.md index 4e3c29bd7..ef555e01a 100644 --- a/docs/src/math/refinement.md +++ b/docs/src/math/refinement.md @@ -1,52 +1,54 @@ # Refinement -```@setup math +## Setup + +```@example math_refinement using BasicBSpline using BasicBSplineExporter using StaticArrays -using Plots; plotly() -``` - -## Documentation - -```@docs -refinement +using Plots ``` ## Example ### Define original manifold -```@example math +```@example math_refinement p = 2 # degree of polynomial k = KnotVector(1:8) # knot vector P = BSplineSpace{p}(k) # B-spline space rand_a = [SVector(rand(), rand()) for i in 1:dim(P), j in 1:dim(P)] a = [SVector(2*i-6.5, 2*j-6.5) for i in 1:dim(P), j in 1:dim(P)] + rand_a # random M = BSplineManifold(a,(P,P)) # Define B-spline manifold +gr() +plot(M) +savefig("refinement_2dim_original.png") # hide nothing # hide ``` +![](refinement_2dim_original.png) + ### h-refinement -Insert additional knots to knot vector. +Insert additional knots to knot vector without changing the shape. -```@repl math -k₊ = (KnotVector([3.3,4.2]),KnotVector([3.8,3.2,5.3])) # additional knot vectors +```@repl math_refinement +k₊ = (KnotVector([3.3,4.2]), KnotVector([3.8,3.2,5.3])) # additional knot vectors M_h = refinement(M, k₊) # refinement of B-spline manifold -save_png("2dim_h-refinement.png", M_h) # save image +plot(M_h) +savefig("refinement_2dim_h.png") # hide +nothing # hide ``` -![](2dim_h-refinement.png) - -Note that this shape and the last shape are equivalent. +![](refinement_2dim_h.png) ### p-refinement -Increase the polynomial degree of B-spline manifold. +Increase the polynomial degree of B-spline manifold without changing the shape. -```@repl math +```@repl math_refinement p₊ = (Val(1), Val(2)) # additional degrees M_p = refinement(M, p₊) # refinement of B-spline manifold -save_png("2dim_p-refinement.png", M_p) # save image +plot(M_p) +savefig("refinement_2dim_p.png") # hide +nothing # hide ``` -![](2dim_p-refinement.png) -Note that this shape and the last shape are equivalent. +![](refinement_2dim_p.png) diff --git a/docs/update_images_in_readme.jl b/docs/update_images_in_readme.jl new file mode 100644 index 000000000..d6b501fb6 --- /dev/null +++ b/docs/update_images_in_readme.jl @@ -0,0 +1,20 @@ +# Run this script after running make.jl + +dir_build = joinpath(@__DIR__, "build") +dir_img = joinpath(@__DIR__, "src", "img") + +for filename in readdir(dir_build) + path_src = joinpath(dir_build, filename) + if startswith(filename, "readme-") && endswith(filename, ".html") + println(path_src) + path_png = joinpath(dir_img, chopsuffix(chopprefix(filename, "readme-"), ".html") * ".png") + cmd = `google-chrome-stable --headless --disable-gpu --screenshot=$path_png $path_src` + run(cmd) + end + + if startswith(filename, "readme-") && endswith(filename, ".png") + println(path_src) + path_png = joinpath(dir_img, chopprefix(filename, "readme-")) + cp(path_src, path_png; force=true) + end +end diff --git a/src/_ChangeBasis.jl b/src/_ChangeBasis.jl index dce046d90..e2d3a43c3 100644 --- a/src/_ChangeBasis.jl +++ b/src/_ChangeBasis.jl @@ -823,6 +823,12 @@ function _changebasis_R(P::BSplineSpace, dP′::BSplineDerivativeSpace{0}) return _changebasis_R(P, P′) end +@doc raw""" + changebasis(P::AbstractFunctionSpace, P′::AbstractFunctionSpace) + +Return `changebasis_R(P, P′)` if ``P ⊆ P′``, otherwise `changebasis_R(P, P′)` if ``P ⊑ P′``. +Throw an error if ``P ⊈ P′`` and ``P ⋢ P′``. +""" function changebasis(P::AbstractFunctionSpace, P′::AbstractFunctionSpace) P ⊆ P′ && return _changebasis_R(P, P′) P ⊑ P′ && return _changebasis_I(P, P′) diff --git a/src/_util.jl b/src/_util.jl index 853fcd7ad..d6bb4c2fa 100644 --- a/src/_util.jl +++ b/src/_util.jl @@ -11,6 +11,7 @@ Calculate ``r``-nomial coefficient r_nomial(n, k, r) +**This function is considered as internal.** ```math (1+x+\cdots+x^r)^n = \sum_{k} a_{n,k,r} x^k ```