[ 前へ | 目次 | 次へ ]
Japanese English
Now Loading...
ダウンロード
PC (※スマートフォンでは動きません) でダウンロードし、ZIPファイルを右クリックメニューから展開して、できたフォルダ内の「 VCSSL.bat(バッチファイル) 」をダブルクリックすると起動します。 Linux等では「 VCSSL.jar 」をコマンド実行してください。
» 詳しい使用方法や、エラーで展開できない際の対応方法などはこちら

y = f(x,t) の形の数式を2Dグラフとしてアニメーション描画するツール

このVCSSLプログラムは、 入力欄に y = f(x,t) の形の数式を入力すると、 それを2次元のグラフにアニメーション描画してくれる簡易ツールです。

使用方法

ダウンロードと展開(解凍)

まず、PC(スマホは未対応)で上の画面の「 ダウンロード 」ボタンを押してください。 するとZIP形式で圧縮されたファイルがダウンロードされます。

Windows をご使用の方は、ここでまずZIPファイルを右クリックし、「プロパティ」を選んで開かれる画面で、 下の方にあるセキュリティ項目の「許可する」にチェックを入れて「OK」で閉じてください。 これを行わないと、ZIP展開やソフト起動時に、警告メッセージが出て展開完了/起動できない場合があります。

その後、ZIPファイルを右クリックして「すべて展開」や「ここに展開」などで展開(解凍)してください。 展開が成功すると、ZIPファイルと同じ名前のフォルダができ、その中にZIPファイルの中身が入っています。

» 展開がエラーで止まってしまう場合や、ファイル名が文字化けしてしまう場合は…

プログラムの起動

Windows をご使用の場合

上記でZIPファイルを展開したフォルダ内にある、以下のバッチファイルをダブルクリック実行してください:

VCSSL__ダブルクリックでプログラム実行.bat

もしプログラムを書き変えながら使いたい場合は、代わりに「 VCSSL_Editor__プログラム編集はこちら.bat 」を実行してください。

正常に起動できると、初回のみ、Java実行環境を入手するか等を尋ねられるので、適時答えて済ませると、プログラムが起動します。 2回目以降はすぐに起動します。

» うまく起動できずにエラーになってしまう場合は…

Linux 等をご使用の場合

ZIPファイルを展開したフォルダ内へコマンドライン端末で cd して、以下の通り入力して実行してください:

java -jar VCSSL.jar
(プログラムの内容を書き変えながら使いたい場合は、代わりに VCSSL_Editor.jar を実行)

» javaコマンドが使用できない等のエラーが表示される場合は…

起動後

画面の様子

起動すると、2つのウィンドウが立ち上がります。 左が数式などの入力画面、右下がグラフ画面です。 右上のアニメーション操作画面は、 入力画面に数式を入力して「 セット 」ボタンを押した時点で表示されます。

入力画面(左)とグラフ画面(右)
※ 画面のデザインや項目名などは、バージョンにより多少異なる場合があります。

数式を入力してグラフを描画する

まず、グラフに描きたい数式を、入力画面の「 y(x,t) = 」の入力欄に入力してください。 y = f(x,t) における f(x,t) の式です。 式の書き方のポイントは以下の通りです:

- 数式の書き方のポイント -
  • 式は半角(123などの、幅の細い文字)で書いてください。キーボード左上の「半角/全角」キーで切り替えられます。
  • 時間を表す変数には小文字の「 t 」を用いてください。
  • 「 x 」も大文字ではなく小文字を用いてください。
  • 四則演算が普通に使用できます。ただし、かけ算は「 * 」、割り算は「 / 」の記号を用いてください。足し算や引き算は普通に「 + 」と「 - 」を用います。
  • かけ算や割り算は、足し算や引き算よりも先に計算されます(手計算と同じです)。
  • sin や cos といった数学関数が使用できます。また、円周率の値は「 PI 」と書けばOKです。 使用可能な数学関数・定数の一覧は、Mathライブラリの仕様書ページをご覧ください。
  • 累乗は「 ** 」の記号を用いてください。例えば x の2乗なら「 x**2 」です。

式を入力したら、入力画面の「 セット 」ボタンを押すと、その式がグラフ画面上に描かれます。 グラフは、入力画面の「 x-N 」に指定した点数の折れ線グラフとして描画されます(点の無い線だけのグラフにする方法は後述)。 x-N の値が大きいほど滑らかになります。

アニメーション

上の手順で「 セット 」ボタンを押した時点で、画面右上にアニメーション操作画面が出現します。 アニメーション操作画面の「 PLAY 」ボタンを押すと、 入力した数式内の「 t 」を時間とみなして、グラフがアニメーションされます。

アニメーション中は、「 PLAY 」ボタンは表示が変わって「 STOP 」ボタンになり、押すとアニメーションが一時停止します。 アニメーション停止中は、ボタン横のスライダーで、時間を手動で自由に動かす事ができます。

その他の機能や細かい設定など

標準では、グラフの折れ線の節に点が打たれます。点の無いグラフにしたい場合は、グラフ画面の「 オプション 」 > 「 点プロット 」の選択を外してください。 線の幅を変えたい場合は、「 線プロット 」の選択を外して選択しなおすと、指定できます。

その他、グラフ画面の「 編集 」メニューなどから、グラフ範囲やタイトル・軸ラベル、凡例(各色の線ごとの表示名)などを設定できます。 また、グラフ画面の「 ファイル 」メニューから、設定内容の保存や、グラフ内容の画像ファイルへの保存などもできます。

なお、このプログラムで起動されるグラフ画面は、 「 リニアングラフ2D( RINEARN Graph 2D ) 」というデータ解析用のグラフソフトで、 単体でも入手・利用できます。操作感などが気に入った方は、ぜひご利用ください。

コード解説

コード全体

このプログラムのコードはVCSSLで記述されています。 VCSSLはC言語系のシンプルな文法を持っているので、C系の言語に触れられた事のある方なら、 コメントを参考にしながらコード内容を比較的簡単に追う or 改造する事ができると思います。

今回のコードは、基本的なグラフ操作の処理に、GUIの画面を組み合わせて、ツールとして仕上げたものです。 グラフの描画処理の基本や、ユーザーの入力した式からグラフを描く流れについては、以下の回で解説していますので、そちらをご参照ください:

- 前提となる処理の記事 -

また、VCSSLでのGUI画面の構築については、「 VCSSL GUI開発ガイド 」をご参照ください。

今回のコードは、サンプルコードというよりは実際に使うためのツールなので、 細部を細かくピックアップしながらの解説は割愛し、コード全体の掲載に留めておきます。 今回のコードの全体は、以下の通りです:


coding UTF-8;

import Math;
import Text;
import GUI;
import File;
import tool.Graph2D;


/** グラフに描画する数式 y(x,t) のデフォルト入力値です。 */
const string DEFAULT_Y_EXPRESSION = "sin(3*x-t)";

/** グラフのX範囲の最大値(右端)のデフォルト入力値です。 */
const string DEFAULT_X_MAX = "1.0";

/** グラフのX範囲の最小値(左端)のデフォルト入力値です。 */
const string DEFAULT_X_MIN = "-1.0";

/** グラフのプロット点数のデフォルト入力値です。 */
const string DEFAULT_X_N = "100";

/** グラフのY範囲の最大値(右端)のデフォルト入力値です。 */
const string DEFAULT_Y_MAX = "1.0";

/** グラフのY範囲の最小値(左端)のデフォルト入力値です。 */
const string DEFAULT_Y_MIN = "-1.0";

/** 時刻 t の最大値(終了値)のデフォルト入力値です。 */
const string DEFAULT_T_MAX = "100.0";

/** 時刻 t の最小値(開始値)のデフォルト入力値です。 */
const string DEFAULT_T_MIN = "0.0";

/** デフォルトのファイル保存先フォルダです。 */
const string DEFAULT_OUTPUT_DIRECTORY_PATH = "./output";

/** デフォルトの出力ファイル名(番号部を除く)です。 */
const string DEFAULT_OUTPUT_FILE_NAME_HEAD = "sample2d_";


/** アニメーションの間、数式 y(x,t) の内容を控えておく変数です。 */
string yExpression;

/** アニメーションの間、グラフのX範囲の最大値(右端)を控えておく変数です。 */
float xMax;

/** アニメーションの間、グラフのX範囲の最小値(左端)を控えておく変数です。 */
float xMin;

/** アニメーションの間、グラフのプロット点数を控えておく変数です。 */
int xN;

/** アニメーションの間、時刻 t の最大値(終了値)を控えておく変数です。 */
float tMax;

/** アニメーションの間、時刻 t の最小値(開始値)を控えておく変数です。 */
float tMin;


// 以下、アニメーション処理や制御用の変数

/** グラフにプロットする座標点列のX値を格納する配列です。 */
float xVertexArray[0];

/** グラフにプロットする座標点列のY値を格納する配列です。 */
float yVertexArray[0];

/** メインループの継続状態を保持します(falseにすると脱出)。 */
bool mainLoopState = true;

/** アニメーションの状態を保持します(true=アニメーション中、false=待機状態)。 */
bool animationState = false;

/** フレームカウンタ(アニメーションの描画回数の中で、何回目かを保持するカウンタ)です。 */
int frameCounter;

/** アニメーションを終了する最終フレームカウンタ値です。フレーム間の時刻変数 t の増分は、時間範囲をこの値で割ったものになります。 */
int frameCounterMax = 1000;

/** 時刻が1つ進む際の待ち時間(アニメーションウェイト)です。 */
int animationWait = 30;

/** グラフをプロットするリクエストフラグ(trueにするとメインループでプロットされ、falseに戻される)です。 */
bool plotRequest = false;

/** グラフ設定を再読み込みするリクエストフラグ(trueにするとメインループで読み込み処理が行われ、falseに戻される)です。 */
bool reloadRequest = false;

/** メインループで連番画像出力処理を行うためのフラグです。 */
bool animationExportRequest = false;


// 以下、グラフやGUIコンポーネントIDを格納する変数

/** グラフのIDを格納します。 **/
int graph = NULL;

/** 入力画面のウィンドウのIDを格納します。 */
int inputWindow = NULL;

/** y(x,t) の数式を入力するテキストフィールドのIDを格納します。 */
int expressionField = NULL;

/** X軸の最大値を入力するテキストフィールドのIDを格納します。 */
int xMaxField = NULL;

/** X軸の最小値を入力するテキストフィールドのIDを格納します。 */
int xMinField = NULL;

/** グラフのプロット点数を入力するテキストフィールドのIDを格納します。 */
int xNField = NULL;

/** Y軸の最大値を入力するテキストフィールドのIDを格納します。 */
int yMaxField = NULL;

/** Y軸の最小値を入力するテキストフィールドのIDを格納します。 */
int yMinField = NULL;

/** Y軸の範囲を自動調整するかどうかを選択するチェックボックスのIDを格納します。 */
int yAutoRangeBox = NULL;

/** 時刻 t の最大値(終了値)を入力するテキストフィールドのIDを格納します。 */
int tMaxField = NULL;

/** 時刻 t の最小値(開始値)を入力するテキストフィールドのIDを格納します。 */
int tMinField = NULL;

/** SETボタンのIDを格納します。 */
int setButton = NULL;

/** 画像出力ボタンのIDを格納します。 */
int outputButton = NULL;

/** EXITボタンのIDを格納します。 */
int exitButton = NULL;

/** 画像出力フォルダ指定フィールドのIDを指定します。 */
int outputPathField = NULL;

/** 画像出力フォルダ選択ボタンのIDを指定します。 */
int outputPathSelectButton = NULL;

/** アニメーション操作ウィンドウのIDを格納します。 */
int animationWindow = NULL;

/** アニメーションの「PLAY」/「STOP」ボタンのIDを格納します。 */
int animationButton = NULL;

/** アニメーションの時間操作スライダーのIDを格納します。 */
int animationSlider = NULL;

/** アニメーションの時間表示ラベルのIDを格納します。 */
int animationLabel = NULL;



/**
 * プログラムの開始時に自動で実行されます。
 */
void main(){

	// GUIを備えたツールではコンソール画面は邪魔なので非表示化
	hide();

	// システム側で標準接続されている2DグラフソフトがあればIDを取得(無ければnewGraph2D関数同様、新規に立ち上がる)
	graph = getGraph2D();

	// このプログラムでは、既存のグラフ内容に重ね描きする用途は考えにくいので、最初に内容をクリアしておく
	clearGraph2D(graph);

	// ※ 普通に newGraph2D 関数で新規生成しないのは、このプログラムがリニアングラフ2Dにも同梱されるツールであり、
	//    リニアングラフ2D上でメニューからこのプログラムを実行した際に、そのグラフ自身を制御対象とするためです。

	// グラフ画面の位置とサイズを設定
	setGraph2DLocation(graph, 330, 120);
	setGraph2DSize(graph, 720, 600);

	// 設定画面を起動
	createInputWindow();


	// メインループ ― プログラム終了までずっと繰り返し、必要なら処理を行い、何も無ければ待機する
	while( mainLoopState ){

		// アニメーションの連番画像出力がリクエストされている場合は、その処理を行う
		if ( animationExportRequest ) {
			string outputDirectoryPath = getComponentText(outputPathField);
			if (!exists(outputDirectoryPath)) {
				alert("指定された保存先フォルダ「" + outputDirectoryPath + "」が存在しません。");
			} else if (!isdir(outputDirectoryPath)) {
				alert("指定された保存先フォルダ「" + outputDirectoryPath + "」はフォルダではありません。");
			} else {
				outputDirectoryPath = getFilePath(outputDirectoryPath);
				outputImages(outputDirectoryPath);
			}
			frameCounter = 0;
			plotRequest = true;
			animationExportRequest = false;
		}

		// アニメーション再生時の状態更新処理
		if( animationState ){
			frameCounter++;
			if( frameCounter <= frameCounterMax ){
				setComponentValueInt( animationSlider, frameCounter );
			}else{
				setAnimationState( false );
			}
		}

		// 状態の反映
		if ( reloadRequest ) {
			reloadRange();
			plotGraph( frameCounter );
			setTimer(0);
			setComponentVisible( animationWindow, true );
			reloadRequest = false;
		}

		// グラフのプロット
		if( plotRequest ){
			plotGraph( frameCounter );
			plotRequest = false;
		}

		sleep( animationWait );
	}

	// メインループを脱出すれば終了
	exit();
}


/**
 * 現在のyExpressionの内容をグラフにプロットします。
 * 
 * @param frameIndex アニメーション始点を 0 とする、プロット対象フレームのインデックス
 */
void plotGraph(int frameIndex){

	// 現在の時刻カウンタ(フレームカウンタ)から、時刻の値を求める
	float dt = (tMax-tMin)/frameCounterMax;
	float t = tMin + frameIndex * dt;

	// グラフに転送するデータ配列を n 要素で確保
	alloc[xN] xVertexArray;
	alloc[xN] yVertexArray;

	// 座標を計算し、データ配列に数値を代入
	float x;
	float y;

	float dx = (xMax-xMin)/(xN-1);
	for( int i=0; i<xN; i++ ){
		x = xMin + i * dx;
		y = feval( yExpression, 0.0 );
		xVertexArray[ i ] = x;
		yVertexArray[ i ] = y;
	}

	// データ配列をグラフソフトウェアに転送し、プロット
	setGraph2DData( graph, xVertexArray, yVertexArray );
	t = round(t, 8, HALF_UP_SIGNIF);
	setComponentText( animationLabel, "t = " + t );
}


/**
 * グラフの領域を再設定します。
 */
void reloadRange(){

	bool fixRange = getComponentValueBool( yAutoRangeBox );

	if( fixRange ){

		hideGraph2D( graph );

		float yRange[] = getRangeY();
		yRange[0] = round(yRange[0], 5, HALF_UP_SIGNIF);
		yRange[1] = round(yRange[1], 5, HALF_UP_SIGNIF);

		setGraph2DAutoRange( graph, false, false );
		setGraph2DRange( graph, xMin, xMax, yRange[0], yRange[1] );

		setComponentText( yMinField, ""+yRange[0] );
		setComponentText( yMaxField, ""+yRange[1] );

		showGraph2D( graph );

	}else{

		if( !evaluable( getComponentText( yMaxField ), 0.0 ) ){
			alert("y-maxの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( yMinField ), 0.0 ) ){
			alert("y-minの式に誤りがあります。");
			return;
		}
		float yMax = getComponentText( yMaxField );
		float yMin = getComponentText( yMinField );
		setGraph2DAutoRange( graph, false, false );
		setGraph2DRange( graph, xMax, xMin, yMax, yMin );
	}
}


/**
 * 現在の設定状態での、yの最大・最小値を計算して返します。
 * 
 * @return yの最小値を[0],最大値を[1]に格納する配列
 */
double[] getRangeY(){
	float yMin = 1.0E30;
	float yMax = 1.0E-30;

	int rate = 0;
	int rateStock = 0;

	float dt = (tMax-tMin)/frameCounterMax;
	float dx = (xMax-xMin)/(xN-1);

	for( int frameCounter=0; frameCounter<=frameCounterMax; frameCounter++ ){
		for( int i=0; i<xN; i++ ){
			float t = tMin + frameCounter * dt;
			float x = xMin + i * dx;
			float y = feval( yExpression, 0.0 );
			if( y < yMin ){
				yMin = y;
			}
			if( y > yMax ){
				yMax = y;
			}
		}

		rate = frameCounter*10 / (frameCounterMax-1);
		if( rate != rateStock ){
			setComponentText( setButton, "計算中: " + rate + "0 %" );
		}
		rateStock = rate;
	}
	setComponentText( setButton, "セット" );
	clear();

	float range[] = { yMin, yMax };
	return range;
}


/**
 * アニメーションのON/OFF状態を切り替えます。 * 
 * 
 * @param state アニメーションのON/OFF状態(ONならtrue)
 */
void setAnimationState( bool state ){

	animationState = state;

	if( state ){

		// タイマーが最終時刻なら、時刻を初期化
		if( getComponentValueInt( animationSlider ) == frameCounterMax ){
			resetTimer();
		}
		setComponentText( animationButton, "STOP" );
	}else{
		setComponentText( animationButton, "PLAY" );
	}
}


/**
 * アニメーション中の時刻を設定します(自動でプロットされます)。
 * 
 * @param t 設定する時刻
 */
void setTimer( int t ){
	frameCounter = t;
	plotRequest = true;
}


/**
 * 時刻を初期化します(アニメーション終了時に使用)。
 */
void resetTimer(){
	frameCounter = 0;
	setComponentText( animationLabel, "t = " + tMin );
	setComponentInt( animationSlider, 0 );
	return;
}


/**
 * グラフを連番画像として保存します。
 * 
 * @param outputDirectoryPath 保存フォルダのパス
 */
void outputImages(string outputDirectoryPath) {
	
	// カーソルを描画させないためにグラフ画面を非表示にする
	hideGraph2D(graph);
	
	if (animationWindow == NULL) {
		popup("先に「 セット 」を行い、設定内容をアニメーションに反映させてください。");
		showGraph2D(graph);
		return;
	}
	
	string outputFileNameHead = input("保存ファイル名(番号部を除く)を入力", DEFAULT_OUTPUT_FILE_NAME_HEAD);
	if (outputFileNameHead == NULL) {
		showGraph2D(graph);
		return;
	}
	
	// 操作できないように設定画面を非表示にし、代わりに進捗を表示するためコンソールを表示する
	hideComponent(animationWindow);
	hideComponent(inputWindow);
	show();
	
	setComponentText(outputButton, "保存中...");
	
	setAnimationState(false);
	plotRequest = false;
	
	popup(
		"アニメーションの全フレーム(コマ)のグラフを、連番の画像ファイルとして保存します。" + EOL + 
		"これには少し時間がかかる事があります。" + EOL + 
		"「 OK 」を押した後は、操作せずに完了までしばらくお待ちください。"
	);

	
	// ファイルをプロットして画像に保存、を全ての入力ファイルに対してくり返す
	sleep(1000);
	for (int frameIndex=0; frameIndex<=frameCounterMax; frameIndex++) {
		plotGraph(frameIndex);
		string outputFileName = outputFileNameHead + frameIndex + ".png";
		string outputFilePath = getFilePath(outputFileName, outputDirectoryPath);
		println("画像保存(" + frameIndex + "/" + frameCounterMax + "): " + outputFilePath);
		exportGraph2D(graph, outputFilePath, "PNG");
		sleep(20);
	}


	// 処理が終わったのでグラフ画面や設定画面を表示し、コンソールは邪魔なので非表示にする
	showGraph2D(graph);
	showComponent(animationWindow);
	showComponent(inputWindow);
	hide();

	setComponentText(outputButton, "画像保存");
	popup("保存しました。保存先フォルダ: " + EOL + outputDirectoryPath);

	frameCounter = 0;
	setComponentValueInt( animationSlider, 0 );
}


/**
 * 入力画面を起動します。
 */
void createInputWindow(){
	
	int leftWidth = 110;
	int rightX = leftWidth + 10;
	int rightWidth = 160;
	int buttonWidth = 270;
	int fontSize = 20;

	inputWindow = newWindow( 0, 0, 320, 630, "入力画面" );

	int expressionLabel = newTextLabel( 10, 10, leftWidth, 25, "y( x, t ) =" );
	setComponentFontSize(expressionLabel, fontSize);
	mountComponent( expressionLabel, inputWindow );

	expressionField = newTextField( rightX, 10, rightWidth, 25, DEFAULT_Y_EXPRESSION );
	setComponentFontSize(expressionField, fontSize);
	mountComponent( expressionField, inputWindow );

	int xMaxLabel = newTextLabel( 10, 60, leftWidth, 25, "x-max =" );
	setComponentFontSize(xMaxLabel, fontSize);
	mountComponent( xMaxLabel, inputWindow );

	xMaxField = newTextField( rightX, 60, rightWidth, 25, DEFAULT_X_MAX );
	setComponentFontSize(xMaxField, fontSize);
	mountComponent( xMaxField, inputWindow );

	int xMinLabel = newTextLabel( 10, 90, leftWidth, 25, "x-min =" );
	setComponentFontSize(xMinLabel, fontSize);
	mountComponent( xMinLabel, inputWindow );

	xMinField = newTextField( rightX, 90, rightWidth, 25, DEFAULT_X_MIN );
	setComponentFontSize(xMinField, fontSize);
	mountComponent( xMinField, inputWindow );


	int xNLabel = newTextLabel( 10, 120, leftWidth, 25, "x-N =" );
	setComponentFontSize(xNLabel, fontSize);
	mountComponent( xNLabel, inputWindow );

	xNField = newTextField( rightX, 120, rightWidth, 25, DEFAULT_X_N );
	setComponentFontSize(xNField, fontSize);
	mountComponent( xNField, inputWindow );


	int tMaxLabel = newTextLabel( 10, 160, leftWidth, 25, "t-max = " );
	setComponentFontSize(tMaxLabel, fontSize);
	mountComponent( tMaxLabel, inputWindow );

	tMaxField = newTextField( rightX, 160, rightWidth, 25, DEFAULT_T_MAX );
	setComponentFontSize(tMaxField, fontSize);
	mountComponent( tMaxField, inputWindow );

	int tMinLabel = newTextLabel( 10, 190, leftWidth, 25, "t-min = " );
	setComponentFontSize(tMinLabel, fontSize);
	mountComponent( tMinLabel, inputWindow );

	tMinField = newTextField( rightX, 190, rightWidth, 25, DEFAULT_T_MIN );
	setComponentFontSize(tMinField, fontSize);
	mountComponent( tMinField, inputWindow );


	yAutoRangeBox = newCheckBox( 10, 250, 500, 20, "Y範囲を自動設定", true );
	mountComponent( yAutoRangeBox, inputWindow );


	int yMaxLabel = newTextLabel( 10, 280, leftWidth, 25, "y-max = " );
	setComponentFontSize(yMaxLabel, fontSize);
	mountComponent( yMaxLabel, inputWindow );

	yMaxField = newTextField( rightX, 280, rightWidth, 25, DEFAULT_Y_MAX );
	setComponentFontSize(yMaxField, fontSize);
	mountComponent( yMaxField, inputWindow );

	int yMinLabel = newTextLabel( 10, 310, leftWidth, 25, "y-min = " );
	setComponentFontSize(yMinLabel, fontSize);
	mountComponent( yMinLabel, inputWindow );

	yMinField = newTextField( rightX, 310, rightWidth, 25, DEFAULT_Y_MIN );
	setComponentFontSize(yMinField, fontSize);
	mountComponent( yMinField, inputWindow );


	setButton = newButton( 10, 360, buttonWidth, 50, "セット" );
	setComponentFontSize(setButton, fontSize);
	mountComponent( setButton, inputWindow );

	outputButton = newButton( 10, 420, buttonWidth, 50, "画像保存" );
	setComponentFontSize(outputButton, fontSize);
	mountComponent( outputButton, inputWindow );

	int outputPathLabel = newTextLabel( 10, 475, 80, 24, "保存場所 =");
	mountComponent( outputPathLabel, inputWindow );

	outputPathField  = newTextField( 90, 475, 120, 24, DEFAULT_OUTPUT_DIRECTORY_PATH);
	mountComponent( outputPathField, inputWindow );

	outputPathSelectButton = newButton( 210, 475, 70, 24, "選択" );
	mountComponent( outputPathSelectButton, inputWindow );

	exitButton = newButton( 10, 520, buttonWidth, 50, "終了" );
	setComponentFontSize(exitButton, fontSize);
	mountComponent( exitButton, inputWindow );
}


/**
 * アニメーション操作画面を起動します。
 */
void createAnimationWindow(){

	animationWindow = newWindow( 330, 0, 500, 120, "アニメーション操作画面" );

	animationButton = newButton( 10, 10, 100, 50, "PLAY" );
	mountComponent( animationButton, animationWindow );

	animationSlider = newHorizontalSlider( 120, 10, 300, 30, 0, frameCounterMax, 0 );
	mountComponent( animationSlider, animationWindow );

	animationLabel = newTextLabel( 125, 40, 300, 20, "" );
	mountComponent( animationLabel, animationWindow );
}


/**
 * 入力画面上の入力項目において、式として評価するのに適さない文字を、適切な文字で置き換えます。
 */
void correctCharactersOnInputWindow() {
	
	setComponentText(
		expressionField, 
		correctCharactersInExpression(
			getComponentText(expressionField)
		)
	);
	
	setComponentText(
		xMaxField, 
		correctCharactersInExpression(
			getComponentText(xMaxField)
		)
	);
	
	setComponentText(
		xMinField, 
		correctCharactersInExpression(
			getComponentText(xMinField)
		)
	);
	
	setComponentText(
		xNField, 
		correctCharactersInExpression(
			getComponentText(xNField)
		)
	);
	
	setComponentText(
		tMaxField, 
		correctCharactersInExpression(
			getComponentText(tMaxField)
		)
	);
	
	setComponentText(
		tMinField, 
		correctCharactersInExpression(
			getComponentText(tMinField)
		)
	);
}


/**
 * 文字列内で、式として評価するのに適さない文字を、適切な文字で置き換えます。
 * 
 * @param 式の内容を格納する文字列
 * @return 適切に文字を置き換えた文字列
 */
string correctCharactersInExpression(string expression) {
	string result = expression;
	
	result = replaceText(result, "×", "*", Text.ALL);
	result = replaceText(result, "÷", "/", Text.ALL);
	result = replaceText(result, "^", "**", Text.ALL);
	result = replaceText(result, "^",  "**", Text.ALL);
	
	result = replaceText(result, "+", "+", Text.ALL);
	result = replaceText(result, "*", "*", Text.ALL);
	result = replaceText(result, "/", "/", Text.ALL);
	result = replaceText(result, "ー", "-", Text.ALL);
	result = replaceText(result, "−", "-", Text.ALL);
	result = replaceText(result, "―", "-", Text.ALL);
	result = replaceText(result, "‐", "-", Text.ALL);
	result = replaceText(result, "ー", "-", Text.ALL);
	result = replaceText(result, "%", "%", Text.ALL);
	result = replaceText(result, "(", "(", Text.ALL);
	result = replaceText(result, ")", ")", Text.ALL);
	
	result = replaceText(result, "A", "A", Text.ALL);
	result = replaceText(result, "B", "B", Text.ALL);
	result = replaceText(result, "C", "C", Text.ALL);
	result = replaceText(result, "D", "D", Text.ALL);
	result = replaceText(result, "E", "E", Text.ALL);
	result = replaceText(result, "F", "F", Text.ALL);
	result = replaceText(result, "G", "G", Text.ALL);
	result = replaceText(result, "H", "H", Text.ALL);
	result = replaceText(result, "I", "I", Text.ALL);
	result = replaceText(result, "J", "J", Text.ALL);
	result = replaceText(result, "K", "K", Text.ALL);
	result = replaceText(result, "L", "L", Text.ALL);
	result = replaceText(result, "M", "M", Text.ALL);
	result = replaceText(result, "N", "N", Text.ALL);
	result = replaceText(result, "O", "O", Text.ALL);
	result = replaceText(result, "P", "P", Text.ALL);
	result = replaceText(result, "Q", "Q", Text.ALL);
	result = replaceText(result, "R", "R", Text.ALL);
	result = replaceText(result, "S", "S", Text.ALL);
	result = replaceText(result, "T", "T", Text.ALL);
	result = replaceText(result, "U", "U", Text.ALL);
	result = replaceText(result, "V", "V", Text.ALL);
	result = replaceText(result, "W", "W", Text.ALL);
	result = replaceText(result, "X", "X", Text.ALL);
	result = replaceText(result, "Y", "Y", Text.ALL);
	result = replaceText(result, "Z", "Z", Text.ALL);
	
	result = replaceText(result, "a", "a", Text.ALL);
	result = replaceText(result, "b", "b", Text.ALL);
	result = replaceText(result, "c", "c", Text.ALL);
	result = replaceText(result, "d", "d", Text.ALL);
	result = replaceText(result, "e", "e", Text.ALL);
	result = replaceText(result, "f", "f", Text.ALL);
	result = replaceText(result, "g", "g", Text.ALL);
	result = replaceText(result, "h", "h", Text.ALL);
	result = replaceText(result, "i", "i", Text.ALL);
	result = replaceText(result, "j", "j", Text.ALL);
	result = replaceText(result, "k", "k", Text.ALL);
	result = replaceText(result, "l", "l", Text.ALL);
	result = replaceText(result, "m", "m", Text.ALL);
	result = replaceText(result, "n", "n", Text.ALL);
	result = replaceText(result, "o", "o", Text.ALL);
	result = replaceText(result, "p", "p", Text.ALL);
	result = replaceText(result, "q", "q", Text.ALL);
	result = replaceText(result, "r", "r", Text.ALL);
	result = replaceText(result, "s", "s", Text.ALL);
	result = replaceText(result, "t", "t", Text.ALL);
	result = replaceText(result, "u", "u", Text.ALL);
	result = replaceText(result, "v", "v", Text.ALL);
	result = replaceText(result, "w", "w", Text.ALL);
	result = replaceText(result, "x", "x", Text.ALL);
	result = replaceText(result, "y", "y", Text.ALL);
	result = replaceText(result, "z", "z", Text.ALL);
	
	result = replaceText(result, "1", "1", Text.ALL);
	result = replaceText(result, "2", "2", Text.ALL);
	result = replaceText(result, "3", "3", Text.ALL);
	result = replaceText(result, "4", "4", Text.ALL);
	result = replaceText(result, "5", "5", Text.ALL);
	result = replaceText(result, "6", "6", Text.ALL);
	result = replaceText(result, "7", "7", Text.ALL);
	result = replaceText(result, "8", "8", Text.ALL);
	result = replaceText(result, "9", "9", Text.ALL);
	result = replaceText(result, "0", "0", Text.ALL);
	result = replaceText(result, ".", ".", Text.ALL);
	
	return result;
}


/**
 * ボタンが押された際にコールされます(イベントハンドラ)
 * 
 * @param id 押されたボタンのID
 */
void onButtonClick( int id ){

	// 「SET」ボタンが押された場合
	if( id == setButton ){

		// 画面上に入力されている文字列内で、式として評価するのに適さない文字を、適切な文字で置き換え
		correctCharactersOnInputWindow();

		// 以下、画面から入力内容を取得

		yExpression = getComponentText( expressionField );

		if( !evaluable( getComponentText( xMaxField ), 0.0 ) ){
			alert("x-maxの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( xMinField ), 0.0 ) ){
			alert("x-minの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( xNField ), 0.0 ) ){
			alert("x-Nの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( tMaxField ), 0.0 ) ){
			alert("t-maxの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( tMinField ), 0.0 ) ){
			alert("t-minの式に誤りがあります。");
			return;
		}

		xMax = feval( getComponentText( xMaxField ), 0.0);
		xMin = feval( getComponentText( xMinField ), 0.0);
		xN = feval( getComponentText( xNField ), 0.0);
		tMax = feval( getComponentText( tMaxField ), 0.0);
		tMin = feval( getComponentText( tMinField ), 0.0);

		float x, t;
		if( !evaluable(yExpression, 0.0) ){
			alert("f(x,t)の式に誤りがあります。");
			return;
		}

		if( animationWindow == NULL ){
			createAnimationWindow();
			setComponentVisible(animationWindow, false);
		}

		setAnimationState( false );
		resetTimer();
		reloadRequest = true;
		
		return;
	}

	// 「画像保存」ボタンが押された場合
	if( id == outputButton ){
		// このフラグを有効化すると、メインループが連番画像出力処理を行う
		animationExportRequest = true;
		return;
	}

	// 画像保存先フォルダの選択ボタンを押した際
	if ( id == outputPathSelectButton ) {
		string path = choose();
		while (!isdir(path)) {
			alert("選択されたものがフォルダではありません。" + EOL + "画像を保存するフォルダを選択してください。");
			path = choose();
		}
		setComponentText(outputPathField, path);
	}

	// 「EXIT」ボタンが押された場合
	if( id == exitButton ){
		animationExportRequest = false;
		mainLoopState = false;
		return;
	}

	// 「PLAY/STOP」ボタンが押された場合
	if( id == animationButton ){
		if( animationState ){
			setAnimationState( false );
		}else{
			setAnimationState( true );
		}
		return;
	}
}


/**
 * スライダーが動かされた際にコールされます(イベントハンドラ)
 * 
 * @param id 動かされたスライダーのID
 */
void onSliderMove( int id, int value ){
	if( id == animationSlider ){
		setTimer( value );
	}
}


/**
 * ウィンドウが閉じられた際にコールされます(イベントハンドラ)
 * 
 * @param id 閉じられたウィンドウのID
 */
void onWindowClose( int id ){
	
	// 入力ウィンドウの場合
	if( id == inputWindow ){

		// このプログラムでは範囲の自動調整を無効化しているので、終了時に戻しておく
		setGraph2DAutoRange( graph, true, true );

		// メインループを脱出、プログラムを終了させる
		mainLoopState = false;

	// アニメーションウィンドウの場合
	}else if( id == animationWindow ){
		
		// アニメーション状態を解除して待機状態に移行
		animationState = false;
	}
}


/**
 * グラフが閉じられた際にコールされます(イベントハンドラ)
 * 
 * @param id 閉じられたグラフのID
 */
void onGraph2DClose( int id ){
	if( id == graph ){
		animationState = false; // アニメーション状態を解除して待機状態に移行
	}
}
download/YxtGraph2D.vcssl

ライセンス

このVCSSL/Vnanoコード( 拡張子が「.vcssl」や「.vnano」のファイル )は実質的な著作権フリー(パブリックドメイン) である CC0 の状態で公開しています。 記事中にC言語/C++/Java言語などでのサンプルコードが掲載されいてる場合は、それらについても同様です。 そのままでのご利用はもちろん、改造や流用などもご自由に行ってください。

※ ただし、このコードの配布フォルダ内には、ダウンロード後すぐに実行できるように、 VCSSLの実行環境も同梱されており、そのライセンス文書は「 License 」フォルダ内に同梱されています (要約すると、商用・非商用問わず自由に使用できますが、使用の結果に対して開発元は一切の責任を負いません、といった具合の内容です)。 配布フォルダ内の各構成物の一覧やライセンスについては「 ReadMe_使用方法_必ずお読みください.txt 」をご参照ください。

※ Vnano の実行環境については、別途スクリプトエンジンのソースコードも一般公開しており、 何らかのソフトウェア内に組み込んでご利用いただく事も可能です。詳細はこちらをご参照ください。

この記事中の商標などについて

  • OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録商標である場合があります。
  • Windows は、米国 Microsoft Corporation の米国およびその他の国における登録商標です。この記事は独立著作物であり、Microsoft Corporation と関連のある、もしくはスポンサーを受けるものではありません。
  • Linux は、Linus Torvalds 氏の米国およびその他の国における商標または登録商標です。
  • その他、文中に使用されている商標は、その商標を保持する各社の各国における商標または登録商標です。

Japanese English
[ 前へ | 目次 | 次へ ]
Vnano版 | 積分値のグラフ描画用データを出力するプログラム

数値的に積分を行い、結果の関数をグラフに描くためのデータを出力するコードです。
3Dグラフを回転アニメーションさせるツール

3Dグラフを、Z軸まわりにゆっくりと回転アニメーションさせるツールです。全角度のグラフを、連番の画像ファイルに保存する事もできます。
連番ファイルから3Dグラフをアニメーション描画するツール

フォルダ内の連番データファイルを読み込み、3Dグラフを高速で連続描画して、アニメーションさせるツールです。グラフを連番の画像ファイルに保存する事もできます。
連番ファイルから2Dグラフをアニメーション描画するツール

フォルダ内の連番データファイルを読み込み、2Dグラフを高速で連続描画して、アニメーションさせるツールです。グラフを連番の画像ファイルに保存する事もできます。
z = f(x,y,t) の形の数式を3Dグラフとしてアニメーション描画するツール

入力欄に z = f(x,y,t) の形の数式を入力すると、それを3次元のグラフにアニメーション描画してくれる簡易ツールです。
y = f(x,t) の形の数式を2Dグラフとしてアニメーション描画するツール

入力欄に y = f(x,t) の形の数式を入力すると、それを2次元のグラフにアニメーション描画してくれる簡易ツールです。
t を媒介変数とする x(t), y(t), z(t) の数式を3Dグラフに描画し、アニメーションもできるツール

入力欄に x(t), y(t), z(t) の形の数式を入力すると、それを t を媒介変数として3次元のグラフに描画し、アニメーションもできる簡易ツールです。
t を媒介変数とする x(t), y(t) の数式を2Dグラフに描画し、アニメーションもできるツール

入力欄に x(t), y(t) の形の数式を入力すると、それを t を媒介変数として2次元のグラフに描画し、アニメーションもできる簡易ツールです。
z = f(x,y) の形の数式を3Dグラフとして描画するツール

入力欄に z = f(x,y) の形の数式を入力すると、それを3次元のグラフに描いてくれる簡易ツールです。
y = f(x) の形の数式を2Dグラフとして描画するツール

入力欄に y = f(x) の形の数式を入力すると、それを2次元のグラフに描いてくれる簡易ツールです。
配列を3Dグラフにアニメーションプロットする(曲面/メッシュグラフ)

座標値配列の内容を、3次元の曲面/メッシュグラフに連続でプロットし、アニメーションさせるサンプルプログラムです。
配列を3Dグラフにアニメーションプロットする(点/線グラフ)

座標値配列の内容を、3次元の点/線グラフに連続でプロットし、アニメーションさせるサンプルプログラムです。
配列を2Dグラフにアニメーションプロットする

座標値配列の内容を、2次元グラフに連続でプロットし、アニメーションさせるサンプルプログラムです。
配列を3Dグラフにプロットする(曲面/メッシュグラフ)

座標値配列の内容を、3次元の曲面/メッシュグラフにプロットするサンプルプログラムです。
ファイルを3Dグラフにプロットする(曲面/メッシュグラフ)

座標値ファイルの内容を、3次元の曲面/メッシュグラフにプロットするサンプルプログラムです。
ユーザーが入力した数式を2Dグラフにプロットする

実行時にユーザーが入力した数式の値を、2次元グラフにプロットするサンプルプログラムです。
配列を3Dグラフにプロットする(点/線グラフ)

座標値配列の内容を、3次元の点/線グラフにプロットするサンプルプログラムです。
配列を2Dグラフにプロットする

座標値配列の内容を、2次元グラフにプロットするサンプルプログラムです。
ファイルを3Dグラフにプロットする(点/線グラフ)

座標値ファイルの内容を、3次元の点/線グラフにプロットするサンプルプログラムです。
ファイルを2Dグラフにプロットする

座標値ファイルの内容を、2次元グラフにプロットするサンプルプログラムです。
この階層の目次
[ 前へ | 目次 | 次へ ]
RINEARN からのお知らせ
※ VCSSL は RINEARN が開発しています。

VCSSLの最新版をリリース: 外部プログラムとの連携機能を少し強化、他
2025-05-25 - VCSSL 3.4.52をリリースしました。外部プログラム(C言語製の実行ファイル等)との連携機能を少し強化し、文字化け対策やOS判別などを可能にしました。他にも細かい機能追加があります。詳細をお知らせします。

VCSSLの最新版をリリース、Java 24上での非互換な挙動を対処
2025-04-22 - VCSSL 3.4.50をリリースしました。Java 24環境上でのネットワークドライブ関連のファイルパス解決で、従来環境とは異なる挙動が生じていたのを解消しました。詳細をお知らせします。

リニアングラフやVCSSLの最新版をリリース、目盛りの位置や内容を自由に指定可能に!
2024-11-24 - リニアングラフ3D/2Dを更新し、自由な位置に、自由な表記内容の目盛りを描けるようになりました! 併せて、Java言語やVCSSLでの、プログラム制御用APIも拡張しています。詳細をお知らせします。

Exevalator 2.2 をリリース、TypeScript 対応によりWebブラウザ上で動作可能に
2024-10-22 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。新たに TypeScript に対応し、Webブラウザ上での式計算にも使えるようになりました。詳細を解説します。

アシスタントAI作成の舞台裏(その2、作成編)
2024-10-12 - アシスタントAIの作成方法解説の後編です。実際にChatGPTの「GPTs」機能を用いて、アシスタントAIを作成する手順や、独自の知識をもたせたり、精度を出すためのノウハウなどを解説しています。

アシスタントAI作成の舞台裏(その1、基礎知識編)
2024-10-07 - アシスタントAI作成方法解説の前編です。今回はまず、アシスタントAIを作る前に抑えておきたい、基礎知識を延々と解説しています。そもそもLLM型AIとはどんな存在か? RAGとは何か? 等々です。

ソフトの利用をサポートしてくれるアシスタントAIを提供開始!
2024-09-20 - RINEARN製ソフトの使い方の質問応答や、一部作業のお手伝いをしてくれる、アシスタントAIを提供開始しました。ChatGPTアカウントさえあれば、誰でも無料で使用できます。使い方を解説します。

Exevalator 2.1 をリリース、新たに Visual Basic に対応
2024-07-28 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。今回から、新たに Visual Basic(VB.NET)でも使用できるようになりました。詳細を解説します。

関数電卓 RINPn(りんぷん)、Esc キーで計算式の一発クリアが可能に
2024-07-20 - 関数電 RINPn の Ver.1.0.2 をリリースしました。今回から、キーボードの「 Esc 」キーを押すと、入力中の計算式を一発でクリアできるようになりました。詳細を解説します。

Exevalator 2.0 をリリース、互換性に注意が必要なバグ修正が 1 件
2024-07-14 - オープンソースの式計算ライブラリ「Exevalator (エグゼバレータ)」の2.0をリリースしました。今回の更新では、互換性に注意を要する 1 件のバグ修正があります。詳細を解説します。