package require Ncs
#//	
#//	NcsEdiTk.tcl
#//	
#//	* Sat Mar 26 22:42:00 JST 2016 Naoyuki Sawa
#//	- 1st リリース。
#//	* Mon Mar 28 21:40:28 JST 2016 Naoyuki Sawa
#//	- 背景サイズを変更できるようにしました。
#//	  基本的には、背景イメージを読み込まない時に背景サイズを明示的に設定するために追加した機能です。
#//	  背景イメージを読み込んだ時も背景サイズを変更出来ますが、背景イメージはスケーリングしません。
#//	  キャンバスにはイメージのスケーリング機能が無い(?)からです。
#//	  もし今後、スケーリングする方が使い易いと思った場合は変更可能ですが、'image create'の段階でスケーリングする必要が有り、少し手間がかかるかも知れません。
#//	- キャラクターサイズを変更できるようにしました。
#//	  スケーリングしない事については、背景と同様です。
#//	- 速度、背景サイズ、キャラクターサイズもセーブするようにしました。
#//	- キャンバスとリストボックスの位置を左右入れ替えました。
#//	  入れ替えた理由は、背景サイズを変更できるようにした事に伴い、キャンバスのリサイズが頻繁に発生するようになったので、
#//	  その時に、リストボックスの位置が移動しない方が使い易いと思ったからです。
#//	- 区間の線と、節点の点の、描画色を変更しました。
#//	  変更前は、背景が暗い色の時に、かなり見辛かったからです。
#//	  変更後もあまり見易いとは言えませんが、多少はましになったと思います。
#//	- データファイルの拡張子を、.txt⇒.ncsに変更しました。
#//	  NcsSeq.txtからインクルードして使うために、拡張子でNcsSeq.txtとデータファイルを区別して、各データファイルをインクルードするNcsSeq.txtを自動生成し易くするためです。
#//	- アクセラレータキーを定義しました。
#//	* Tue Mar 29 21:02:48 JST 2016 Naoyuki Sawa
#//	- '節点の時刻を固定する'チェックボックスを追加しました。
#//	  あらかじめ希望する時間に比例した距離間隔の節点を作成しておき、このチェックボックスをチェックして、節点を移動すると、節点の時刻を維持したまま位置だけ変更できます。
#//	  あまり使い易いとは言えませんが、一応、節点間の距離に比例 *しない* 時間のパスも作成出来るようになりました。
#//	- ダイアログボックスの初期フォルダの指定方法を変更しました。
#//	  tk_getOpenFileやtk_getSaveFileに対して'-initialdir'を指定しないと、カレントフォルダではなく前回の実行中にダイロアグボックスで開いたフォルダになってしまうようだ。
#//	  従って、'-initialdir [pwd]'を明示的に指定する事にした。
#//	  ただし、毎回[pwd]を指定すると毎回カレントフォルダから辿って行く必要が生じて不便なので、二回目以降は'-initialdir ""'とした。
#//	  これは、'-initialdir'を指定しないのと同じである。
#//	  こうすれば、二回目以降は前回のフォルダから開ける。
#//============================================================================
#//	定数
#//============================================================================
set VERSION		20160329						;#//最終更新日
set SCRIPT		[file tail [info script]]				;#//スクリプトファイル名
set TITLE		"$SCRIPT - NCS曲線エディタ/Tk ($VERSION)"		;#//タイトル
set COPYRIGHT		"Copyright (C) 2016 Naoyuki Sawa"			;#//コピーライト
set FPS			20							;#//フレームレート
set MAX_NODE		8							;#//節点の上限数	調整可。多過ぎると遅くなります。
#//============================================================================
#//	グローバル変数
#//============================================================================
proc initGlobal {} {
useGlobal
#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
set backgroundImageFileName	""						;#//背景イメージのファイル名。					背景イメージ無しならば""
set backgroundImage		""						;#//背景イメージ。'image create photo'の戻り値です。		背景イメージ無しならば""
set backgroundWidth		[expr {128*4}]					;#//背景の幅。							背景イメージ無しならば[expr {128*4}]
set backgroundHeight		[expr { 88*4}]					;#//背景の高さ。						背景イメージ無しならば[expr { 88*4}]
#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
set characterImageFileName	""						;#//キャラクターイメージのファイル名。				キャラクターイメージ無しならば""
set characterImage		""						;#//キャラクターイメージ。'image create photo'の戻り値です。	キャラクターイメージ無しならば""
set characterWidth		16						;#//キャラクターの幅。						キャラクターイメージ無しならば16
set characterHeight		16						;#//キャラクターの高さ。					キャラクターイメージ無しならば16
#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
set nNode			4						;#//節点の数
set nAxs			2						;#//軸の数		Ncsモジュールの仕様上は可変だが、当アプリケーションでは定数として扱う。
set aTim(0)			0						;#//┬各節点の時刻	初期データ
set aTim(1)			1000						;#//│
set aTim(2)			2000						;#//│
set aTim(3)			3000						;#//┘
set aPos(0,0)			[expr {$backgroundWidth * 0 / 3}]		;#//┬各節点の座標	初期データ
set aPos(1,0)			[expr {$backgroundWidth * 1 / 3}]		;#//│
set aPos(2,0)			[expr {$backgroundWidth * 2 / 3}]		;#//│
set aPos(3,0)			[expr {$backgroundWidth * 3 / 3}]		;#//│
set aPos(0,1)			                           0			;#//│
set aPos(1,1)			[expr {$backgroundHeight - 0}]			;#//│
set aPos(2,1)			                           0			;#//│
set aPos(3,1)			[expr {$backgroundHeight - 0}]			;#//┘
set pNcsSeq			[dict create]					;#//NCS補間係数
#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
set bTimFix			0						;#//時間ロック
set tAll			$aTim([expr {$nNode - 1}])			;#//総時間		初期データ
set tSpeed			1000						;#//キャラクターの移動速度		1秒あたりのtCharacter増加量。FPSには非依存です。
set tCharacter			0						;#//キャラクターの移動パラメータ(時刻)
#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
set selectedSeg			-1						;#//選択中の区間
set selectedNode		-1						;#//選択中の節点
set bLoading			0						;#//ロード中の自動再計算抑止フラグ
}
#//以下のフィールドは起動直後のみ初期化し、それ以降にinitGlobalが呼ばれた時は変更しない。
set sInitialDir			[pwd]						;#//ダイアログボックスの初期フォルダ	tk_getOpenFileやtk_getSaveFileに対して'-initialdir'を指定しないと、カレントフォルダではなく前回の実行中にダイロアグボックスで開いたフォルダになってしまうようだ。従って、'-initialdir [pwd]'を明示的に指定する事にした。ただし、毎回[pwd]を指定すると毎回カレントフォルダから辿って行く必要が生じて不便なので、二回目以降は'-initialdir ""'とした。これは、'-initialdir'を指定しないのと同じである。こうすれば、二回目以降は前回のフォルダから開ける。
#//============================================================================
#//	定数とグローバル変数をインポートする処理
#//============================================================================
proc useGlobal {} {
uplevel {
#//定数
global VERSION
global SCRIPT
global TITLE
global COPYRIGHT
global FPS
global MAX_NODE
#//グローバル変数
global backgroundImageFileName
global backgroundImage
global backgroundWidth
global backgroundHeight
#//
global characterImageFileName
global characterImage
global characterWidth
global characterHeight
#//
global nNode
global nAxs
global aTim
global aPos
global pNcsSeq
#//
global bTimFix
global tAll
global tSpeed
global tCharacter
#//
global selectedSeg
global selectedNode
global bLoading
#//
global sInitialDir
}
}
#//============================================================================
#//	共通処理
#//============================================================================
#//	console show								;#//┬開発中のみ
#//	wm geometry . +0+0							;#//┘
after idle {focus -force .}							;#//起動直後にメインウインドウがアクティブになるようにする。開発中にwish.exeから起動した時にコンソールウインドウがアクティブになってしまうのを防ぐためです。必須ではありません。
bind all <Alt-c> {console show}							;#//wish.exeから起動しなかった場合でも、Alt+Cが押されたらコンソールウインドウを表示する。
#//bind all <Escape> {exit}							;#//Escapeキーが押されたら、すぐにプログラムを終了する。操作ミスでセーブせずに終わってしまうのが嫌な場合は、この行をコメントアウトして下さい。
bindtags . [concat [bindtags .] MainWindow]					;#//メインウインドウ以外が削除された時に<Destroy>が起動されないように、メインウインドウには特別なバインドタグを設定しておく。
bind MainWindow <Destroy> {exit}						;#//[×]ボタンが押されてメインウインドウが削除されたら、プログラムを終了する。
wm title . $TITLE								;#//メインウインドウのタイトルを設定する。
#//----------------------------------------------------------------------------
#//一般的なオプション設定
option add *font {mincho 9}
option add *Button.takeFocus 0
option add *Checkbutton.takeFocus 0
option add *exportSelection 0
#//□「Why doesn`t the `canvas` seem to start at 0,0?」(http://tcltk.free.fr/tcltkfaq.php3?idxquery=127)
#//│The canvas coords are skewed by the highlight border and the relief.
#//│By default, they overlap onto the canvas coord space.
#//│There are two ways to deal with this:
#//││## Method 1, zero out the highlightthickness and borderwidth (borderwidth defaults to zero) anyway
#//││.canvas configure -borderwidth 0 -highlightthickness 0
#//││## Method 2, adjust the canvas position to expose 0,0 properly
#//││.canvas xview moveto 0
#//││.canvas yview moveto 0
#//│Method 2 is better, but you must do it anytime you adjust the canvas borderwidth or highlightthickness.
option add *Canvas.borderWidth 0
option add *Canvas.highlightThickness 0
#//----------------------------------------------------------------------------
#//一般的なリストボックスバインド。
bind Listbox <Home> {if {[%W size]} then {%W activate   0; %W selection clear 0 end; %W selection set   0; %W see   0; break}}
bind Listbox <End>  {if {[%W size]} then {%W activate end; %W selection clear 0 end; %W selection set end; %W see end; break}}
#//============================================================================
#//	アプリケーション特有の処理
#//============================================================================
#//メニューを初期化する。
proc initMenu {} {
	useGlobal
	menu .m -type menubar
	. configure -menu .m
	#//Fileメニュー
	.m add cascade -label "File" -underline 0 -menu .m.file
	menu .m.file -tearoff 0
	.m.file add command -label "New Data"  -underline 0 -command {onNewData}  -accelerator "Cntl+N" ; bind all <Control-n> {onNewData}
	.m.file add command -label "Load Data" -underline 0 -command {onLoadData} -accelerator "Cntl+L" ; bind all <Control-l> {onLoadData}
	.m.file add command -label "Save Data" -underline 0 -command {onSaveData} -accelerator "Cntl+S" ; bind all <Control-s> {onSaveData}
	.m.file add separator
	.m.file add command -label "Quit"      -underline 1 -command {onQuit}     -accelerator "Cntl+Q" ; bind all <Control-q> {onQuit}
	#//imageメニュー
	.m add cascade -label "Image" -underline 0 -menu .m.image
	menu .m.image -tearoff 0
	.m.image add command -label "Load Background Image" -underline 5 -command {onLoadBackgroundImage} -accelerator "Cntl+B" ; bind all <Control-b> {onLoadBackgroundImage}
	.m.image add command -label "Load Character Image"  -underline 5 -command {onLoadCharacterImage}  -accelerator "Cntl+C" ; bind all <Control-c> {onLoadCharacterImage}
	#//Helpメニュー
	.m add cascade -label "Help" -underline 0 -menu .m.help
	menu .m.help -tearoff 0
	.m.help add command -label "About" -underline 0 -command {onAbout} -accelerator "Cntl+A" ; bind all <Control-a> {onAbout}
}
#//----------------------------------------------------------------------------
#//ウインドウを初期化する。
proc initWindow {} {
	useGlobal
	#//リストボックスを作成する。
	frame .fr
	listbox .lb -width 16 -yscrollcommand {.sv set} -xscrollcommand {.sh set}
	bind .lb <<ListboxSelect>> {onListboxSelect}		;##//リストボックスの選択項目を変更した時に呼ばれる関数
	scrollbar .sv -orient vertical   -command {.lb yview}
	scrollbar .sh -orient horizontal -command {.lb xview}
	#//入力欄を作成する。
	#//時間固定
	checkbutton .bTimFix -text "節点の時刻を固定する"	;#//'-variable bTimFix'を指定してもしなくても同じ。
	trace add variable bTimFix write onWriteNum		;#//時間固定が変更されたら、データを更新する。
	#//総時間
	frame .fa
	label .fa.l -text "総時間" -width 10
	entry .fa.e -width 5 -textvariable tAll -validate key -validatecommand {onValidateNum %P}	;#//入力欄に、数字しか入力出来なくする。
	trace add variable tAll write onWriteNum	;#//総時間が変更されたら、データを更新する。
	pack .fa.l -side left -fill both
	pack .fa.e -side left -fill both -expand 1
	#//速度
	frame .fs
	label .fs.l -text "速度" -width 10
	entry .fs.e -width 5 -textvariable tSpeed -validate key -validatecommand {onValidateNum %P}	;#//入力欄に、数字しか入力出来なくする。
	pack .fs.l -side left -fill both
	pack .fs.e -side left -fill both -expand 1
	#//背景サイズ
	frame .fbw
	label .fbw.l -text "背景幅" -width 10
	entry .fbw.e -width 5 -textvariable backgroundWidth -validate key -validatecommand {onValidateNum %P}	;#//入力欄に、数字しか入力出来なくする。
	trace add variable backgroundWidth write onWriteBackgroundSize	;#//背景サイズが変更されたら、キャンバスのサイズを変更する。
	pack .fbw.l -side left -fill both
	pack .fbw.e -side left -fill both -expand 1
	frame .fbh
	label .fbh.l -text "背景高さ" -width 10
	entry .fbh.e -width 5 -textvariable backgroundHeight -validate key -validatecommand {onValidateNum %P}	;#//入力欄に、数字しか入力出来なくする。
	trace add variable backgroundHeight write onWriteBackgroundSize	;#//背景サイズが変更されたら、キャンバスのサイズを変更する。
	pack .fbh.l -side left -fill both
	pack .fbh.e -side left -fill both -expand 1
	#//キャラクターサイズ
	frame .fcw
	label .fcw.l -text "キャラ幅" -width 10
	entry .fcw.e -width 5 -textvariable characterWidth -validate key -validatecommand {onValidateNum %P}	;#//入力欄に、数字しか入力出来なくする。
	pack .fcw.l -side left -fill both
	pack .fcw.e -side left -fill both -expand 1
	frame .fch
	label .fch.l -text "キャラ高さ" -width 10
	entry .fch.e -width 5 -textvariable characterHeight -validate key -validatecommand {onValidateNum %P}	;#//入力欄に、数字しか入力出来なくする。
	pack .fch.l -side left -fill both
	pack .fch.e -side left -fill both -expand 1
	#//リストボックスと入力欄を配置する。
	grid .lb .sv -in .fr -sticky news	;#//┬リストボックス
	grid .sh x   -in .fr -sticky news	;#//┘
	grid .bTimFix -in .fr -sticky w		;#//時間固定
	grid .fa - -in .fr -sticky news		;#//総時間
	grid .fs - -in .fr -sticky news		;#//速度
	grid .fbw - -in .fr -sticky news	;#//┬背景サイズ
	grid .fbh - -in .fr -sticky news	;#//┘
	grid .fcw - -in .fr -sticky news	;#//┬キャラクターサイズ
	grid .fch - -in .fr -sticky news	;#//┘
	grid    rowconfigure .fr 0 -weight 1
	grid columnconfigure .fr 0 -weight 1
	pack .fr -side left -fill both
	#//キャンバスを作成する。
	canvas .c -closeenough 10	;#//※TODO:'-closeenough'の値は使ってみて調整する予定です。
	#//キャンバスを配置する。
	bind .c <B1-Motion> {onCanvasB1Motion %x %y}		;#//キャンバスの上で左ドラッグ中に呼ばれる関数
	bind .c <ButtonRelease-1> {onCanvasButtonRelease1}	;#//キャンバスの上で左ボタンを離した時に呼ばれる関数
	bind .c <Button-3> {onCanvasButton3 %x %y}		;#//キャンバスの上で右ボタンを押した時に呼ばれる関数
	pack .c -side left -fill both -expand 1
	#//
	changeBackgroundImage $backgroundImageFileName	;#//キャンバスの初期サイズ設定のために必要。
	changeCharacterImage  $characterImageFileName	;#//こちらは必須ではないが形式のために一応。
}
#//----------------------------------------------------------------------------
#//入力欄に、数字しか入力出来なくする。
proc onValidateNum {value} {
	useGlobal
#//	if {![string is digit -strict $value]} then {return 0}	;#//←本当はこうしたいのだが、全体を選択して入力し直す時に怪しい挙動になるので…
	if {![string is digit         $value]} then {return 0}	;#//←仕方なくこれで妥協した。
	return 1
}
#//----------------------------------------------------------------------------
#//入力欄が変更されたら、データを更新する。
proc onWriteNum {name1 name2 op} {
	useGlobal
	#//ロード中の自動再計算抑止フラグがセットされていたら、何もしない。
	if {$bLoading} then {return}
	#//総時間の入力欄が入力されたら、時間固定を解除する。
	if {$name1 eq "tAll"} then {set bTimFix 0}
	#//節点の時刻を更新する。(bTimFix=1だった場合は更新されない。)
	updateTim
	#//NCS補間係数を更新する。
	updateNcsSeq
	#//キャンバスを更新する。
	updateCanvas
	#//リストボックスを更新する。
	updateListBox
}
#//----------------------------------------------------------------------------
#//背景イメージを変更する。
proc changeBackgroundImage {sFileName} {
	useGlobal
	#//古い背景イメージが有れば、削除する。
	if {$backgroundImage ne ""} then {
		image delete $backgroundImage
		set backgroundImage ""
	}
	#//背景イメージのファイル名を格納する。
	set backgroundImageFileName $sFileName
	#//背景イメージが有れば…
	if {$backgroundImageFileName ne ""} then {
		#//背景イメージをロードする。
		set backgroundImage [image create photo -file $backgroundImageFileName]
		#//背景イメージの幅と高さを格納する。
		set backgroundWidth  [image width  $backgroundImage]
		set backgroundHeight [image height $backgroundImage]
	} else {
		#//背景イメージの幅と高さを格納する。
		set backgroundWidth  [expr {128*4}]
		set backgroundHeight [expr { 88*4}]
	}
	#//キャンバスのサイズを変更する。
	updateScrollRegion
}
#//----------------------------------------------------------------------------
#//背景サイズが変更されたら、キャンバスのサイズを変更する。
proc onWriteBackgroundSize {name1 name2 op} {
	useGlobal
	#//キャンバスのサイズを変更する。
	updateScrollRegion
}
#//----------------------------------------------------------------------------
#//キャンバスのサイズを変更する。
proc updateScrollRegion {} {
	useGlobal
	#//キャンバスサイズの入力欄が片方でも空ならば、何もしない。
	if {($backgroundWidth eq "") || ($backgroundHeight eq "")} then {return}
	#//キャンバスのサイズを変更する。
	wm geometry . {}		;#//これ以前にユーザーがウインドウサイズを変更していた場合、'.c configure -width $w -height $h'によるトップレベルウインドウのリサイズが無効になる。この行は、それを再び有効にするためのトリックです。
	set x [expr {-int($backgroundWidth  * 0.25)}]	;#//┐
	set y [expr {-int($backgroundHeight * 0.25)}]	;#//├画面外の表示領域は、上下左右とも背景サイズの1/4ずつとする事にした。※TODO:調整可
	set w [expr { int($backgroundWidth  * 1.50)}]	;#//│
	set h [expr { int($backgroundHeight * 1.50)}]	;#//┘
	.c configure -width $w -height $h				\
	             -scrollregion [list $x $y [expr {$x + $w}]		\
	                                       [expr {$y + $h}]]
#//	             -confine 0		←何故か'-confine 0'を指定すると何も表示されなくなったので指定しない事にした。※TODO:要調査
#//{{2016/03/28修正:'canvas x(y)view moveto'の引数は、スクロール領域の論理座標ではなく、スクロール領域の幅(高さ)を1.0とした時の割合である。つまり、スクロール領域の左上に合わせたい場合は、常に0を指定すれば良い。
#//	.c xview moveto $x
#//	.c yview moveto $y
#//↓2016/03/28修正:'canvas x(y)view moveto'の引数は、スクロール領域の論理座標ではなく、スクロール領域の幅(高さ)を1.0とした時の割合である。つまり、スクロール領域の左上に合わせたい場合は、常に0を指定すれば良い。
	.c xview moveto 0
	.c yview moveto 0
#//}}2016/03/28修正:'canvas x(y)view moveto'の引数は、スクロール領域の論理座標ではなく、スクロール領域の幅(高さ)を1.0とした時の割合である。つまり、スクロール領域の左上に合わせたい場合は、常に0を指定すれば良い。
}
#//----------------------------------------------------------------------------
#//キャラクターイメージを変更する。
proc changeCharacterImage {sFileName} {
	useGlobal
	#//古いキャラクターイメージが有れば、削除する。
	if {$characterImage ne ""} then {
		image delete $characterImage
		set characterImage ""
	}
	#//キャラクターイメージのファイル名を格納する。
	set characterImageFileName $sFileName
	#//キャラクターイメージが有れば…
	if {$characterImageFileName ne ""} then {
		#//キャラクターイメージをロードする。
		set characterImage [image create photo -file $characterImageFileName]
		#//キャラクターイメージの幅と高さを格納する。
		set characterWidth  [image width  $characterImage]
		set characterHeight [image height $characterImage]
	} else {
		#//キャラクターイメージの幅と高さを格納する。
		set characterWidth  16
		set characterHeight 16
	}
}
#//----------------------------------------------------------------------------
#//データを初期化する。
proc onNewData {} {
	useGlobal
	#//グローバル変数を初期化する。
	initGlobal
	changeBackgroundImage $backgroundImageFileName	;#//キャンバスの初期サイズ設定のために必要。
	changeCharacterImage  $characterImageFileName	;#//こちらは必須ではないが形式のために一応。
	#//節点の時刻を更新する。(bTimFix=1だった場合は更新されない。)
	updateTim
	#//NCS補間係数を更新する。
	updateNcsSeq
	#//キャンバスを更新する。
	updateCanvas
	#//リストボックスを更新する。
	updateListBox
	#//メッセージを表示する。
	tk_messageBox -type ok -icon info -title $TITLE -message "データを初期化しました。"
}
#//----------------------------------------------------------------------------
#//データをロードする。
proc onLoadData {} {
	useGlobal
	#//ファイルを開くダイアログボックスを表示する。
	set sFileName [tk_getOpenFile						\
		-title "Load Data"						\
		-defaultextension {.ncs}					\
		-filetypes {{{Data Files} {.ncs}} {{All Files} {*}}}		\
		-initialdir $sInitialDir]	;#//┬tk_getOpenFileやtk_getSaveFileに対して'-initialdir'を指定しないと、カレントフォルダではなく前回の実行中にダイロアグボックスで開いたフォルダになってしまうようだ。従って、'-initialdir [pwd]'を明示的に指定する事にした。ただし、毎回[pwd]を指定すると毎回カレントフォルダから辿って行く必要が生じて不便なので、二回目以降は'-initialdir ""'とした。これは、'-initialdir'を指定しないのと同じである。こうすれば、二回目以降は前回のフォルダから開ける。
	if {$sFileName eq ""} then {return}	;#//│
	set sInitialDir ""			;#//┘
	#//ファイルを開く。
	set fp [open $sFileName]
	#//データをロードするための一時的な変数を用意する。
	set _bTimFix			""
	set _tSpeed			""
	set _backgroundImageFileName	""
	set _backgroundWidth		""
	set _backgroundHeight		""
	set _characterImageFileName	""
	set _characterWidth		""
	set _characterHeight		""
	set _nNode			0
#//不要	set _aTim(0)			0
#//不要	set _aPos(0,0)			0
#//不要	set _aPos(0,1)			0
	while {[gets $fp sLine] >= 0} {
		#//時間固定の行ならば…
		if {[regexp {^#//bTimFix[[:blank:]]+([[:digit:]]+)$} $sLine {} _bTimFix]} then {
			#//** no job **
		#//速度の行ならば…
		} elseif {[regexp {^#//tSpeed[[:blank:]]+([[:digit:]]+)$} $sLine {} _tSpeed]} then {
			#//** no job **
		#//背景イメージの行ならば…
		} elseif {[regexp {^#//backgroundImageFileName[[:blank:]]+\"(.+)\"$} $sLine {} _backgroundImageFileName]} then {
			#//** no job **
		#//背景サイズの行ならば…
		} elseif {[regexp {^#//backgroundWidth[[:blank:]]+([[:digit:]]+)$} $sLine {} _backgroundWidth]} then {
			#//** no job **
		} elseif {[regexp {^#//backgroundHeight[[:blank:]]+([[:digit:]]+)$} $sLine {} _backgroundHeight]} then {
			#//** no job **
		#//キャラクターイメージの行ならば…
		} elseif {[regexp {^#//characterImageFileName[[:blank:]]+\"(.+)\"$} $sLine {} _characterImageFileName]} then {
			#//** no job **
		#//キャラクターサイズの行ならば…
		} elseif {[regexp {^#//characterWidth[[:blank:]]+([[:digit:]]+)$} $sLine {} _characterWidth]} then {
			#//** no job **
		} elseif {[regexp {^#//characterHeight[[:blank:]]+([[:digit:]]+)$} $sLine {} _characterHeight]} then {
			#//** no job **
		#//節点データの行ならば…
		} elseif {[regexp {^[[:blank:]]*(-?[[:digit:]]+)[[:blank:]]*,[[:blank:]]*(-?[[:digit:]]+)[[:blank:]]*,[[:blank:]]*(-?[[:digit:]]+)[[:blank:]]*$} $sLine {} t x y]} then {
			set _aTim($_nNode)	$t
			set _aPos($_nNode,0)	$x
			set _aPos($_nNode,1)	$y
			incr _nNode
		#//それ以外ならば…
		} else {
			#//ファイルを閉じる。
			close $fp
			#//メッセージを表示する。
			tk_messageBox -type ok -icon error -title $TITLE -message "ロードエラーが発生しました。\n$sLine"
			return	;#//ここまで
		}
	}
	#//ファイルを閉じる。
	close $fp
	#//まず、グローバル変数を初期化しておく。
	initGlobal
	#//ロード中の自動再計算抑止フラグをセットする。
	set bLoading 1
#//{{ロード中の自動再計算抑止区間
	#//データを格納する。
	if {!$_nNode} then {
		#//メッセージを表示する。
		tk_messageBox -type ok -icon error -title $TITLE -message "節点データが有りません。"
		return	;#//ここまで
	}
	set nNode $_nNode
	for {set iNode 0} {$iNode < $nNode} {incr iNode} {
		set aTim($iNode)   $_aTim($iNode)
		set aPos($iNode,0) $_aPos($iNode,0)
		set aPos($iNode,1) $_aPos($iNode,1)
	}
	if {$_bTimFix ne ""} then {set bTimFix $_bTimFix}
	set tAll $aTim([expr {$nNode - 1}])	;#//tAllは明示的にデータには保存していない。最後の節点の時刻から取得する。
	if {$_tSpeed ne ""} then {set tSpeed $_tSpeed}
	changeBackgroundImage $_backgroundImageFileName					;#//┐
	if {$_backgroundWidth  ne ""} then {set backgroundWidth  $_backgroundWidth }	;#//├この順番必須
	if {$_backgroundHeight ne ""} then {set backgroundHeight $_backgroundHeight}	;#//┘
	changeCharacterImage  $_characterImageFileName					;#//┐
	if {$_characterWidth  ne ""} then {set characterWidth  $_characterWidth }	;#//├この順番必須
	if {$_characterHeight ne ""} then {set characterHeight $_characterHeight}	;#//┘
#//}}ロード中の自動再計算抑止区間
	#//ロード中の自動再計算抑止フラグをクリアする。
	set bLoading 0
	#//節点の時刻を更新する。(bTimFix=1だった場合は更新されない。)
	updateTim
	#//NCS補間係数を更新する。
	updateNcsSeq
	#//キャンバスを更新する。
	updateCanvas
	#//リストボックスを更新する。
	updateListBox
	#//メッセージを表示する。
	tk_messageBox -type ok -icon info -title $TITLE -message "\"$sFileName\"をロードしました。"
}
#//----------------------------------------------------------------------------
#//データをセーブする。
proc onSaveData {} {
	useGlobal
	#//ファイル名を付けて保存ダイアログボックスを表示する。
	set sFileName [clock format [clock seconds] -format {%Y%m%d%H%M%S}]
	set sFileName [tk_getSaveFile						\
		-title "Save Data"						\
		-initialfile $sFileName						\
		-defaultextension {.ncs}					\
		-filetypes {{{Data Files} {.ncs}} {{All Files} {*}}}		\
		-initialdir $sInitialDir]	;#//┬tk_getOpenFileやtk_getSaveFileに対して'-initialdir'を指定しないと、カレントフォルダではなく前回の実行中にダイロアグボックスで開いたフォルダになってしまうようだ。従って、'-initialdir [pwd]'を明示的に指定する事にした。ただし、毎回[pwd]を指定すると毎回カレントフォルダから辿って行く必要が生じて不便なので、二回目以降は'-initialdir ""'とした。これは、'-initialdir'を指定しないのと同じである。こうすれば、二回目以降は前回のフォルダから開ける。
	if {$sFileName eq ""} then {return}	;#//│
	set sInitialDir ""			;#//┘
	#//ファイルを作成する。
	set fp [open $sFileName "w"]
	#//時間固定の値が空でなければ、速度を出力する。(bTimFixはチェックボックスなので値が空になる事はないはずだが一応確認する事にした。)
	if {$bTimFix ne ""} then {
		puts $fp "#//bTimFix $bTimFix"
	}
	#//速度の入力欄が空でなければ、速度を出力する。
	if {$tSpeed ne ""} then {
		puts $fp "#//tSpeed $tSpeed"
	}
	#//背景イメージが有れば、背景イメージファイル名を出力する。
	if {$backgroundImageFileName ne ""} then {
		puts $fp "#//backgroundImageFileName \"$backgroundImageFileName\""
	}
	#//背景サイズの入力欄が空でなければ、背景サイズを出力する。
	if {$backgroundWidth ne ""} then {
		puts $fp "#//backgroundWidth $backgroundWidth"
	}
	if {$backgroundHeight ne ""} then {
		puts $fp "#//backgroundHeight $backgroundHeight"
	}
	#//キャラクターイメージが有れば、キャラクターイメージファイル名を出力する。
	if {$characterImageFileName ne ""} then {
		puts $fp "#//characterImageFileName \"$characterImageFileName\""
	}
	#//キャラクターサイズの入力欄が空でなければ、キャラクターサイズを出力する。
	if {$characterWidth ne ""} then {
		puts $fp "#//characterWidth $characterWidth"
	}
	if {$characterHeight ne ""} then {
		puts $fp "#//characterHeight $characterHeight"
	}
	#//各節点について…
	for {set iNode 0} {$iNode < $nNode} {incr iNode} {
		set t $aTim($iNode)
		set x $aPos($iNode,0)
		set y $aPos($iNode,1)
		#//節点データを出力する。
		puts $fp "$t\t,$x\t,$y"
	}
	#//ファイルを閉じる。
	close $fp
	#//メッセージを表示する。
	tk_messageBox -type ok -icon info -title $TITLE -message "\"$sFileName\"をセーブしました。"
}
#//----------------------------------------------------------------------------
#//プログラムを終了する。
proc onQuit {} {
	useGlobal
	destroy .
}
#//----------------------------------------------------------------------------
#//背景イメージをロードする。
proc onLoadBackgroundImage {} {
	useGlobal
	#//ファイルを開くダイアログボックスを表示する。
	set sFileName [tk_getOpenFile									\
		-title "Load Background Image"								\
		-defaultextension {.bmp}								\
		-filetypes {{{Image Files} {.bmp .gif .jpeg .jpg .png .tga}} {{All Files} {*}}}		\
		-initialdir $sInitialDir]	;#//┬tk_getOpenFileやtk_getSaveFileに対して'-initialdir'を指定しないと、カレントフォルダではなく前回の実行中にダイロアグボックスで開いたフォルダになってしまうようだ。従って、'-initialdir [pwd]'を明示的に指定する事にした。ただし、毎回[pwd]を指定すると毎回カレントフォルダから辿って行く必要が生じて不便なので、二回目以降は'-initialdir ""'とした。これは、'-initialdir'を指定しないのと同じである。こうすれば、二回目以降は前回のフォルダから開ける。
	if {$sFileName eq ""} then {return}	;#//│
	set sInitialDir ""			;#//┘
	#//背景イメージを変更する。
	changeBackgroundImage $sFileName
	#//キャンバスを更新する。
#//不要	updateTim
#//不要	updateNcsSeq
	updateCanvas
#//不要	updateListBox
}
#//----------------------------------------------------------------------------
#//キャラクターイメージをロードする。
proc onLoadCharacterImage {} {
	useGlobal
	#//ファイルを開くダイアログボックスを表示する。
	set sFileName [tk_getOpenFile									\
		-title "Load Character Image"								\
		-defaultextension {.bmp}								\
		-filetypes {{{Image Files} {.bmp .gif .jpeg .jpg .png .tga}} {{All Files} {*}}}		\
		-initialdir $sInitialDir]	;#//┬tk_getOpenFileやtk_getSaveFileに対して'-initialdir'を指定しないと、カレントフォルダではなく前回の実行中にダイロアグボックスで開いたフォルダになってしまうようだ。従って、'-initialdir [pwd]'を明示的に指定する事にした。ただし、毎回[pwd]を指定すると毎回カレントフォルダから辿って行く必要が生じて不便なので、二回目以降は'-initialdir ""'とした。これは、'-initialdir'を指定しないのと同じである。こうすれば、二回目以降は前回のフォルダから開ける。
	if {$sFileName eq ""} then {return}	;#//│
	set sInitialDir ""			;#//┘
	#//キャラクターイメージを変更する。
	changeCharacterImage $sFileName
	#//キャンバスを更新する。
#//不要	updateTim
#//不要	updateNcsSeq
	updateCanvas
#//不要	updateListBox
}
#//----------------------------------------------------------------------------
#//バージョン情報を表示する。
proc onAbout {} {
	useGlobal
	tk_messageBox -type ok -icon info -title "About" -message [join [list	\
		"$TITLE"							\
		"$COPYRIGHT"							\
		""								\
		"■使い方"							\
		"①点を移動するには⇒点を左ボタンでドラッグして下さい。"					\
		"②点を線の途中に追加するには⇒線の上で右クリックして下さい。"					\
		"③点を先頭,又は末尾に追加するには⇒先頭の点,又は,末尾の点の近くで右クリックして下さい。"	\
		"④点を削除するには⇒点の上で右クリックして下さい。"						\
		] "\n"]
}
#//----------------------------------------------------------------------------
#//マウスカーソルが曲線の上に入った時に呼ばれる関数。
proc onSegEnter {iNode} {
	useGlobal
	#//選択中の区間を格納する。
	set selectedSeg $iNode
	#//曲線のスタイルを変更する。
	.c itemconfigure "seg($iNode)" -fill red
	#//リストボックスの選択項目を変更する。
	.lb selection clear 0 end
	.lb activate $iNode
	.lb selection set $iNode [expr {$iNode + 1}]
	.lb see $iNode
	.lb xview 0	;#//利便性のため
}
#//----------------------------------------------------------------------------
#//マウスカーソルが曲線の上から出た時に呼ばれる関数。
proc onSegLeave {iNode} {
	useGlobal
	#//選択中の区間をクリアする。
	set selectedSeg -1
	#//曲線のスタイルを元に戻す。
	.c itemconfigure "seg($iNode)" -fill black
	#//リストボックスの選択項目をクリアする。
	.lb selection clear 0 end
}
#//----------------------------------------------------------------------------
#//マウスカーソルが節点の上に入った時に呼ばれる関数。
proc onNodeEnter {iNode} {
	useGlobal
	#//選択中の節点を格納する。
	set selectedNode $iNode
	#//節点のスタイルを変更する。
	.c itemconfigure "node($iNode)" -fill red
	#//リストボックスの選択項目を変更する。
	.lb selection clear 0 end
	.lb activate $iNode
	.lb selection set $iNode
	.lb see $iNode
	.lb xview 0	;#//利便性のため
}
#//----------------------------------------------------------------------------
#//マウスカーソルが節点の上から出た時に呼ばれる関数。
proc onNodeLeave {iNode} {
	useGlobal
	#//選択中の節点をクリアする。
	set selectedNode -1
	#//節点のスタイルを元に戻す。
	.c itemconfigure "node($iNode)" -fill white
	#//リストボックスの選択項目をクリアする。
	.lb selection clear 0 end
}
#//----------------------------------------------------------------------------
#//キャンバスの上で左ドラッグ中に呼ばれる関数。
proc onCanvasB1Motion {x y} {
	useGlobal
	#//選択中の節点が無ければ、何もしない。
	if {$selectedNode == -1} then {return}
	#//マウスカーソルの座標を、クライアント座標系⇒キャンバス座標系に変換する。
	set x [.c canvasx $x]
	set y [.c canvasy $y]
	#//'canvas canvasx'コマンドの戻り値は実数なので、整数に変換する。
	#// - Ncsモジュールの仕様上、座標値は整数でなければならないので、この変換は必須です。
	set x [expr {int($x)}]
	set y [expr {int($y)}]
	#//座標値を、キャンバスのスクロール領域内に切り詰める。
	lassign [.c cget -scrollregion] x0 y0 x1 y1
	if {$x < $x0} then {set x $x0}
	if {$x > $x1} then {set x $x1}
	if {$y < $y0} then {set y $y0}
	if {$y > $y1} then {set y $y1}
	#//座標値を格納する。
	set aPos($selectedNode,0) $x
	set aPos($selectedNode,1) $y
	#//節点の時刻を更新する。(bTimFix=1だった場合は更新されない。)
	updateTim
	#//NCS補間係数を更新する。
	updateNcsSeq
	#//キャンバスを更新する。
	updateCanvas
	#//リストボックスを更新する。
	updateListBox
}
#//----------------------------------------------------------------------------
#//キャンバスの上で左ボタンを離した時に呼ばれる関数。
proc onCanvasButtonRelease1 {} {
	useGlobal
	#//選択中の節点をクリアする。
	set selectedNode -1
	#//節点の時刻を更新する。(bTimFix=1だった場合は更新されない。)
	updateTim
	#//NCS補間係数を更新する。
	updateNcsSeq
	#//キャンバスを更新する。
	updateCanvas
	#//リストボックスを更新する。
	updateListBox
}
#//----------------------------------------------------------------------------
#//キャンバスの上で右ボタンを押した時に呼ばれる関数。
proc onCanvasButton3 {_x _y} {
	useGlobal
	set bUpdate 0			;#//この下の処理でデータを変更したら、このフラグをセットする。
	set _selectedSeg  -1		;#//┬データ変更後に選択状態にしたい区間や節点が有れば、この変数を変更する。
	set _selectedNode -1		;#//┘
	#//マウスカーソルの座標を、クライアント座標系⇒キャンバス座標系に変換しておく。
	set _x [.c canvasx $_x]
	set _y [.c canvasy $_y]
	set _x [expr {int($_x)}]	;#//'canvas canvasx'コマンドの戻り値は実数なので、整数に変換する。
	set _y [expr {int($_y)}]	;#// - Ncsモジュールの仕様上、座標値は整数でなければならないので、この変換は必須です。
	#//選択中の節点が有れば…
	if {$selectedNode != -1} then {
		#//節点の数が1個よりも多ければ…
		if {$nNode > 1} then {
			#//選択中の節点を削除する。
			incr nNode -1
			for {set iNode $selectedNode} {$iNode < $nNode} {incr iNode} {
				set x $aPos([expr {$iNode + 1}],0)
				set y $aPos([expr {$iNode + 1}],1)
				set aPos($iNode,0) $x
				set aPos($iNode,1) $y
			}
			#//データを変更したフラグをセットする。
			set bUpdate 1
		}
	#//選択中の区間が有れば…
	} elseif {$selectedSeg != -1} then {
		#//節点の数が上限数よりも少なければ…
		if {$nNode < $MAX_NODE} then {
			#//選択中の区間の途中に、節点を追加する。
			for {set iNode $nNode} {$iNode > [expr {$selectedSeg + 1}]} {incr iNode -1} {
				set x $aPos([expr {$iNode - 1}],0)
				set y $aPos([expr {$iNode - 1}],1)
				set aPos($iNode,0) $x
				set aPos($iNode,1) $y
			}
			set aPos($iNode,0) $_x
			set aPos($iNode,1) $_y
			incr nNode
			#//追加した節点を選択状態にする。
			set _selectedNode $iNode
			#//データを変更したフラグをセットする。
			set bUpdate 1
		}
	#//選択中の節点も、選択中の区間も無ければ…
	} else {
		#//節点の数が上限数よりも少なければ…
		if {$nNode < $MAX_NODE} then {
			#//マウスカーソルの座標と、最初の節点の座標の距離を求める。
			set dx [expr {$_x - $aPos(0,0)}]
			set dy [expr {$_y - $aPos(0,1)}]
			set d0 [expr {sqrt(($dx * $dx) + ($dy * $dy))}]
			#//マウスカーソルの座標と、最後の節点の座標の距離を求める。
			set dx [expr {$_x - $aPos([expr {$nNode - 1}],0)}]
			set dy [expr {$_y - $aPos([expr {$nNode - 1}],1)}]
			set dN [expr {sqrt(($dx * $dx) + ($dy * $dy))}]
			#//マウスカーソルの座標が、最初の節点に近ければ…
			if {$d0 < $dN} then {
				#//最初の節点の前に、節点を追加する。
				for {set iNode $nNode} {$iNode > 0} {incr iNode -1} {
					set x $aPos([expr {$iNode - 1}],0)
					set y $aPos([expr {$iNode - 1}],1)
					set aPos($iNode,0) $x
					set aPos($iNode,1) $y
				}
				set aPos($iNode,0) $_x
				set aPos($iNode,1) $_y
				incr nNode
				#//追加した節点を選択状態にする。
				set _selectedNode 0
			#//マウスカーソルの座標が、最後の節点に近ければ…
			} else {
				#//最後の節点の後に、節点を追加する。
				set aPos($nNode,0) $_x
				set aPos($nNode,1) $_y
				incr nNode
				#//追加した節点を選択状態にする。
				set _selectedNode [expr {$nNode - 1}]
			}
			#//データを変更したフラグをセットする。
			set bUpdate 1
		}
	}
	#//データを変更したフラグがセットされていたら…
	if {$bUpdate} then {
		#//選択状態を変更する。
		set selectedSeg  $_selectedSeg
		set selectedNode $_selectedNode
		.lb selection clear 0 end
		#//時間固定を解除する。		;#//┬時間固定を解除して増減した各節点の時刻を再計算するために必要です。
		set bTimFix 0			;#//┘
		#//節点の時刻を更新する。(bTimFix=1だった場合は更新されない。)
		updateTim
		#//NCS補間係数を更新する。
		updateNcsSeq
		#//キャンバスを更新する。
		updateCanvas
		#//リストボックスを更新する。
		updateListBox
	#//データを変更したフラグがセットされていなければ…
	} else {
		#//警告音を鳴らす。
		bell
	}
}
#//----------------------------------------------------------------------------
#//リストボックスの選択項目を変更した時に呼ばれる関数。
proc onListboxSelect {} {
	useGlobal
	#//選択項目を取得する。
	set iNode [lindex [.lb curselection] 0]
	#//選択項目が有効ならば…
	if {($iNode != "") && ($iNode >= 0) && ($iNode < $nNode)} then {
		#//選択中の区間をクリアする。
		set selectedSeg  -1
		#//選択中の節点を格納する。
		set selectedNode $iNode
	#//選択項目が無効ならば…
	} else {
		#//選択中の区間をクリアする。
		set selectedSeg  -1
		#//選択中の節点をクリアする。
		set selectedNode -1
	}
	#//キャンバスを更新する。
#//不要	updateTim
#//不要	updateNcsSeq
	updateCanvas
#//不要	updateListBox
}
#//----------------------------------------------------------------------------
#//各節点の時刻を更新する。(bTimFix=1だった場合は更新されない。)
proc updateTim {} {
	useGlobal
	#//時間固定のチェックボックスがOnならば、何もしない。
	if {$bTimFix} then {return}
	#//区間の長さの合計を求める。
	set dSum 0.0
	for {set iNode 0} {$iNode < [expr {$nNode - 1}]} {incr iNode} {
		set x0 [expr {double($aPos($iNode,0))}]
		set y0 [expr {double($aPos($iNode,1))}]
		set x1 [expr {double($aPos([expr {$iNode + 1}],0))}]
		set y1 [expr {double($aPos([expr {$iNode + 1}],1))}]
		set dx [expr {$x1 - $x0}]
		set dy [expr {$y1 - $y0}]
		set d($iNode) [expr {sqrt(($dx * $dx) + ($dy * $dy))}]
		set dSum [expr {$dSum + $d($iNode)}]
	}
	if {$dSum < 1.0} then {set dSum 1.0}
	#//総時間を、各区間の長さの比に従って、各節点に時間を割り振る。
	set _tAll $tAll
	if {$_tAll eq ""} then {set _tAll [expr {($nNode - 1) * 1000}]}
	set t 0.0
	for {set iNode 0} {$iNode < [expr {$nNode - 1}]} {incr iNode} {
		set aTim($iNode) [expr {round($t)}]
		set dt [expr {$_tAll * $d($iNode) / $dSum}]
		set t [expr {$t + $dt}]
		#//前の節点と同じ時刻ならば、(+1)する。
		#// - Ncsモジュールの仕様上、同じ時刻が複数存在してはいけない。
		if {int($t) <= $aTim($iNode)} then {
			set t [expr {double($aTim($iNode) + 1)}]
		}
	}
	#//最後の節点の時刻を格納する。
	if {$t > $_tAll} then {set _tAll $t}	;#//計算誤差によって全体の時間を超えていた場合は、全体の時間を変更する。
	set aTim($iNode) [expr {int($t)}]
}
#//----------------------------------------------------------------------------
#//NCS補間係数を更新する。
proc updateNcsSeq {} {
	useGlobal
	#//NCS補間係数を更新する。
	set pNcsSeq [NcsSeq_New aTim aPos $nNode $nAxs]
}
#//----------------------------------------------------------------------------
#//キャンバスを更新する。
proc updateCanvas {} {
	useGlobal
	#//図形を全て削除する。
	.c delete all
	#//背景イメージが有れば…
	if {$backgroundImage ne ""} then {
		#//背景イメージ図形を作成する。
		.c create image 0 0 -image $backgroundImage -anchor nw
	#//背景イメージが無ければ…
	} else {
		#//** no job **
	}
	#//キャラクターを描画する。
	set x [NcsSeq_GetVal $pNcsSeq $tCharacter 0]
	set y [NcsSeq_GetVal $pNcsSeq $tCharacter 1]
	if {$characterImage ne ""} then {
		.c create image $x $y -image $characterImage
	} else {
		set x0 [expr {$x - ($characterWidth / 2)}]
		set y0 [expr {$y - ($characterHeight / 2)}]
		set x1 [expr {$x0 + $characterWidth}]
		set y1 [expr {$y0 + $characterHeight}]
		.c create oval $x0 $y0 $x1 $y1 -fill yellow -width 3
	}
	#//背景サイズの入力欄が片方でも空でなければ…
	if {($backgroundWidth ne "") && ($backgroundHeight ne "")} then {
		#//背景枠を作成する。
		.c create rectangle 0 0 $backgroundWidth $backgroundHeight -dash .
		#//十字の線を作成する。
		lassign [.c cget -scrollregion] x0 y0 x1 y1
		set x [expr {$backgroundWidth  / 2}]
		set y [expr {$backgroundHeight / 2}]
		.c create line $x $y0 $x $y1 -dash .
		.c create line $x0 $y $x1 $y -dash .
	}
	#//各区間の曲線を描画する。
	for {set iNode 0} {$iNode < [expr {$nNode - 1}]} {incr iNode} {
		set t0 $aTim([expr {$iNode + 0}])
		set t1 $aTim([expr {$iNode + 1}])
		set dt [expr {($t1 - $t0) / 10}]
		if {$dt <  1} then {set dt  1}
#//		if {$dt > 10} then {set dt 10}		;#//総時間に大きな値を入力した時に細かく分割し過ぎて負荷が掛かりすぎるのでやめた。
		set t $t0
		set x [NcsSeq_GetVal $pNcsSeq $t 0]
		set y [NcsSeq_GetVal $pNcsSeq $t 1]
		set coords [list $x $y]
		while {1} {
			incr t $dt
			if {$t >= $t1} then {set t $t1}
			set x [NcsSeq_GetVal $pNcsSeq $t 0]
			set y [NcsSeq_GetVal $pNcsSeq $t 1]
			lappend coords $x $y
			if {$t >= $t1} then {break}
		}
		.c create line $coords -fill white -width 3	;#//この線はダッシュパターンの背景になる。スタイル固定で良いのでタグは割り当てない。
		set id [.c create line $coords]
		.c addtag "seg($iNode)" withtag $id
		if {$iNode == $selectedSeg} then {
			.c itemconfigure "seg($iNode)" -fill red -width 3 -dash .-
		} else {
			.c itemconfigure "seg($iNode)" -fill black -width 3 -dash .-
		}
		.c bind $id <Enter> "onSegEnter $iNode"		;#//マウスカーソルが曲線の上に入った時に呼ばれる関数
		.c bind $id <Leave> "onSegLeave $iNode"		;#//マウスカーソルが曲線の上から出た時に呼ばれる関数
	}
	#//各節点を描画する。
	for {set iNode 0} {$iNode < $nNode} {incr iNode} {
		set x $aPos($iNode,0)
		set y $aPos($iNode,1)
		set coords [list				\
			[expr {$x - 5}] [expr {$y - 5}]		\
			[expr {$x + 5}] [expr {$y + 5}]]		;#//※TODO:調整可
		set id [.c create rectangle $coords]
		.c addtag "node($iNode)" withtag $id
		if {$iNode == $selectedNode} then {
			.c itemconfigure "node($iNode)" -fill red -width 3
		} else {
			.c itemconfigure "node($iNode)" -fill white -width 3
		}
		.c bind $id <Enter> "onNodeEnter $iNode"	;#//マウスカーソルが節点の上に入った時に呼ばれる関数
		.c bind $id <Leave> "onNodeLeave $iNode"	;#//マウスカーソルが節点の上から出た時に呼ばれる関数
	}
}
#//----------------------------------------------------------------------------
#//リストボックスを更新する。
proc updateListBox {} {
	useGlobal
	#//アクティブな項目と、選択状態の項目を取得しておく。
	set active [.lb index active]
	set cursel [lindex [.lb curselection] 0]
	#//リストボックスをクリアする。
	.lb delete 0 end
	#//各節点について…
	for {set iNode 0} {$iNode < $nNode} {incr iNode} {
		#//リストボックスに項目を追加する。
		set t $aTim($iNode)
		set x $aPos($iNode,0)
		set y $aPos($iNode,1)
		set s [format {%5d%5d%5d} $t $x $y]
		.lb insert end $s
	}
	#//アクティブな項目と、選択状態の項目を復元する。
	if {($active ne "") && ($active < $nNode)} then {.lb activate $active}
	if {($cursel ne "") && ($cursel < $nNode)} then {.lb selection set $cursel}
}
#//----------------------------------------------------------------------------
proc doUpdate {} {
	useGlobal
	#//キャラクターの移動パラメータ(時刻)を更新する。
	set _tSpeed $tSpeed
	if {$_tSpeed eq ""} then {set _tSpeed 1000}
	set dt [expr {max(1, $_tSpeed / $FPS)}]
	incr tCharacter $dt
	set tEnd $aTim([expr {$nNode - 1}])
	if {$tCharacter >= $tEnd} then {set tCharacter 0}
	#//キャンバスを更新する。
#//不要	updateTim
#//不要	updateNcsSeq
	updateCanvas
#//不要	updateListBox
}
#//----------------------------------------------------------------------------
initGlobal
initMenu
initWindow
focus .lb
updateTim
updateNcsSeq
updateCanvas
updateListBox
proc mainLoop {} {
	useGlobal
	after [expr {1000 / $FPS}] {mainLoop}
	doUpdate
}
mainLoop
