なめらかな曲線をJuliaでSVG出力する
概要
- Plots.jlで出力されるグラフ(曲線)は折れ線なのでカクカクしている。
-
BasicBSpline
パッケージを使って出力すればBézier曲線で近似されるので嬉しい!
本記事で使うパッケージはJuliaのREPLから以下のコマンドでインストールできます。
]add Plots
add StaticArrays
add BasicBSpline
add https://github.com/hyrodium/BasicBSplineExporter.jl
Plots.jlでの出力
Plots.jlパッケージを使ってグラフを出力してみましょう。
using Plots
plot(sin,-8,8) # 正弦波を-8から8までプロット
savefig("sin_Plots.svg") # SVGで保存
savefig("sin_Plots.png") # PNGで保存
極大値の付近でSVG画像を拡大してみます。
カクカクしていますね。[1]
これは曲線が折れ線によって近似されているためです。以下の画像はSVG画像をInkscapeで開いて確認しているところです。
滑らかなグラフを得るにはどのようにすれば良いでしょうか?
SVGではBézier曲線に対応しているのでファイル形式としては可能のはず…
→ BasicBSplineパッケージを使いましょう!
BasicBSplineExporter.jlでの出力
using BasicBSpline
using BasicBSplineExporter
using StaticArrays
f(t) = SVector(t,sin(t)) # 正弦波のパラメータ表示
t0,t1 = -8,8 # 左端と右端
p = 3 # 多項式次数。SVGでは3次までのBézier曲線が使える。
k = KnotVector(t0:t1)+p*KnotVector(t0,t1) # ノット列
P = BSplineSpace{p}(k) # B-spline空間の定義
a = fittingcontrolpoints(f,(P,)) # B-spline曲線の制御点の計算
M = BSplineManifold(a,(P,)) # B-spline曲線の定義
save_svg("sin_BasicBSpline.svg", M, xlims=(-10,10), ylims=(-2,2)) # B-spline曲線をSVGで保存
save_png("sin_BasicBSpline.png", M, xlims=(-10,10), ylims=(-2,2)) # B-spline曲線をPNGで保存![](https://storage.googleapis.com/zenn-user-upload/731678b5e5e8-20211229.png)
Inkscapeで確認すると以下のようにBézier曲線で滑らかに曲線が表現されていることが分かります。
上記のように定義したB-spline曲線は全体で
滑らかでない場合(Plots.jl)
Plots.jlでは以下のようにすれば画像の出力までできます。
plot(abs∘sin,-8,8)
savefig("abssin_Plots.svg")
savefig("abssin_Plots.png")
折れ線近似なので、導関数の不連続点でも特徴を捉えたグラフになっているように見えます。
しかし先端を拡大してみると、グラフは
滑らかでない場合(BasicBSpline.jl)
これまでと同様に、以下を実行すればグラフが出力できます。
f(t) = SVector(t,abs(sin(t)))
t0,t1 = -8,8
p = 3
k = KnotVector(range(t0,t1,length=50))+p*KnotVector(t0,t1)
P = BSplineSpace{p}(k)
a = fittingcontrolpoints(f,(P,))
M = BSplineManifold(a,(P,))
save_svg("abssin_BasicBSpline![](https://storage.googleapis.com/zenn-user-upload/555b66bd3bac-20211229.png).svg", M, xlims=(-10,10), ylims=(-2,2))
save_png("abssin_BasicBSpline.png", M, xlims=(-10,10), ylims=(-2,2))
出力された画像↓
導関数の不連続点での拡大↓
ノットの数(区分多項式の分割の数)を増やしたとしても「滑らかに繋ぐ」という制約があるので近似精度はあまり良くなりません。
B-splineでは、ノットの重複に応じて関数の滑らかさが減るため[2]、
k = KnotVector(range(t0,t1,length=12))+p*KnotVector(t0,t1)
k += p*KnotVector(-2π:π:2π) # 滑らかさを減らしたい点でのノットを増やす
P = BSplineSpace{p}(k)
a = fittingcontrolpoints(f,(P,))
M = BSplineManifold(a,(P,))
save_svg("abssin_BasicBSpline_modified.svg", M, xlims=(-10,10), ylims=(-2,2))
save_png("abssin_BasicBSpline_modified.png", M, xlims=(-10,10), ylims=(-2,2))
出力された画像↓
拡大しても綺麗!↓
まとめ
- BasicBSpline等のパッケージを使えば滑らかなグラフが出力できて便利!
- B-splineではノット列の重複に応じて滑らかさを変更できて便利!
- とりあえずプロットするのならPlots.jlパッケージが便利!
追記
本記事の英語版をJulia Foremにも投稿しました。
Discussion