技術文章その他

 

「関数電卓」アプリにおける陽関数グラフ描画
Nobuhide Tsuda
9-Oct-2016

概要

関数電卓」アプリを開発しリリースした。
三角関数・指数関数などを数式通りに入力し、計算結果を表示することができるのはもちろん、
式を自然な数式表示でき、陽関数グラフを描画する機能を実装した。
※ 世界で唯一の○○が□□する関数電卓だよ。
本稿では、その陽関数グラフ描画について述べる。

「関数電卓」

数式を入力し「=」を押すと計算結果を表示する。
※ 入力された数式(べき乗、ルート)は自然な表記で表示される。

グラフモードに切り替え、変数 x を用いた式(陽関数;y = f(x))を入力し、「=」を押すとグラフを描画。

グラフ描画

関数表記には「陽関数」「媒介変数関数」「陰関数」の3種類がある。

「媒介変数関数」「陰関数」のグラフ描画は「陽関数」に比べてグラフ描画の難易度が高い。

「陽関数」のグラフ描画

描画範囲([X1, X2), [Y1, Y2) )がわかっていれば、陽関数なので、各 x に対応する y はすぐに求めることができる。
単純なアルゴリズムは以下のようになる。

    for(x = X1; x < X2; x+=DX) {     // 描画範囲内の x について
        y = f(x);      //     y を計算
        if( f(x) が有効であれば  )
            (x, y) をプロット;
    }

※ 描画範囲は、画面座標系における座標系原点位置・スケールから決まる。
※ f(x) の計算は、式を構文解析し、演算子の優先順位を考慮して、項・式を評価するだけ。

上記アルゴリズムでは、abs(dy/dx) > 1 の時、グラフが連続しないという問題がある。
また、√(90-x^2) のように、f(x) が不連続な場合に、端点がプロットされない場合があるという問題もある。
さらに、Android 端末の場合、解像度はまちまちで、X1, X2 は実際の端末により異なるのだが、 cocos2d-x では座標系をすべて共通の論理座標系で指定するのが(おいらの中では)普通で、 それをちゃんと考慮していないとグラフの品質が劣化する。

直線補完

連続しない問題の解決策として、点を単にプロットするのではなく、それらの間を直線を描画する方法(線形補間)がある。

    直前点 = 空;
    for(x = X1; x < X2; x+=DX) {     // 描画範囲内の x について
        y = f(x);      //     y を計算
        if( f(x) が有効 ) {
            if( 直前点 != 空 )
                直線病が:直前点 - (x, y) 
            直前点 = (x, y);
        } else
            直前点 = 空
    }

上記のアルゴリズムでグラフは連続するのだが、今度は逆に tan(x) の x = 90度の部分ように連続して欲しくない場合にも、 グラフが連続してしまうという問題が発生する。

追跡法

端点が描画されないという問題を解決するために「追跡法」を実装した。
x を画面左から順に増やし、有効な点を見つけたら、-x 方向、+x 方向の両方に追跡し、プロットするようにした。

    for(x = X1; x < X2; ) {     // 描画範囲内の x について
        y = f(x);      //     y を計算
        if( f(x) が有効 ) {
        	-x 方向に対して f(x) が有効である間描画
        	+x 方向に対して f(x) が有効である間描画
        } else
        	x += DX;
    }

abs(dy/dx) > 1 の場合にもグラフを連続させるコード:

	//	(x, y) から +x 方向に対して f(x) が有効である間描画
	while( f(x) が有効 ) {
		(x, y) をプロット;
		if( abs(dy/dx) <= 1 )
			x += DX;
		else
			x += dx/dy;
	}

dy/dx は数式処理により正確に計算することもできるが、現状は ⊿y/⊿x | ⊿x = 0.000001 により計算している。
⊿x はもうっと小さくてもいいかもしれない。最適な⊿x の値は不明。

abs(dy/dx) > 1 の場合は、y をひとつ増やして、それに対応する x を求めればいいのだが、y = f(x) から x = g(y) を求めるのは容易ではない。

結果

√(90-x^2)、tan(x) など、たいていの場合でうまくいくようになった。

が、dy/dx の絶対値が極端に大きくなると計算誤差により、端点まで描画されなくなることがある。

まとめ、今後の課題

参考文献