JSXGraph/MathJaxの使い方:関数グラフのプロットと数式のタイプセッティングを行うJavaScriptライブラリ


ブラウザ上で関数グラフをプロットするJavaScriptライブラリJSXGraphと、数式をタイプセッティングしてくれるJavaScriptライブラリMathJaxの使い方のメモです。今回作ったサンプルの画面はこれです。

DEMO: https://codepen.io/kaz_hashimoto/pen/vYKvyVr

このDEMOでは、メニューで曲線の種類を選ぶとグラフと関数の数式を表示します。うち1つのグラフは、関数のパラメータをスライダーで調整できます。グラフが更新されると、下のカラーチャートにも反映されます。10個のセルの背景色は、ベースの色rgb(128,255,0)に対して、それぞれアルファチャネルにy = f(x) (x = n/10, n = 1〜10)の値を指定して得られた色です。

動作環境 (記事執筆時点)

  • JSXGraph v1.1.0
  • MathJax v3.1.2
  • jQuery 3.5.1
  • 動作確認ブラウザ: デスクトップ版 Chrome 86, Firefox 82, Safari 14, Opera 72
  • macOS 10.14 Mojave

JSXGraphとMathJaxの使い方

設定

JSXGraphとMathJaxライブラリはCDNから利用できます。HTMLファイルに追加する行は以下のとおり。

JSXGraph

html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsxgraph/1.1.0/jsxgraph.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsxgraph/1.1.0/jsxgraphcore.min.js"></script>

MathJax

html
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>

埋め込み先のdivを設置

グラフと数式を埋め込むために空のdivを2つ用意します。JSXGraphのAPIはグラフの描画領域を作成するのにHTML要素のidを必要とするため、グラフの出力先のdivにはid="plot"を付けました。

html
<div class="graph-wrap">
  <!-- グラフの出力先(JSXGraph用) -->
  <div id="plot" class="graph"></div>
  <!-- 数式の出力先(MathJax用) -->
  <div class="math"></div>
</div>

グラフの出力先のdiv.plot要素については、CSSであらかじめ領域を確保しておきます。このサンプルでは幅・高さ共に260pxに設定しました。

css
.graph-wrap {
  width: 260px;
}
.graph {
  width: 100%;
  height: 260px;
}

JSXGraphの使い方

Boardの作成

まず、プロット領域となるバウンディングボックスをJXG.JSXGraphのメソッドinitBoardを呼び出して作成します。第1引数は、プロット領域となるHTML要素のidです。領域のサイズは、左上、右下頂点の座標の成分を配列にしてboundingboxに指定します。

javascript
let board = JXG.JSXGraph.initBoard('plot', {
  boundingbox: [ -0.1, 1.1, 1.1, -0.1],  // 領域の座標[左、上、右、下]
  axis: true,  // 軸を表示する
  showNavigation: false,  // ナビゲーションボタンを表示しない
  showCopyright: false    // コピーライト文字列を表示しない
});

軸タイトルの追加

軸を表すタイトルを付けましょう。JSXGraph APIには軸タイトルを指定する機能が用意されてないようなので、Boardcreateメソッドを呼び出し、ジオメトリック要素textとしてboardに文字列を配置しました。

javascript
const text_css = 'font-family: "Times New Roman", Times, "serif"; font-style: italic';
board.create('text', [1.05, 0.08, 't'],
       { fontSize: 16, cssStyle: text_css });
board.create('text', [0.05, 1.05, 'y'],
       { fontSize: 16, cssStyle: text_css });

テキストの位置と内容は、create()の第2引数に配列で [ x座標, y座標, 文字列 ] の形式で指定します。テキストのスタイルは第3引数に指定するのですが、fontSize以外のスタイルはcssStyle項目の値として、CSSのルールセットを渡します。
参考)Wiki: Texts and Transformations

関数グラフのプロット

関数のグラフを描くには、Boardcreateメソッドの第1引数にfunctiongraphを指定して呼び出します。下記のコードは、関数bezier(t)(0 ≤ t ≤ 1)の曲線のグラフを描画します。

javascript
function bezier(t) {
  return t * t * (3 - 2 * t);
}

let graph = board.create('functiongraph', [bezier, 0, 1]);

スライダーの作成

グラフにスライダーを設置することができます。スライダーを作成するには、Boardcreateメソッドを第1引数にsliderを指定して呼び出します。下記のコードは、ラベルpを持つスライダーを目盛の開始位置の座標(0.2, 0.4) 、終了位置(0.8, 0.4)に配置し、値のレンジを1〜4、初期値を2に設定します。

javascript
let slider = board.create('slider', [[0.2, 0.4], [0.8, 0.4], [1, 2, 4] ], {name: 'p'});

スライダーの現在の値はValueメソッドで読み出します。dragイベントのハンドラを登録することにより、ハンドルを動かしている時のスライダーの値を取得できます。

javascript
slider.on('drag', function(e) {
  console.log('p=' + this.Value());
});

パラメータを含む関数のグラフ

次に、パラメータpを含む関数f(t, p)の曲線を描く方法です。pの値をスライダーで動かしながら曲線の形状の変化をグラフに反映させることができます。下記のコードは、パラメータpを含む関数parameterized(t)(0 ≤ t ≤ 1)の曲線1について、pの現在値をスライダーから読み取ってグラフを描画します。

javascript
function parameterized(t) {
  const p = slider.Value();
  const tp = t**p;
  return tp / (tp + (1 - t)**p);
}

graph = board.create('functiongraph', [parameterized, 0, 1]);

グラフの要素の消去

メニューでグラフを切り替えた時やスライダーでパラメータpの値を動かした時、グラフの内容を更新する前に、現在表示されている曲線などを消去する必要があります。でないと、前の曲線が画面に残ったままの状態で新たな曲線が追記されてしまいます。グラフの要素を消すには、BoardremoveObjectメソッドを呼び出します。

javascript
function clearGraph() {
  if (graph) { // 今表示されている曲線があれば消す
    board.removeObject(graph);
    graph = null;
  }
  if (slider) { // スライダーが表示されていれば消す
    board.removeObject(slider);
    slider = null;
  }
}

これでグラフが描けました! 次は数式を表示してみましょう。

MathJaxの使い方

MathJaxを使って数式を描画するには、出力先divのinnerHTMLに、LaTeXコマンドで記述した数式の文字列をセットし、MathJax.typeset()関数を呼び出します。下記のコードは、変数mathに設定したディスプレイ数式モードのLaTex形式文字列(\$\$...\$\$)をdiv.math要素のコンテントに書き込んだ後、MathJax.typeset()関数を使って数式を描画させます。タイプセットの更新前に現在の状態をクリアするため、最初にdiv.math要素に対してMathJax.typesetClear()関数を呼んでいます。

javascript
MathJax.typesetClear([$('.math').get(0)]);
const math = '$$f_{p}(t)=\\frac{t^p}{t^p+(1-t)^p}$$';
$('.math').html(math);
MathJax.typeset();

参考) Typesetting and Converting Mathematics

LaTexタイプセッティングの表示確認には、こちらのLive Demoが便利です。
https://www.mathjax.org/#demo

注)上記コード例で、「\\frac」のようにLaTexコマンドの前のバックスラッシュをエスケープしていますが、Live Demoのテキストエリアにはバックスラッシュを1個にして入力します。


  1. この関数の数式は、stackexchange記事 Ease-in-out function より引用