» 詳しい使用方法や、エラーで展開できない際の対応方法などはこちら
z = f(x,y,t) の形の数式を3Dグラフとしてアニメーション描画するツール
このVCSSLプログラムは、 入力欄に z = f(x,y,t) の形の数式を入力すると、 それを3次元のグラフにアニメーション描画してくれる簡易ツールです。
使用方法
ダウンロードと展開(解凍)
まず、PC(スマホは未対応)で上の画面の「 ダウンロード 」ボタンを押してください。 するとZIP形式で圧縮されたファイルがダウンロードされます。
その後、ZIPファイルを右クリックして「すべて展開」や「ここに展開」などで展開(解凍)してください。 展開が成功すると、ZIPファイルと同じ名前のフォルダができ、その中にZIPファイルの中身が入っています。
» 展開がエラーで止まってしまう場合や、ファイル名が文字化けしてしまう場合は…
プログラムの起動
Windows をご使用の場合
上記でZIPファイルを展開したフォルダ内にある、以下のバッチファイルをダブルクリック実行してください:
もしプログラムを書き変えながら使いたい場合は、代わりに「 VCSSL_Editor__プログラム編集はこちら.bat 」を実行してください。
正常に起動できると、初回のみ、Java実行環境を入手するか等を尋ねられるので、適時答えて済ませると、プログラムが起動します。 2回目以降はすぐに起動します。
Linux 等をご使用の場合
ZIPファイルを展開したフォルダ内へコマンドライン端末で cd して、以下の通り入力して実行してください:
(プログラムの内容を書き変えながら使いたい場合は、代わりに VCSSL_Editor.jar を実行)
» javaコマンドが使用できない等のエラーが表示される場合は…
起動後
画面の様子
起動すると、2つのウィンドウが立ち上がります。 左が数式などの入力画面、右下がグラフ画面です。 右上のアニメーション操作画面は、 入力画面に数式を入力して「 セット 」ボタンを押した時点で表示されます。

※ 画面のデザインや項目名などは、バージョンにより多少異なる場合があります。
数式を入力してグラフを描画する
まず、グラフに描きたい数式を、入力画面の「 z(x,y,t) = 」の入力欄に入力してください。 z = f(x,y,t) における f(x,y,t) の式です。 式の書き方のポイントは以下の通りです:
- 式は半角(123などの、幅の細い文字)で書いてください。キーボード左上の「半角/全角」キーで切り替えられます。
- 時間を表す変数には小文字の「 t 」を用いてください。
- 「 x 」や「 y 」も、大文字ではなく小文字を用いてください。
- 四則演算が普通に使用できます。ただし、かけ算は「 * 」、割り算は「 / 」の記号を用いてください。足し算や引き算は普通に「 + 」と「 - 」を用います。
- かけ算や割り算は、足し算や引き算よりも先に計算されます(手計算と同じです)。
- sin や cos といった数学関数が使用できます。また、円周率の値は「 PI 」と書けばOKです。 使用可能な数学関数・定数の一覧は、Mathライブラリの仕様書ページをご覧ください。
- 累乗は「 ** 」の記号を用いてください。例えば x の2乗なら「 x**2 」です。
式を入力したら、入力画面の「 セット 」ボタンを押すと、その式がグラフ画面上に描かれます。 グラフは、入力画面の「 x-N 」と「 y-N 」に指定した数を、 それぞれメッシュのX方向およびY方向の刻み数とする、曲面グラフとして描画されます。 x-N, y-N の値が大きいほど曲面が滑らかになります。
アニメーション
上の手順で「 セット 」ボタンを押した時点で、画面右上にアニメーション操作画面が出現します。 アニメーション操作画面の「 PLAY 」ボタンを押すと、 入力した数式内の「 t 」を時間とみなして、グラフがアニメーションされます。
アニメーション中は、「 PLAY 」ボタンは表示が変わって「 STOP 」ボタンになり、押すとアニメーションが一時停止します。 アニメーション停止中は、ボタン横のスライダーで、時間を手動で自由に動かす事ができます。
その他の機能や細かい設定など
3Dグラフを曲面ではなくメッシュ(網のような線)で描かせたい場合は、グラフ画面の「 オプション 」 > 「 曲面プロット 」の選択を外し、代わりに「 メッシュプロット 」を選択してください。
また、背景色を黒ではなく白にしたい場合は、グラフ画面の「 オプション 」 > 「 ブラックスクリーン 」の選択を外してください。 なお、白背景は印刷には適していますが、PC画面上ではZ値のグラデーションが見づらくなってしまうため、用途に応じて使い分けてください。
その他、グラフ画面の「 編集 」メニューなどから、グラフ範囲や軸ラベル、目盛り、やカメラアングル、および光や色などの細かい設定を行えます。 また、グラフ画面の「 ファイル 」メニューから、設定内容の保存や、グラフ内容の画像ファイルへの保存などもできます。
なお、このプログラムで起動されるグラフ画面は、 「 リニアングラフ3D( RINEARN Graph 3D ) 」というデータ解析用のグラフソフトで、 単体でも入手・利用できます。操作感などが気に入った方は、ぜひご利用ください。
コード解説
コード全体
このプログラムのコードはVCSSLで記述されています。 VCSSLはC言語系のシンプルな文法を持っているので、C系の言語に触れられた事のある方なら、 コメントを参考にしながらコード内容を比較的簡単に追う or 改造する事ができると思います。
今回のコードは、基本的なグラフ操作の処理に、GUIの画面を組み合わせて、ツールとして仕上げたものです。 グラフの描画処理の基本や、ユーザーの入力した式からグラフを描く流れについては、以下の回で解説していますので、そちらをご参照ください:
- (グラフの描画方法について) 配列を3Dグラフにプロットする(曲面/メッシュグラフ)
- (アニメーションについて) 配列を3Dグラフにアニメーションプロットする(曲面/メッシュグラフ)
- (入力式のグラフ化について) ユーザーが入力した数式を2Dグラフにプロットする
また、VCSSLでのGUI画面の構築については、「 VCSSL GUI開発ガイド 」をご参照ください。
今回のコードは、サンプルコードというよりは実際に使うためのツールなので、 細部を細かくピックアップしながらの解説は割愛し、コード全体の掲載に留めておきます。 今回のコードの全体は、以下の通りです:
coding UTF-8;
import Math;
import Text;
import GUI;
import File;
import tool.Graph3D;
/** グラフに描画する数式 z(x,y,t) のデフォルト入力値です。 */
const string DEFAULT_Z_EXPRESSION = "sin(3*x) + cos(3*y-t)";
/** グラフのX範囲の最大値のデフォルト入力値です。 */
const string DEFAULT_X_MAX = "1.0";
/** グラフのX範囲の最小値のデフォルト入力値です。 */
const string DEFAULT_X_MIN = "-1.0";
/** グラフのメッシュにおけるX方向の点数のデフォルト入力値です。 */
const string DEFAULT_X_N = "60";
/** グラフのY範囲の最大値のデフォルト入力値です。 */
const string DEFAULT_Y_MAX = "1.0";
/** グラフのY範囲の最小値のデフォルト入力値です。 */
const string DEFAULT_Y_MIN = "-1.0";
/** グラフのメッシュにおけるY方向の点数のデフォルト入力値です。 */
const string DEFAULT_Y_N = "60";
/** 時刻 t の最大値(終了値)のデフォルト入力値です。 */
const string DEFAULT_MAX = "100.0";
/** 時刻 t の最小値(開始値)のデフォルト入力値です。 */
const string DEFAULT_MIN = "0.0";
/** デフォルトのファイル保存先フォルダです。 */
const string DEFAULT_OUTPUT_DIRECTORY_PATH = "./output";
/** デフォルトの出力ファイル名(番号部を除く)です。 */
const string DEFAULT_OUTPUT_FILE_NAME_HEAD = "sample3d_";
/** アニメーションの間、数式 z(x,y,t) の内容を控えておく変数です。 */
string zExpression;
/** アニメーションの間、グラフのX範囲の最大値を控えておく変数です。 */
float xMax;
/** アニメーションの間、グラフのX範囲の最小値を控えておく変数です。 */
float xMin;
/** グラフのメッシュにおけるX方向の点数を控えておく変数です。 */
int xN;
/** アニメーションの間、グラフのY範囲の最大値を控えておく変数です。 */
float yMax;
/** アニメーションの間、グラフのY範囲の最小値を控えておく変数です。 */
float yMin;
/** グラフのメッシュにおけるY方向の点数を控えておく変数です。 */
int yN;
/** アニメーションの間、時刻 t の最大値(終了値)を控えておく変数です。 */
float tMax;
/** アニメーションの間、時刻 t の最小値(開始値)を控えておく変数です。 */
float tMin;
// 以下、アニメーション処理や制御用の変数
/** グラフにプロットする座標点列のX値を格納する配列です。 */
double xVertexArray[0][0];
/** グラフにプロットする座標点列のY値を格納する配列です。 */
double yVertexArray[0][0];
/** グラフにプロットする座標点列のZ値を格納する配列です。 */
double zVertexArray[0][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;
/** z(x,y,t) の数式を入力するテキストフィールドのIDを格納します。 */
int expressionField = NULL;
/** X軸の最大値を入力するテキストフィールドのIDを格納します。 */
int xMaxField = NULL;
/** X軸の最小値を入力するテキストフィールドのIDを格納します。 */
int xMinField = NULL;
/** グラフのメッシュにおけるX方向の点数を入力するテキストフィールドのIDを格納します。 */
int xNField = NULL;
/** Y軸の最大値を入力するテキストフィールドのIDを格納します。 */
int yMaxField = NULL;
/** Y軸の最小値を入力するテキストフィールドのIDを格納します。 */
int yMinField = NULL;
/** グラフのメッシュにおけるY方向の点数を入力するテキストフィールドのIDを格納します。 */
int yNField = NULL;
/** 時刻 t の最小値(開始値)を入力するテキストフィールドのIDを格納します。 */
int tMaxField = NULL;
/** 時刻 t の最大値(終了値)を入力するテキストフィールドのIDを格納します。 */
int tMinField = NULL;
/** Z軸の最大値を入力するテキストフィールドのIDを格納します。 */
int zMaxField = NULL;
/** Z軸の最小値を入力するテキストフィールドのIDを格納します。 */
int zMinField = NULL;
/** Z軸の範囲を自動調整するかどうかを選択するチェックボックスのIDを格納します。 */
int zAutoRangeBox = 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();
// システム側で標準接続されている3DグラフソフトがあればIDを取得(無ければnewGraph3D関数同様、新規に立ち上がる)
graph = getGraph3D();
// ※ 普通に newGraph3D 関数で新規生成しないのは、このプログラムがリニアングラフ3Dにも同梱されるツールであり、
// リニアングラフ3D上でメニューからこのプログラムを実行した際に、そのグラフ自身を制御対象とするためです。
// このプログラムでは、既存のグラフ内容に重ね描きする用途は考えにくいので、最初に内容をクリアしておく
clearGraph3D(graph);
// グラフ画面の位置とサイズを設定
setGraph3DLocation(graph, 340, 120);
setGraph3DSize(graph, 720, 600);
// プロットオプションを設定
setGraph3DOption( graph, "WITH_POINTS", false );
setGraph3DOption( graph, "WITH_LINES", false );
setGraph3DOption( graph, "WITH_MEMBRANES", true );
setGraph3DOption( graph, "WITH_MESHES", false );
// 設定画面を起動
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 ( animationExportRequest ) {
sleep( animationWait );
continue;
}
if( animationState ){
frameCounter++;
if( frameCounter <= frameCounterMax ){
setComponentValueInt( animationSlider, frameCounter );
}else{
setAnimationState( false );
}
}
if (reloadRequest) {
reloadRange();
plotGraph( frameCounter );
if( animationWindow < 0 ){
createAnimationWindow();
}else{
setComponentVisible( animationWindow, true );
}
setTimer(0);
reloadRequest = false;
}
if( plotRequest ){
plotGraph( frameCounter );
plotRequest = false;
}
sleep( animationWait );
}
// メインループを脱出すれば終了
exit();
}
/**
* 現在のzExpressionの内容をグラフにプロットします。
*
* @param frameIndex アニメーション始点を 0 とする、プロット対象フレームのインデックス
*/
void plotGraph(int frameIndex){
// フレームインデックスから、時刻の値へ変換
float dt = (tMax-tMin)/frameCounterMax;
float t = tMin + frameIndex * dt;
// グラフに転送するデータ配列を xN * yN 要素で確保
alloc[yN][xN] xVertexArray;
alloc[yN][xN] yVertexArray;
alloc[yN][xN] zVertexArray;
// 座標を計算し、データ配列に数値を代入
double x;
double y;
double z;
for( int i=0; i<yN; i++ ){
for( int j=0; j<xN; j++ ){
x = xMin + (xMax-xMin)*j/(xN-1);
y = yMin + (yMax-yMin)*i/(yN-1);
z = feval( zExpression, 0.0 );
xVertexArray[ i ][ j ] = x;
yVertexArray[ i ][ j ] = y;
zVertexArray[ i ][ j ] = z;
}
}
// データ配列をグラフソフトウェアに転送し、プロット
setGraph3DData( graph, xVertexArray, yVertexArray, zVertexArray );
t = round(t, 8, HALF_UP_SIGNIF);
setComponentText( animationLabel, "t = " + t );
}
/**
* グラフの領域を再設定します。
*/
void reloadRange(){
bool fixRange = getComponentValueBool( zAutoRangeBox );
if( fixRange ){
hideGraph3D( graph );
double zRange[] = getRangeZ();
zRange[0] = round(zRange[0], 5, HALF_UP_SIGNIF);
zRange[1] = round(zRange[1], 5, HALF_UP_SIGNIF);
setGraph3DAutoRange( graph, false, false, false );
setGraph3DRange( graph, xMax, xMin, yMax, yMin, zRange[0], zRange[1] );
showGraph3D( graph );
setComponentText( zMaxField, ""+zRange[0] );
setComponentText( zMinField, ""+zRange[1] );
}else{
if( !evaluable( getComponentText( zMaxField ), 0.0 ) ){
alert("z-maxの式に誤りがあります。");
return;
}
if( !evaluable( getComponentText( zMinField ), 0.0 ) ){
alert("z-minの式に誤りがあります。");
return;
}
double zMax = feval( getComponentText( zMaxField ), 0.0 );
double zMin = feval( getComponentText( zMinField ), 0.0 );
setGraph3DAutoRange( graph, false, false, false );
setGraph3DRange( graph, xMax, xMin, yMax, yMin, zMax, zMin );
}
}
/**
* 現在の設定状態での、zの最大・最小値を計算して返します。
*
* @return zの最小値を[0],最大値を[1]に格納する配列
*/
double[] getRangeZ(){
double zMin = 1.0E30;
double zMax = 1.0E-30;
int rate = 0;
int rateStock = 0;
float dt = (tMax-tMin)/frameCounterMax;
float dx = (xMax-xMin)/(xN-1);
float dy = (yMax-yMin)/(yN-1);
for( int frameCounter=0; frameCounter<=frameCounterMax; frameCounter++ ){
for( int yi=0; yi<yN; yi++ ){
for( int xi=0; xi<xN; xi++ ){
float t = tMin + frameCounter * dt;
float x = xMin + xi * dx;
float y = yMin + yi * dy;
float z = feval( zExpression, 0.0 );
if( z < zMin ){
zMin = z;
}
if( z > zMax ){
zMax = z;
}
}
}
rate = frameCounter*10 / (frameCounterMax-1);
if( rate != rateStock ){
setComponentText( setButton, "計算中: " + rate + "0 %" );
}
rateStock = rate;
}
setComponentText( setButton, "セット" );
clear();
double range[] = { zMax, zMin };
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;
double tMin = getComponentText( tMinField );
setComponentText( animationLabel, "t = " + tMin );
setComponentValueInt( animationSlider, 0 );
return;
}
/**
* グラフを連番画像として保存します。
* @param outputDirectoryPath 保存フォルダのパス
*/
void outputImages(string outputDirectoryPath) {
// カーソルを描画させないためにグラフ画面を非表示にする
hideGraph3D(graph);
if (animationWindow == NULL) {
popup("先に「 セット 」を行い、設定内容をアニメーションに反映させてください。");
showGraph3D(graph);
return;
}
string outputFileNameHead = input("保存ファイル名(番号部を除く)を入力", DEFAULT_OUTPUT_FILE_NAME_HEAD);
if (outputFileNameHead == NULL) {
showGraph3D(graph);
return;
}
setComponentText(outputButton, "保存中...");
// 操作できないように設定画面を非表示にし、代わりに進捗を表示するためコンソールを表示する
hideComponent(animationWindow);
hideComponent(inputWindow);
show();
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);
exportGraph3D(graph, outputFilePath, "PNG");
sleep(20);
}
// 処理が終わったのでグラフ画面や設定画面を表示し、コンソールは邪魔なので非表示にする
showGraph3D(graph);
showComponent(animationWindow);
showComponent(inputWindow);
hide();
setComponentText(outputButton, "画像保存");
popup("保存しました。保存先フォルダ: " + EOL + outputDirectoryPath);
frameCounter = 0;
setComponentValueInt( animationSlider, 0 );
}
/**
* 設定画面を起動します。
*/
void createInputWindow(){
int leftWidth = 100;
int rightX = leftWidth + 10;
int rightWidth = 190;
int buttonWidth = 290;
int fontSize = 20;
inputWindow = newWindow( 0, 0, 340, 640, "入力画面" );
int expressionLabel = newTextLabel( 10, 10, leftWidth, 25, "z(x,y,t) = " );
setComponentFontSize(expressionLabel, fontSize);
mountComponent( expressionLabel, inputWindow );
expressionField = newTextField( rightX, 10, rightWidth, 25, DEFAULT_Z_EXPRESSION );
setComponentFontSize(expressionField, fontSize);
mountComponent( expressionField, inputWindow );
int lowX = 10;
int columnWidth = 70;
int xMaxLabel = newTextLabel( lowX, 60, columnWidth, 25, "x-max" );
setComponentFontSize(xMaxLabel, fontSize);
mountComponent( xMaxLabel, inputWindow );
xMaxField = newTextField( lowX+columnWidth, 60, columnWidth, 25, DEFAULT_X_MAX );
setComponentFontSize(xMaxField, fontSize);
mountComponent( xMaxField, inputWindow );
int xMinLabel = newTextLabel( lowX, 90, columnWidth, 25, "x-min" );
setComponentFontSize(xMinLabel, fontSize);
mountComponent( xMinLabel, inputWindow );
xMinField = newTextField( lowX+columnWidth, 90, columnWidth, 25, DEFAULT_X_MIN );
setComponentFontSize(xMinField, fontSize);
mountComponent( xMinField, inputWindow );
int xNLabel = newTextLabel( lowX, 120, columnWidth, 25, "x-N" );
setComponentFontSize(xNLabel, fontSize);
mountComponent( xNLabel, inputWindow );
xNField = newTextField( lowX+columnWidth, 120, columnWidth, 25, DEFAULT_X_N );
setComponentFontSize(xNField, fontSize);
mountComponent( xNField, inputWindow );
lowX = 160;
columnWidth = 70;
int yMaxLabel = newTextLabel( lowX, 60, columnWidth, 25, "y-max" );
setComponentFontSize(yMaxLabel, fontSize);
mountComponent( yMaxLabel, inputWindow );
yMaxField = newTextField( lowX+columnWidth, 60, columnWidth, 25, DEFAULT_Y_MAX );
setComponentFontSize(yMaxField, fontSize);
mountComponent( yMaxField, inputWindow );
int yMinLabel = newTextLabel( lowX, 90, columnWidth, 25, "y-min" );
setComponentFontSize(yMinLabel, fontSize);
mountComponent( yMinLabel, inputWindow );
yMinField = newTextField( lowX+columnWidth, 90, columnWidth, 25, DEFAULT_Y_MIN );
setComponentFontSize(yMinField, fontSize);
mountComponent( yMinField, inputWindow );
int yNLabel = newTextLabel( lowX, 120, columnWidth, 25, "y-N" );
setComponentFontSize(yNLabel, fontSize);
mountComponent( yNLabel, inputWindow );
yNField = newTextField( lowX+columnWidth, 120, columnWidth, 25, DEFAULT_Y_N );
setComponentFontSize(yNField, fontSize);
mountComponent( yNField, inputWindow );
int tMaxLabel = newTextLabel( 10, 170, leftWidth, 25, "t-max =" );
setComponentFontSize(tMaxLabel, fontSize);
mountComponent( tMaxLabel, inputWindow );
tMaxField = newTextField( rightX, 170, rightWidth, 25, DEFAULT_MAX );
setComponentFontSize(tMaxField, fontSize);
mountComponent( tMaxField, inputWindow );
int tMinLabel = newTextLabel( 10, 200, leftWidth, 25, "t-min =" );
setComponentFontSize(tMinLabel, fontSize);
mountComponent( tMinLabel, inputWindow );
tMinField = newTextField( rightX, 200, rightWidth, 25, DEFAULT_MIN );
setComponentFontSize(tMinField, fontSize);
mountComponent( tMinField, inputWindow );
zAutoRangeBox = newCheckBox( 10, 250, 500, 20, "Z範囲を自動設定", true );
mountComponent( zAutoRangeBox, inputWindow );
int zMaxLabel = newTextLabel( 10, 280, leftWidth, 25, "z-max = " );
setComponentFontSize(zMaxLabel, fontSize);
mountComponent( zMaxLabel, inputWindow );
zMaxField = newTextField( rightX, 280, rightWidth, 25, "1.0" );
setComponentFontSize(zMaxField, fontSize);
mountComponent( zMaxField, inputWindow );
int zMinLabel = newTextLabel( 10, 310, leftWidth, 25, "z-min = " );
setComponentFontSize(zMinLabel, fontSize);
mountComponent( zMinLabel, inputWindow );
zMinField = newTextField( rightX, 310, rightWidth, 25, "-1.0" );
setComponentFontSize(zMinField, fontSize);
mountComponent( zMinField, 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( 340, 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(
yMaxField,
correctCharactersInExpression(
getComponentText(yMaxField)
)
);
setComponentText(
yMinField,
correctCharactersInExpression(
getComponentText(yMinField)
)
);
setComponentText(
yNField,
correctCharactersInExpression(
getComponentText(yNField)
)
);
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();
// 以下、画面から入力内容を取得
zExpression = 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( yMaxField ), 0.0 ) ){
alert("y-maxの式に誤りがあります。");
return;
}
if( !evaluable( getComponentText( yMinField ), 0.0 ) ){
alert("y-minの式に誤りがあります。");
return;
}
if( !evaluable( getComponentText( yNField ), 0.0 ) ){
alert("y-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);
yMax = feval( getComponentText( yMaxField ), 0.0);
yMin = feval( getComponentText( yMinField ), 0.0);
yN = feval( getComponentText( yNField ), 0.0);
tMax = feval( getComponentText( tMaxField ), 0.0);
tMin = feval( getComponentText( tMinField ), 0.0);
float x, y, t;
if( !evaluable(zExpression, 0.0) ){
alert("f(x,y,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 ){
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 ){
// このプログラムでは範囲の自動調整を無効化しているので、終了時に戻しておく
setGraph3DAutoRange( graph, true, true, true );
// メインループを脱出、プログラムを終了させる
mainLoopState = false;
}else if( id == animationWindow ){
animationState = false;
}
}
/**
* グラフが閉じられた際にコールされます(イベントハンドラ)
*
* @param id 閉じられたグラフのID
*/
void onGraph3DClose( int id ){
if( id == graph ){
animationState = false;
}
}
download/ZxytGraph3D.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 氏の米国およびその他の国における商標または登録商標です。
- その他、文中に使用されている商標は、その商標を保持する各社の各国における商標または登録商標です。
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次元グラフにプロットするサンプルプログラムです。 |