概要
ベジェ曲線の算出過程をPlotlyのスライダーを使用してインタラクティブに描画させてみる.
環境
- Windows 10
- Python 3.6.3 (Anaconda)
- NumPy 1.14.2
- Plotly 3.4.1
参考
- Plotly User Guide for Python | https://plot.ly/python/user-guide/
- Python Slider Controls | plotly | https://plot.ly/python/sliders/
- ベジェ曲線 - Wikipedia | https://ja.wikipedia.org/wiki/ベジェ曲線
出力結果
3次
5次
コードと解説
1. ライブラリのインポート
import numpy as np
import plotly.offline as py # オフライン描画用Plotlyライブラリ
import plotly.graph_objs as go # グラフオブジェクト(辞書データ)作成用Plotlyライブラリ
py.init_notebook_mode() # jupyter notebook 上でplotlyを使用する場合
2. ベジェ曲線を算出するための関数を定義
def bezier(p_in, t, sep=False):
"""ベジェ曲線上の点および算出途中で生成された点群を出力
Wikipedia ベジェ曲線作図法に倣う
Parameters
----------
p_in: list like object
入力点群(点数で次数を決定)
t: float
媒介変数
sep: bool
各回での算出結果にNoneを含ませる (default False)
Returns
-------
list
出力点群
"""
p_out = []
for i in range(len(p_in) - 1):
p_out.append((1 - t) * p_in[i] + t * p_in[i + 1])
if len(p_in) > 2:
p = bezier(p_out, t, sep) # 再帰
if sep:
p_out += [None] # Plotlyで区切って描画させるため
p_out += p
return p_out # リストの最後がベジェ曲線上の点
3. 入力点群データの作成
ps_x = np.random.rand(4)
# array([0.36265296, 0.00793013, 0.76807413, 0.64266143])
ps_y = np.random.rand(4)
# array([0.08902042, 0.47218923, 0.36629385, 0.84850309])
# ベジェ曲線上の点の算出テスト
bezier(ps_x, 0.5, True)
# [0.18529154541040233,
# 0.38800212828554853,
# 0.705367778887567,
# None,
# 0.28664683684797543,
# 0.5466849535865578,
# None,
# 0.4166658952172666] # ベジェ曲線上の点
4. Plotlyグラフ作成
- 大まかな流れ
- data(traceの集合体)の作成
- ステップごとの点群を描画するためのtraceを作成(21個)
- ベジェ曲線描画のためのtraceを作成
- 入力点群描画のためのtraceを作成
- レイアウトの作成
- スライダー設定を作成
- ステップごとの描画設定を作成
- スライダー設定を作成
- figure作成
- 描画
- data(traceの集合体)の作成
# traceを格納するための空のリストを準備
data = []
# ステップごとに,描画するためのtraceデータを作成し,dataリストに格納する
t_step = np.linspace(0, 1, 21) # ステップ分の媒介変数tのリストを作成(0.05ピッチ).
for t in t_step:
# あるtでのベジェ曲線上の点および算出途中で生成された点群を取得
p_x = bezier(ps_x, t, sep=True)
p_y = bezier(ps_y, t, sep=True)
# ベジェ曲線上の点および算出途中で生成された点群を描画するためのtraceを作成
trace = go.Scatter(
visible=False, # さしあたり全てのフレームを非表示設定とする
mode='lines+markers', # ラインプロット + 点プロット
name='t = %.2f' % t,
x=p_x,
y=p_y,
line=dict(dash='dot'))
data.append(trace) # traceをdataリストに格納
# 初回表示するステップの設定
step0 = int(len(t_step) / 2) # 初回表示ステップ番号を定義
data[step0]['visible'] = True # 初回表示ステップを表示設定する
# 滑らかな曲線を描画するため,ベジェ曲線上の点リストを算出(0.01ピッチ).
curve_x = [bezier(ps_x, t)[-1] for t in np.linspace(0, 1, 101)]
curve_y = [bezier(ps_y, t)[-1] for t in np.linspace(0, 1, 101)]
# ベジェ曲線を描画するためのtraceを作成
trace_line = go.Scatter(
visible=True,
x=curve_x,
y=curve_y,
mode='lines', # ラインプロット
name='Bézier curve')
data.append(trace_line) # traceをdataリストに格納
# 入力点群データを描画するためのtraceを作成
trace_point = go.Scatter(
visible=True,
x=ps_x,
y=ps_y,
mode='lines+markers+text', # ラインプロット + 点プロット + テキストプロット
name='input',
line=dict(dash='dash'),
text=['P%d' % i for i in range(len(ps_x))],
textposition='middle right')
data.append(trace_point) # traceをdataリストに格納
# 各ステップの描画方法を設定した設定リストを作成
steps = []
for i in range(len(t_step)):
step = dict(
method='restyle',
args=['visible', [False] * len(data)]) # さしあたり全てのフレームを非表示設定とする
step['args'][1][i] = True # iステップを表示設定とする
step['args'][1][-2] = True # ベジェ曲線は常に描画
step['args'][1][-1] = True # 入力データ点は常に描画
steps.append(step)
# スライダー設定辞書データを作成
sliders = [
dict(
active=step0, # 初期表示ステップの指定
currentvalue={'prefix': 't: '}, # 現在値表示エリアに表示する文字列の定義
pad={'t': 20}, # スライダー描画位置の設定(padding-top: 20)
steps=steps)] # ステップ設定リストを指定
# レイアウト設定
layout = go.Layout(
sliders=sliders, # 作成したスライダー設定辞書データをレイアウトに織り込み
xaxis=dict(range=[0, 1]), # x軸表示範囲の設定
yaxis=dict(range=[0, 1])) # y軸表示範囲の設定
# fig, plot
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)