簡易関数プロットライブラリ simpleplot.js

ファイル:simpleplot.js (version 2020/08/01)

概要

自分が使うために作成した,ごくごく簡易的な JavaScript の関数プロットライブラリです.

ライブラリといっても,いくつかのグラフ描画ページを作成する際に自分が繰り返し書いていたコードをまとめただけでして,世の中でよく利用されているカッコいい chart library のように,汎用的で見栄えの良いグラフ作成ツールを提供することは,最初から目的としていません.HTML/JavaScript の canvas API をそのまま使っており,canvas とその context を取得してライブラリのオブジェクトに context を結びつける形にしているため,ライブラリを使う側では canvas の Id も,その context も,そのまま利用できます.ライブラリが用意していない機能,たとえば軸のラベルや目盛の値などは,どうぞご自由にコードを書いてください,というスタンスです.

ライブラリの中身は,初心者が作ったものですのでちょっとご覧いただくだけで理解できると思います.「普通,こうは書かないだろ」というコードも多々あると思いますが,自分の目的はこれで達成できているため,とりあえずよしとします.

本ライブラリを使ったページの作成例は,こちらを参照してください.なお,仕様は予告なく変更されます.

使用法

コード例を示します. 【実行】 【ダウンロード html | JavaScript

html

<!DOCTYPE html>
<html>
  <head>
    <title>simpleplot-js sample</title>
    <script src="simpleplot.js"></script>
  </head>
  <body>
    <canvas id="myCanvas" width="360" height="200"></canvas>
    <script src="simpleplot-sample.js"></script>
  </body>
</html>

head セクションで simpleplot.js を読み込み,標準的な方法で canvas 要素を配置しておきます.

JavaScript

// simpleplot-sample.js

// 通常の方法でCanvasのIdと 2D コンテキストを取得
const cnv = document.getElementById("myCanvas");
const ctx = cnv.getContext("2d");

// SimplePlot オブジェクトを生成し,コンテキストを結びつける.
const myPlot = new SimplePlot;
myPlot.setContext(ctx);

// プロット範囲四隅の canvas 内ピクセル値と,対応する変数値を指定.
myPlot.setPixRange(20, 20, 340, 180);  // 左上 (20, 20) と右下 (340, 180):Canvas座標での指定
myPlot.setValRange(-1, 0, 1, 1);       // 左下 (-1, 0) と右上 (1, 1)(注意!)

// ↓↓↓グラフ更新を行う場合,ここから先をコールバック関数に記述.
// canvasをクリア(グラフ更新を行う場合)
ctx.clearRect(0, 0, cnv.width, cnv.height);

// プロット領域の枠を描画(任意)
// myPlot.drawFrame();

// 軸の交点を指定し,軸を描画
myPlot.setAxisIntersection(0, 0);
myPlot.drawAxis('x');
myPlot.drawAxis('y');

// 目盛を描画(任意)
myPlot.drawScale('x');
myPlot.drawScale('y');

// 関数曲線を描画.描画する関数として myFunc を指定.
myPlot.plotFunction(myFunc);
// ↑↑↑グラフ更新を行う場合,ここまでをコールバック関数に記述.

// 別途,描画する関数を定義しておく.
function myFunc(x) {
  return x * x;  // y = x^2
}

基本的な流れはこのようになります.各関数の中で実行される描画関連コマンドは,SimplePlot オブジェクトに結びつけられている context の現在の指定に従って行われます.色,線の幅や塗りつぶしなど,ライブラリでサポートしていない各種指定は,標準的な canvas API を使ってください.JavaScriptコードは html ファイルの中に <script> ~ </script> で囲んで記述しても構いません.

本ライブラリでは,canvas内にグラフ本体を描く矩形のプロットエリアを指定します.canvas APIのコマンドは画面上のピクセル値に基づいて行われますが,本ライブラリでは画面上のピクセル座標と対応する変数座標を用意し,変数座標により関数もしくはデータのプロットを行います.目盛とその値,軸ラベルなどは,プロットエリアの外に描くことができます.

関数リファレンス

準備

SimplePlot()

SimplePlot オブジェクトのコンストラクタ.

setContext(context)

戻り値:なし
context: canvas の2dコンテキスト.

SimplePlot ライブラリによる描画対象の context を指定します.最初に必ず実行してください.

getContext()

戻り値:SimplePlot オブジェクトに結びつけられた context.

単純にセットされた context を返します.

setPixRange(xmin, ymin, xmax, ymax)

戻り値:なし
xmin: canvas内プロットエリア左上のx座標(ピクセル値).
ymin: canvas内プロットエリア左上のy座標(ピクセル値).
xmax: canvas内プロットエリア右下のx座標(ピクセル値).
ymax: canvas内プロットエリア右下のy座標(ピクセル値).

グラフを描く領域の範囲をピクセル値で指定します.(xmin, ymin) は canvas 内左上の座標,(xmax, ymax) は canvas 内右下の座標です.canvas の左上が (0, 0) で,x 座標は右方向,y 座標は下方向がプラスになっていることに従った記述としています.一応初期値は (0, 0)-(100, 100) で設定していますが,作成した canvas の大きさに適合するよう必ず実行してください.

setValRange(xmin, ymin, xmax, ymax)

戻り値:なし
xmin: canvas内プロットエリア左下のx座標(変数値).
ymin: canvas内プロットエリア左下のy座標(変数値).
xmax: canvas内プロットエリア右上のx座標(変数値).
ymax: canvas内プロットエリア右上のy座標(変数値).

グラフを描く領域の四隅に対応する変数値を指定します.(xmin, ymin) はプロットエリア左下の変数値座標,(xmax, ymax) はプロットエリア右上の変数値座標です.setPixRange() と併せて実行することで,変数座標系と canvas のピクセル座標系との対応が定義されますので,必ず実行してください.デフォルトは,(-1, -1) - (1, 1) です.

描画

本ライブラリでは,線の色,太さ,パターンや塗りつぶしの色などを指定する方法を用意していません.canvas API の標準的な方法で,各関数を実行する前に指定してください.グラフ本体の描画は,plot* 関数群の中から適合するものを選んで実行します.

drawFrame()

戻り値:なし

プロット領域の外枠を描画します.単純に,setPixRange() で指定された矩形領域の外周に線を引きます.

drawAxis(axis)

戻り値:なし
axis: 'x' または 'y'

x軸またはy軸を描きます.どちらの軸を描くかを axis で指定します. 原点 (0, 0) 以外で軸が交差する場合は,setAxisIntersection() であらかじめ交点を指定しておきます.

drawScale(axis[, tick])

戻り値:なし
axis: 'x' または 'y'
tick: 目盛間隔(省略可)

x軸またはy軸の目盛を描きます.どちらの軸を描くかを axis で指定します.目盛間隔(正の値)を tick で指定しますが,0以下を指定するか省略すれば自動的に目盛の数が多すぎず少なすぎない値に計算されます.


以下は,関数描画を実行する関数群です.現在のところ,関数指定による連続した折れ線,(x, y) データを折れ線でつないだもの,縦線,およびドット,縦線とドットの組み合わせを用意しています. どのタイプのグラフを描くかは,どの関数を呼び出すかによって明示的に指定します.

plotFunction(func[, min[, max]])

戻り値:なし
func: プロットする関数(必須).
min: 横軸最小値(省略可).省略時はプロットエリア左端.
min: 横軸最大値(省略可).省略時はプロットエリア右端.

指定した連続関数のグラフを描きます.指定する関数は,y = func(x) の形で,で,引数が1つ,戻り値が1つの関数です.引数としてxを与えたときの戻り値yで表される点 (x, y) を,setDivision() で指定した分割数の折れ線で描きます.

plotFunctionWithImpulse(func[, interval[, min[, max]]])

戻り値:なし
func: プロットする関数(必須).
interval: 横軸間隔(省略可).デフォルトは1.
min: 横軸最小値(省略可).省略時はプロットエリア左端.
min: 横軸最大値(省略可).省略時はプロットエリア右端.

指定した連続関数を interval 間隔でサンプリングしたものを,gnuplot の with impulses スタイルのように,縦線で描きます.指定する関数は,y = func(x) の形で,引数が1つ,戻り値が1つの関数です.

plotFunctionWithDisk(func[, interval[, min[, max]]])

戻り値:なし
func: プロットする関数(必須).
interval: 横軸間隔(省略可).デフォルトは1.
min: 横軸最小値(省略可).省略時はプロットエリア左端.
min: 横軸最大値(省略可).省略時はプロットエリア右端.

指定した連続関数を interval 間隔でサンプリングしたものを,gnuplot の with dots スタイルのように,丸印でプロットします.指定する関数は,y = func(x) の形で,引数が1つ,戻り値が1つの関数です.

plotDataXY(xdata, ydata)

戻り値:なし
xdata: プロットする点のx値を格納する配列.
ydata: プロットする点のy値を格納する配列.

(x, y) のデータ列を折れ線で結んだグラフを描きます.xdata, ydataの配列は,先頭からの順番が対応している必要があります.((x, y) のオブジェクトの配列にすることも検討中.)

plotDataXYWithImpulse(xdata, ydata)

戻り値:なし
xdata: プロットする点のx値を格納する配列.
ydata: プロットする点のy値を格納する配列.

(x, y) のデータ列を,gnuplot の with impulses スタイルのように,縦線で描きます.xdata, ydataの配列は,先頭からの順番が対応している必要があります.((x, y) のオブジェクトの配列にすることも検討中.)

plotDiscreteSignal(signal[, min[, max]])

戻り値:なし
signal: プロットするデータを格納するオブジェクト.
min: 横軸最小値(省略可).省略時はプロットエリア左端.
min: 横軸最大値(省略可).省略時はプロットエリア右端.

離散データの値を縦線と点により描画します.MATLAB の stem スタイル,gnuplot の with impulses と with dots の重ね打ちに相当します.signal は,x[-1] のような負のインデックス番号を使用可能にするため,配列ではなくオブジェクトになっています.オブジェクトのプロパティにインデックス(信号のサンプル番号)を,オブジェクトの値に信号値を格納します.インデックス番号に対する値が定義されていない場合は,ゼロと見なします.


以下は,Canvas API の描画コマンド moveTo, lineTo を,ピクセル座標指定ではなく変数値指定で行うために用意したものです.

moveTo(x, y)

戻り値:なし
x: 移動先の点のx座標
y: 移動積の点のy座標

context の moveTo(x, y) の座標指定を,ピクセル値ではなく変数値で行います.

lineTo(x, y)

戻り値:なし
x: 移動先の点のx座標
y: 移動積の点のy座標

context の lineTo(x, y) の座標指定を,ピクセル値ではなく変数値で行います.

ユーティリティ,オプション指定

val2pix_x(val)
val2pix_y(val)

戻り値:ピクセル座標値
val: 変数値

x座標,y座標それぞれの変数値をピクセル値に変換します.

setAxisIntersection(x, y)

戻り値:なし
x: 軸交点のx座標(変数値).
y: 軸交点のy座標(変数値).

x軸とy軸が交わる点 (x, y) の座標を変数値で指定します.

setTickLength(len)

戻り値:なし
len: 目盛の長さ(ピクセル値)

目盛の長さを指定します.デフォルトは 5 px.軸の両側に目盛を描く場合は,軸の上と下(または左と右)それぞれにこの長さの目盛が描かれます.

setTickStyle(axis, style)

戻り値:なし
axis: 'x' もしくは 'y'
style: x座標の場合,'both', 'upper', 'lower' のいずれか.y座標の場合,'both', 'left', 'right' のいずれか.デフォルトは 'both'.

目盛を軸のどちら側に描くかを指定します.

setDivision(div)

戻り値:なし
div: 関数グラフの線分分割数.

関数グラフを描くとき,曲線を分割する線分の数を指定します.デフォルトは100.

setBaseValue(val)

戻り値:なし
val: 縦線グラフの基準値.

plotFunctionWithImpulse(), plotDiscreteSignal() で縦線グラフを描くとき,縦の線分の関数またはデータではない端点の値を指定します.デフォルトは 0.

setDiskRadius(radius)

戻り値:なし
radius: 丸印の半径サイズ.

plotFunctionWithDisk(), plotDiscreteSignal() で描画する丸印のサイズ(半径)をドット数で指定します.デフォルトは 2 px.

クラス変数

基本的には内部処理用のつもりですが,SamplePlot オブジェクトの変数にはアクセスできます(できてしまいます).自分用なので,この辺はあまり考えて作っていません.

  // プロットエリアのCanvas上範囲.デフォルト:(0,0)-(100,100)
  pixRange.x.min = 0;
  pixRange.x.max = 100;
  pixRange.y.min = 0;
  pixRange.y.max = 100;

  // プロットエリアの対応座標値.デフォルト:(-1, -1)-(1, 1)
  valRange.x.min = -1;
  valRange.x.min = 1;
  valRange.y.min = -1;
  valRange.y.min = 1;

  // グラフの分割数デフォルト値
  division = 100;

  // 軸の交点座標.デフォルト:(0, 0)
  axisIntersectionX = 0;
  axisIntersectionY = 0;

  // 目盛の長さ.デフォルト:±5
  tickLength = 5;

  // 目盛間隔.デフォルト:1
  tickInterval = 1;

  // 目盛のスタイル.デフォルト:軸の両側
  tickStyle = {
      x: "both",
      y: "both",
  }

  // 縦線グラフの基準値.デフォルト:0
  baseValue = 0;

  // 目盛の最大値と最小値.
  endScale.min = -1;
  endScale.max =  1;

  // 点(円盤)による描画の半径
  diskRadius = 2;

サポートしていないもの

万が一機能追加の要望があれば検討してみますが,期待しないでください.HTML5 + JavaScript に初めて触って作ったものなので,入門者のスキルしかありません...

作成の経緯

よくできた chart library が既に世の中にあるにもかかわらずわざわざ素の canvas API で書こうと思ったのは,第一義的には html5 + JavaScript の練習問題としてです.他の人が作ったライブラリを利用するにしても,サンプルコードの Copy & Paste で終わらせるのではなく,基本を理解しておく必要があると思いました.実際,これを作った後,有名な chart library も使い始めるようになりました.

あえて理由付けをするなら,以下のようになります.

  1. 多くの chart library は数値データを用意してそれをライブラリに読み込ませる形式であり,教育目的に有用な関数プロットが,最初に探したときに適当なものが見つからなかったこと.本ライブラリは \(y=f(x)\) という関数を定義しておき,その関数を指定するとグラフを描いてくれるというタイプです.後で探したら,このようなタイプの関数プロットライブラリとしては,Function Plot などがありました.多機能でよくできています.
  2. ディジタル信号処理に関する講義を担当しており,この分野で説明のためによく使われる,縦棒の上に黒丸印がついた形のグラフが,簡単に使えそうな chart library のギャラリーの中に見つからなかったこと.MATLAB では stem コマンドで,gnuplot では with impulses と with dots オプションを使って重ね打ちして作るようなグラフです.d3.jsギャラリーの中に Lollipop Chart という名前でそれらしいのを見つけましたが,d3.js は私のような初心者にはちょっと敷居が高く,素のコードを書いた方が参考資料も多いのでてっとり早い.

ライブラリ履歴

2024.07.10 plotDataXYWithImpulse() 追加.
2022.04.01 plotFunctionWithDisk(), plotDiscreteSignal() において,diskRadiusの値が0の場合は黒丸を描画しないよう修正.
2020.08.01 setTickStyle() を追加.目盛を軸の両側にするか,片側にするかのオプション.
マニュアル(このファイル)書き直し.
2020.07.17 getContext() を追加.変数 ctx でアクセスできるので,あまり意味はない.
2020.06.23 2020.06.03 の変更と2020.06.21の変更をマージ
2020.06.21 縦線プロット (plotFunctionWithImpulse) の基準値指定を追加:setBaseValue()
2020.06.03 の変更が反映されていない.
2020.06.03 Contextへの描画命令,moveTo(), lineTo() を,画面座標ではなく変数座標で行う関数を追加.そのまま命令をContextへ引き渡すbeginPath(), stroke() も用意したが,たぶん意味はない.
2020.05.30 関数を指定して描画させるだけでなく,先に値を計算しておく形の折れ線グラフも必要になったため,plotDataXY() を追加.
2020.05.11 Edgeで実行したところ動作しない.classの書き方が間違っていたことに気づき,修正.
SimplePlot クラスのローカル変数宣言を,すべて Constructor の中に入れ,this.*** で宣言.むしろ,FirefoxとChromeで動いていたのが不思議.
2020.05.09 setFunction()廃止.plot***() の第1引数で明示的に指定する形に変更.
plotDiscreteSignal() 追加.第1引数は Array ではなく Object とし,インデックス番号に負を使用可能とする. 目盛間隔自動計算のバグ修正.
2020.05.05 いくつかの関数にデフォルト引数を導入.
2020.04.29 関数名変更:drawFunction → plotFunction
ディジタル信号処理の説明用に,plotFunctionWithImpulse, plotFunctionWithDisk を追加.
2020.04.26 枠描画 drawFrame を追加.
目盛間隔自動計算で,10のべき乗ごとだったのを,各桁1, 2, 5となるよう変更.
2020.04.22 いくつかの教材で共通に使えそうなコードをまとめ,ライブラリ化.
2020.04.10 2つめの教材(指数関数の形状)を作成.
2020.04.06 コロナウィルス感染拡大防止対策のため遠隔授業実施が濃厚となり,「動く教材」を作りたいと思い至る.教材作成に html5 + JavaScript が使えそうだと知り,初めての教材(正弦波グラフ)を作成.