coding Shift_JIS; 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 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; // アニメーション状態を解除して待機状態に移行 } }