coding Shift_JIS; // 文字コードの明示(文字化け予防) // ライブラリの読み込み import tool.Graph2D; import File; import GUI; /** デフォルトのファイル読み込みフォルダです。 */ const string DEFAULT_INPUT_DIRECTORY_PATH = "./input"; /** デフォルトのファイル保存先フォルダです。 */ const string DEFAULT_OUTPUT_DIRECTORY_PATH = "./output"; /** デフォルトの読み込みファイル名(番号部分や拡張子は除く)です。 */ const string DEFAULT_INPUT_FILE_NAME_HEAD = "sample2d_"; /** デフォルトの読み込みファイルの拡張子です。 */ const string DEFAULT_INPUT_FILE_EXTENTION = ".txt"; /** グラフのX範囲の最大値のデフォルト入力値です。 */ const string DEFAULT_X_MAX = "1.0"; /** グラフのX範囲の最小値のデフォルト入力値です。 */ const string DEFAULT_X_MIN = "-1.0"; /** グラフのY範囲の最大値のデフォルト入力値です。 */ const string DEFAULT_Y_MAX = "1.0"; /** グラフのY範囲の最小値のデフォルト入力値です。 */ const string DEFAULT_Y_MIN = "-1.0"; /** アニメーション速度のデフォルト値です。 */ const string DEFAULT_ANIMATION_SPEED = "1.0"; /** アニメーション速度が 1.0 の時のアニメーションウェイト値です。 */ const int BASE_ANIMATION_WAIT = 33; /** 連番の開始番号を探す上限です。 */ const int FILE_NUMBER_SEARCH_LIMIT = 1000; // 以下、内部状態関連の変数 /** ファイル読み込みフォルダのパスを保持します。 */ string inputDirectoryPath = NULL; /** 読み込みファイル名(番号部分や拡張子は除く)を保持します。 */ string inputFileNameHead = NULL; /** 読み込みファイルの拡張子を保持します。 */ string inputFileExtention = NULL; /** 連番の開始番号を保持します。 */ int inputFileNumberBegin = -1; /** 連番の終了番号を保持します。 */ int inputFileNumberEnd = -1; /** メインループの継続状態を保持します(falseにすると脱出)。 */ bool mainLoopState = true; /** アニメーションの状態を保持します(true=アニメーション中、false=待機状態)。 */ bool animationState = false; /** 時刻が1つ進む際の待ち時間(アニメーションウェイト)を保持します。 */ int animationWait = (int)( BASE_ANIMATION_WAIT / (float)DEFAULT_ANIMATION_SPEED ); /** * フレームカウンタ(アニメーションの描画回数の中で、何回目かを保持するカウンタ)です。 * inputFileNumberBegin から inputFileNumberEnd まで変化します。 */ int frameCounter = 0; /** アニメーションを終了する最終フレームカウンタ値を保持します。inputFileNumberEnd-inputFileNumberBegin に一致します。 */ int frameCounterMax = -1; /** グラフをプロットするリクエストフラグ(trueにするとメインループでプロットされ、falseに戻される)です。 */ bool plotRequest = false; /** メインループで連番画像出力処理を行うためのフラグです。 */ bool animationExportRequest = false; // 以下、グラフやGUIコンポーネントIDを格納する変数 /** グラフのIDを格納します。 **/ int graph = NULL; /** 入力画面のウィンドウのIDを格納します。 */ int inputWindow = NULL; /** X軸の範囲を自動調整するかどうかを選択するチェックボックスのIDを格納します。 */ int xAutoRangeBox = NULL; /** X軸の最大値を入力するテキストフィールドのIDを格納します。 */ int xMaxField = NULL; /** X軸の最小値を入力するテキストフィールドのIDを格納します。 */ int xMinField = NULL; /** Y軸の範囲を自動調整するかどうかを選択するチェックボックスのIDを格納します。 */ int yAutoRangeBox = NULL; /** Y軸の最大値を入力するテキストフィールドのIDを格納します。 */ int yMaxField = NULL; /** Y軸の最小値を入力するテキストフィールドのIDを格納します。 */ int yMinField = NULL; /** アニメーション速度を入力するテキストフィールドのIDを格納します。 */ int animationSpeedField = NULL; /** セットボタンのIDを格納します。 */ int setButton = NULL; /** 画像出力ボタンのIDを格納します。 */ int outputButton = NULL; /** 終了ボタンの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() { // ユーザー入力による設定処理を実行 configure(); // システム側で標準接続されている2DグラフソフトがあればIDを取得(無ければnewGraph2D関数同様、新規に立ち上がる) graph = getGraph2D(); // 取得したグラフ画面が他のウィンドウよりも後ろにある場合は、最前面に持ってきたいため、一旦非表示にして再表示する hideGraph2D(graph); showGraph2D(graph); // このプログラムでは、既存のグラフ内容に重ね描きする用途は考えにくいので、最初に内容をクリアしておく clearGraph2D(graph); // ※ 普通に newGraph2D 関数で新規生成しないのは、このプログラムがリニアングラフ2Dにも同梱される事を見込んだツールであり、 // リニアングラフ2D上でメニューからこのプログラムを実行した際に、そのグラフ自身を制御対象とするためです。 // グラフ画面の位置とサイズを設定 setGraph2DLocation(graph, 330, 120); setGraph2DSize(graph, 720, 600); // 設定画面とアニメーション操作画面を起動 createInputWindow(); createAnimationWindow(); // 最初のファイルをプロット plotGraph(inputFileNumberBegin); // メインループ ― プログラム終了までずっと繰り返し、必要なら処理を行い、何も無ければ待機する 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 ){ if( frameCounter < frameCounterMax ){ setComponentValueInt( animationSlider, frameCounter+1 ); }else{ setComponentValueInt( animationSlider, 0 ); } } // グラフのプロット if( plotRequest ){ int fileNumber = inputFileNumberBegin + frameCounter; plotGraph(fileNumber); plotRequest = false; } sleep( animationWait ); } // メインループを脱出すれば終了 exit(); } /** * ユーザー入力による設定処理を行います。 */ void configure() { // ファイル読み込みフォルダの選択(スキップすると input フォルダを使用) inputDirectoryPath = DEFAULT_INPUT_DIRECTORY_PATH; if (confirm("ファイルを読み込むフォルダを指定しますか?", "(「いいえ」を選択すると「 " + DEFAULT_INPUT_DIRECTORY_PATH + " 」フォルダを使用)")) { inputDirectoryPath = choose("ファイルを読み込むフォルダを選択", "."); } println("ファイル読み込みフォルダ = " + inputDirectoryPath); // 画像ファイル名(番号部分や拡張子は除く)の入力 inputFileNameHead = input("ファイル名(番号部分や拡張子は除く)を入力", DEFAULT_INPUT_FILE_NAME_HEAD); println("ファイル名 = " + inputFileNameHead); // 拡張子の選択 inputFileExtention = input("拡張子を入力", DEFAULT_INPUT_FILE_EXTENTION); // フォルダ内のファイルを検索し、連番の始点を inputFileNumberBegin、終点を inputFileNumberEnd に設定 scanFileIndex(); // 連番の始点と終点から、フレームカウンタの最大値を求めて設定 frameCounterMax = inputFileNumberEnd - inputFileNumberBegin; } /** * 指定されたファイル番号のファイルを、グラフにプロットします。 */ void plotGraph(int fileNumber) { string fileName = inputFileNameHead + fileNumber + inputFileExtention; string filePath = getFilePath(fileName, inputDirectoryPath); setGraph2DFile(graph, filePath); setComponentText( animationLabel, "ファイル番号 = " + fileNumber ); } /** * 現在の入力内容に基づいて、グラフの範囲設定を行います。 */ void setGraphRange() { // 範囲の自動調整機能の有無を取得して反映させる bool xAutoRangeEnabled = getComponentValueBool(xAutoRangeBox); bool yAutoRangeEnabled = getComponentValueBool(yAutoRangeBox); setGraph2DAutoRange(graph, xAutoRangeEnabled, yAutoRangeEnabled); // X軸の範囲設定 if (!xAutoRangeEnabled) { if ( !evaluable( getComponentText( xMaxField ), 0.0 ) ) { alert("x-max の入力内容に誤りがあります。"); return; } if ( !evaluable( getComponentText( xMinField ), 0.0 ) ) { alert("x-min の入力内容に誤りがあります。"); return; } float xMax = feval(getComponentText( xMaxField ), 0.0); float xMin = feval(getComponentText( xMinField ), 0.0); setGraph2DRangeX(graph, xMin, xMax); } // Y軸の範囲設定 if (!yAutoRangeEnabled) { if ( !evaluable( getComponentText( yMaxField ), 0.0 ) ) { alert("y-max の入力内容に誤りがあります。"); return; } if ( !evaluable( getComponentText( yMinField ), 0.0 ) ) { alert("y-min の入力内容に誤りがあります。"); return; } float yMax = feval(getComponentText( yMaxField ), 0.0); float yMin = feval(getComponentText( yMinField ), 0.0); setGraph2DRangeY(graph, yMin, yMax); } } /** * ファイルを検索し、連番の始点と終点を判定します。 */ void scanFileIndex() { println("ファイル検索中..."); // 始点番号を検索し、fileNumberBegin に設定 inputFileNumberBegin = 0; while (true) { // 以下、inputFileNumberBegin を0から1ずつ増やしながら、 // その値を連番部分とする名前のファイルが存在するか検査し、 // 最初に存在したものを開始番号としてループ脱出 string fileName = inputFileNameHead + inputFileNumberBegin + inputFileExtention; string filePath = getFilePath(fileName, inputDirectoryPath); if (exists(filePath)) { println("開始ファイル: " + filePath); break; } if(inputFileNumberBegin >= FILE_NUMBER_SEARCH_LIMIT) { pop("連番ファイルを" + FILE_NUMBER_SEARCH_LIMIT + "番まで探しましたが、見つかりませんでした。"); exit(); } inputFileNumberBegin++; } println("開始番号 = " + inputFileNumberBegin); // 終点番号の検索し、inputFileNumberEnd に設定 inputFileNumberEnd = inputFileNumberBegin; while (true) { // 以下、inputFileNumberBegin を inputFileNumberBegin から1ずつ増やしながら、 // その値を連番とする名前のファイルが存在するか検査し、 // 「最初に存在しなくなった番号-1」を終了番号としてループ脱出 string fileName = inputFileNameHead + inputFileNumberEnd + inputFileExtention; string filePath = getFilePath(fileName, inputDirectoryPath); if (!exists(filePath)) { inputFileNumberEnd--; break; } inputFileNumberEnd++; } println("終了番号 = " + inputFileNumberEnd); } /** * 現在の入力内容に基づいて、アニメーションのウェイト値を設定します。 */ void setAnimationWait() { if ( !evaluable( getComponentText(animationSpeedField), 0.0 ) ) { alert("「速度」の項目の入力内容に誤りがあります。"); } // 入力内容からアニメーション速度の値を取得 float speed = feval(getComponentText(animationSpeedField), 0.0); // 負の場合はおかしいので弾く if (speed < 0) { alert("「速度」の項目には正の値を設定してください。"); } // スピードが小さすぎると、ウェイト値が大きくなりすぎて、メインループの応答間隔が著しく悪化するので弾く if (speed < 0.01) { alert("「速度」の項目の値が小さすぎます。"); } // アニメーション速度をアニメーションウェイトに変換 animationWait = BASE_ANIMATION_WAIT / speed; } /** * アニメーションのON/OFF状態を切り替えます。 * * @param state アニメーションのON/OFF状態(ONならtrue) */ void setAnimationState( bool state ){ animationState = state; if( state ){ setComponentText( animationButton, "STOP" ); }else{ setComponentText( animationButton, "PLAY" ); } } /** * グラフを連番画像として保存します。 * * @param outputDirectoryPath 保存フォルダのパス */ void outputImages(string outputDirectoryPath) { setComponentText(outputButton, "保存中..."); hideGraph2D(graph); hideComponent(animationWindow); hideComponent(inputWindow); setAnimationState(false); plotRequest = false; popup( "全ての入力ファイルのグラフを、連番の画像ファイルとして保存します。" + EOL + "これには少し時間がかかる事があります。" + EOL + "「 OK 」を押した後は、操作せずに完了までしばらくお待ちください。" ); // ファイルをプロットして画像に保存、を全ての入力ファイルに対してくり返す for (frameCounter=0; frameCounter<=frameCounterMax; frameCounter++) { int fileNumber = inputFileNumberBegin + frameCounter; plotGraph(fileNumber); string outputFileName = inputFileNameHead + fileNumber + ".png"; string outputFilePath = getFilePath(outputFileName, outputDirectoryPath); println("画像保存(" + frameCounter + "/" + frameCounterMax + "): " + outputFilePath); exportGraph2D(graph, outputFilePath, "PNG"); } showGraph2D(graph); showComponent(animationWindow); showComponent(inputWindow); 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, 530, "入力画面" ); xAutoRangeBox = newCheckBox( 10, 10, 500, 20, "X範囲を自動設定", true ); mountComponent( xAutoRangeBox, inputWindow ); int xMaxLabel = newTextLabel( 10, 40, leftWidth, 25, "x-max =" ); setComponentFontSize(xMaxLabel, fontSize); mountComponent( xMaxLabel, inputWindow ); xMaxField = newTextField( rightX, 40, rightWidth, 25, DEFAULT_X_MAX ); setComponentFontSize(xMaxField, fontSize); mountComponent( xMaxField, inputWindow ); int xMinLabel = newTextLabel( 10, 70, leftWidth, 25, "x-min =" ); setComponentFontSize(xMinLabel, fontSize); mountComponent( xMinLabel, inputWindow ); xMinField = newTextField( rightX, 70, rightWidth, 25, DEFAULT_X_MIN ); setComponentFontSize(xMinField, fontSize); mountComponent( xMinField, inputWindow ); yAutoRangeBox = newCheckBox( 10, 110, 500, 20, "Y範囲を自動設定", true ); mountComponent( yAutoRangeBox, inputWindow ); int yMaxLabel = newTextLabel( 10, 140, leftWidth, 25, "y-max = " ); setComponentFontSize(yMaxLabel, fontSize); mountComponent( yMaxLabel, inputWindow ); yMaxField = newTextField( rightX, 140, rightWidth, 25, DEFAULT_Y_MAX ); setComponentFontSize(yMaxField, fontSize); mountComponent( yMaxField, inputWindow ); int yMinLabel = newTextLabel( 10, 170, leftWidth, 25, "y-min = " ); setComponentFontSize(yMinLabel, fontSize); mountComponent( yMinLabel, inputWindow ); yMinField = newTextField( rightX, 170, rightWidth, 25, DEFAULT_Y_MIN ); setComponentFontSize(yMinField, fontSize); mountComponent( yMinField, inputWindow ); int animationSpeedLabel = newTextLabel( 10, 220, leftWidth, 25, "速度 =" ); setComponentFontSize(animationSpeedLabel, fontSize); mountComponent( animationSpeedLabel, inputWindow ); animationSpeedField = newTextField( rightX, 220, rightWidth, 25, DEFAULT_ANIMATION_SPEED ); setComponentFontSize(animationSpeedField, fontSize); mountComponent( animationSpeedField, inputWindow ); setButton = newButton( 10, 260, buttonWidth, 50, "セット" ); setComponentFontSize(setButton, fontSize); mountComponent( setButton, inputWindow ); outputButton = newButton( 10, 320, buttonWidth, 50, "画像保存" ); setComponentFontSize(outputButton, fontSize); mountComponent( outputButton, inputWindow ); int outputPathLabel = newTextLabel( 10, 375, 80, 24, "保存場所 ="); mountComponent( outputPathLabel, inputWindow ); outputPathField = newTextField( 90, 375, 120, 24, DEFAULT_OUTPUT_DIRECTORY_PATH); mountComponent( outputPathField, inputWindow ); outputPathSelectButton = newButton( 210, 375, 70, 24, "選択" ); mountComponent( outputPathSelectButton, inputWindow ); exitButton = newButton( 10, 420, 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 ); } /** * ボタンが押された際にコールされます(イベントハンドラ) * * @param id 押されたボタンのID */ void onButtonClick( int id ){ // 「セット」ボタンが押された場合 if( id == setButton ){ setAnimationWait(); setGraphRange(); plotRequest = true; return; } // 「終了」ボタンが押された場合 if( id == exitButton ){ mainLoopState = false; return; } // 「画像保存」ボタンが押された場合 if( id == outputButton ){ // このフラグを有効化すると、メインループが連番画像出力処理を行う animationExportRequest = true; return; } // 画像保存先フォルダの選択ボタンを押した際 if ( id == outputPathSelectButton ) { string path = choose(); while (!isdir(path)) { alert("選択されたものがフォルダではありません。" + EOL + "画像を保存するフォルダを選択してください。"); path = choose(); } setComponentText(outputPathField, path); return; } // 「PLAY/STOP」ボタンが押された場合 if( id == animationButton ){ if( animationState ){ setAnimationState( false ); }else{ setAnimationState( true ); } return; } } /** * チェックボックスが選択された際にコールされます(イベントハンドラ) */ void onCheckBoxClick( int id, bool value ) { if (id == xAutoRangeBox || id == yAutoRangeBox) { bool xAutoRangeEnabled = getComponentValueBool(xAutoRangeBox); bool yAutoRangeEnabled = getComponentValueBool(yAutoRangeBox); setGraph2DAutoRange(graph, xAutoRangeEnabled, yAutoRangeEnabled); } } /** * スライダーが動かされた際にコールされます(イベントハンドラ) * * @param id 動かされたスライダーのID */ void onSliderMove( int id, int value ){ if( id == animationSlider ){ frameCounter = value; plotRequest = true; } } /** * ウィンドウが閉じられた際にコールされます(イベントハンドラ) * * @param id 閉じられたウィンドウのID */ void onWindowClose( int id ){ if( id == inputWindow || id == animationWindow ){ // このプログラムでは、場合によっては範囲の自動調整を無効化するので、終了時に戻しておく setGraph2DAutoRange( graph, true, true ); // メインループを脱出、プログラムを終了させる mainLoopState = false; } } /** * グラフが閉じられた際にコールされます(イベントハンドラ) * * @param id 閉じられたグラフのID */ void onGraph2DClose( int id ){ if( id == graph ){ animationState = false; // アニメーション状態を解除して待機状態に移行 } }