8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ベジェ曲線の算出過程をPlotlyのスライダーを使用してインタラクティブに描画させてみる

Last updated at Posted at 2018-12-26

概要

ベジェ曲線の算出過程をPlotlyのスライダーを使用してインタラクティブに描画させてみる.

環境

  • Windows 10
  • Python 3.6.3 (Anaconda)
  • NumPy 1.14.2
  • Plotly 3.4.1

参考

出力結果

3次

以下で解説のコードで描画したもの.
BezierCurveAnimation.gif

5次

入力点数を増やすと面白い.
BezierCurveAnimation5.gif

コードと解説

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作成
    • 描画
# 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)
8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?