/*
 *	NcsEdit.hsp
 *
 *	自然3次スプラインエディター
 *
 *	* Sun Oct 04 22:59:17 JST 2009 Naoyuki Sawa
 *	- 新規作成。
 *	- HSP 3.2で作成しました。
 *	- clipncs.hモジュールのNCS構造体データを出力します。
 *	  NCS補間係数を計算済みなので、実行時にNCS_new()を計算する必要がありません。
 *	  そのまま、NCS_interpolate()で利用できます。
 *	* Mon Oct 05 22:37:25 JST 2009 Naoyuki Sawa
 *	- node_num=1の時に、*UpdateSelが不正終了するバグを修正しました。
 *	- 縦画面、横画面に同時に対応できるよう、ガイド描画を変更しました。
 *	- 使い方とバージョン情報のダイアログを追加しました。
 *	- セーブするNCS補間係数を、P/ECEのスケールに合うよう変更しました。
 *	  P/ECEの画面を、(-64,-44)-(64,44)、または、(-44,-64)-(44,64) と見なした座標系です。
 *	  P/ECEで利用する時は、NCS補間を行った後、(0,0) が画面中心になるようにシフトしてください。
 *	  尚、タイムスケールは常に先頭の点が0.0、末尾の点が1.0で、区間長に比例して配分しています。
 *	* Wed Oct 07 00:04:44 JST 2009 Naoyuki Sawa
 *	- ノード、および、区間上のカーソル位置に、tを表示しました。
 *	* Thu Oct 08 00:05:25 JST 2009 Naoyuki Sawa
 *	- セーブ時の、名前を付けて保存ダイアログで、拡張子が省略されたら、".c"を付加する処理を追加しました。
 *	* Fri Oct 09 00:01:44 JST 2009 Naoyuki Sawa
 *	- NcsEdit.exeのデータを利用するサンプルアプリを作成しました。
 *	  ncssampl/main.cの、app_main()関数のコメントを参照してください。
 *	- 本ツールには変更有りません。
 */
#define APPNAME "NcsEdit"
#define APPVER "Thu Oct 2009 Naoyuki Sawa"

#packopt name APPNAME
#packopt hide 1

#include "win32api.as"
#include "hspmath.as"
#include "NCS.as"
#include "CRS.as"

/****************************************************************************
 *	定数定義
 ****************************************************************************/

#define SCREEN_SIZE	400.0
#define CANVAS_CENTER	(SCREEN_SIZE / 2.0)

#define CANVAS_SCALE	2.0
#define CANVAS_SIZE1	( 88.0 * CANVAS_SCALE)
#define CANVAS_SIZE2	(128.0 * CANVAS_SCALE)

#define NODE_HALF	3.0

#define MAX_NODE	8

#define SEG_DIV		0.01

#define CMD_LOAD	1
#define CMD_SAVE	2
#define CMD_EXIT	3
#define CMD_HELP	4
#define CMD_ABOUT	5

/****************************************************************************
 *	変数初期化
 ****************************************************************************/

	ddim node_t, MAX_NODE	/* UpdateNodeTで自動設定する */
	ddim node_x, MAX_NODE
	ddim node_y, MAX_NODE

	node_num = 0
	node_sel = -1
	seg_sel = -1
	t_sel = 0.0

	anim_t = 0.0

	notesel note

/****************************************************************************
 *	ウインドウ初期化
 ****************************************************************************/

	title APPNAME

	/* メインメニュー */
	CreateMenu
	hMenu = stat
		CreatePopupMenu
		hSubMenu = stat
		AppendMenu hSubMenu, MF_STRING, CMD_LOAD, "開く(&O)..."
		AppendMenu hSubMenu, MF_STRING, CMD_SAVE, "名前を付けて保存(&A)..."
		AppendMenu hSubMenu, MF_SEPARATOR, 0, 0
		AppendMenu hSubMenu, MF_STRING, CMD_EXIT, "終了(&X)"
	AppendMenu hMenu, MF_POPUP, hSubMenu, "ファイル(&F)"
		CreatePopupMenu
		hSubMenu = stat
		AppendMenu hSubMenu, MF_STRING, CMD_HELP, "使い方(&H)..."
		AppendMenu hSubMenu, MF_STRING, CMD_ABOUT, "バージョン情報(&A)..."
	AppendMenu hMenu, MF_POPUP | MF_HELP, hSubMenu, "ヘルプ(&H)"
	SetMenu hwnd, hMenu
	DrawMenuBar hwnd

	screen 0, SCREEN_SIZE, SCREEN_SIZE + (ginfo_sy - ginfo_winy)
	font msmincho, 10, font_antialias

	/* screen命令よりも後で行うこと!! */
	oncmd gosub *OnCommand, WM_COMMAND
	oncmd gosub *OnLButtonDown, WM_LBUTTONDOWN
	oncmd gosub *OnLButtonUp, WM_LBUTTONUP
	oncmd gosub *OnMouseMove, WM_MOUSEMOVE
	oncmd gosub *OnRButtonDown, WM_RBUTTONDOWN

/****************************************************************************
 *	メインループ
 ****************************************************************************/

	/* 初期データ */
	node_x(node_num) = CANVAS_CENTER
	node_y(node_num) = CANVAS_CENTER - (CANVAS_SIZE2 / 2.0)
	node_num++
	node_x(node_num) = CANVAS_CENTER
	node_y(node_num) = CANVAS_CENTER
	node_num++
	node_x(node_num) = CANVAS_CENTER
	node_y(node_num) = CANVAS_CENTER + (CANVAS_SIZE2 / 2.0)
	node_num++

	/* 初期選択 */
	gosub *UpdateNodeT
	gosub *UpdateNCS
	node_sel = -1
	seg_sel = -1
	t_sel = 0.0

	GetTickCount
	t0 = double(stat)
	repeat
		await 1000 / 60 /* 調整可 */

		redraw 0
		color 0, 0, 0
		boxf

		gosub *DrawGuide
		if node_num != 0 {
			if node_num >= 2 {
				gosub *DrawCurve
			}
			gosub *DrawNode

			GetTickCount
			t1 = double(stat)
			if node_num >= 2 {
				anim_t += (t1 - t0) / 1000.0 / (node_num - 1)
			}
			t0 = t1
			if anim_t >= 1.0 {
				anim_t = 0.0
			}
			gosub *DrawAnim
		}
	
		redraw 1
	loop

	stop

/****************************************************************************
 *	描画
 ****************************************************************************/

*DrawGuide
	color 0, 0, 100
	/* 十字 */
	pos  CANVAS_CENTER,           0
	line CANVAS_CENTER, SCREEN_SIZE
	pos            0, CANVAS_CENTER
	line SCREEN_SIZE, CANVAS_CENTER
	/* 縦画面用ガイド */
	x = CANVAS_CENTER - (CANVAS_SIZE1 / 2.0)
	w =                  CANVAS_SIZE1
	y = CANVAS_CENTER - (CANVAS_SIZE2 / 2.0)
	h =                  CANVAS_SIZE2
	pos  x    , y
	line x + w, y
	line x + w, y + h
	line x    , y + h
	line x    , y
	/* 横画面用ガイド */
	x = CANVAS_CENTER - (CANVAS_SIZE2 / 2.0)
	w =                  CANVAS_SIZE2
	y = CANVAS_CENTER - (CANVAS_SIZE1 / 2.0)
	h =                  CANVAS_SIZE1
	pos  x    , y
	line x + w, y
	line x + w, y + h
	line x    , y + h
	line x    , y

	return

/*--------------------------------------------------------------------------*/

*DrawCurve
	color 150, 150, 150
	/* 操作中なら、CRS補間で描く */
	GetCapture
	if stat == hwnd {
		pos node_x(0), node_y(0)
		t = 0.0
		repeat
			t += SEG_DIV / (node_num - 1)
			if t >= 1.0 {
				break
			}
			x = CRS_interpolate(node_x, node_num, t * (node_num - 1))
			y = CRS_interpolate(node_y, node_num, t * (node_num - 1))
			line x, y
		loop
		line node_x(node_num - 1), node_y(node_num - 1)
	/* 操作中でなければ、NCS補間で描く */
	} else {
		pos node_x(0), node_y(0)
		t = 0.0
		i = 0
		repeat
			t += SEG_DIV / (node_num - 1)
			if t >= 1.0 {
				break
			}
			if node_sel == -1 {
				if i <= node_num - 1 {
					if t >= node_t(i + 1) {
						i++
					}
				}
				if i == seg_sel {
					color 255, 255, 0
				} else {
					color 150, 150, 150
				}
			}
			x = NCS_interpolate(ncs_x, t)
			y = NCS_interpolate(ncs_y, t)
			line x, y
		loop
		line node_x(node_num - 1), node_y(node_num - 1)
		if node_sel == -1 {
			if t_sel != 0.0 {
				color 255, 255, 0
				x = NCS_interpolate(ncs_x, t_sel)
				y = NCS_interpolate(ncs_y, t_sel)
				pos x + 10, y - 5
				mes str(t_sel)
			}
		}
	}

	return

/*--------------------------------------------------------------------------*/

*DrawNode
	/* 非選択ノード */
	color 200, 200, 200
	for i, 0, node_num
		if i != node_sel {
			x = node_x(i)
			y = node_y(i)
			boxf x - NODE_HALF, y - NODE_HALF, x + NODE_HALF, y + NODE_HALF
			pos x + 10, y - 5
			mes str(node_t(i))
		}
	next

	/* 選択ノード */
	if node_sel != -1 {
		color 255, 0, 0
		x = node_x(node_sel)
		y = node_y(node_sel)
		boxf x - NODE_HALF, y - NODE_HALF, x + NODE_HALF, y + NODE_HALF
		pos x + 10, y - 5
		mes str(node_t(node_sel))
	}

	return

/*--------------------------------------------------------------------------*/

*DrawAnim
	/* 操作中なら、CRS補間で描く */
	GetCapture
	if stat == hwnd {
		x = CRS_interpolate(node_x, node_num, anim_t * (node_num - 1))
		y = CRS_interpolate(node_y, node_num, anim_t * (node_num - 1))
	/* 操作中でなければ、NCS補間で描く */
	} else {
		x = NCS_interpolate(ncs_x, anim_t)
		y = NCS_interpolate(ncs_y, anim_t)
	}
	color 0, 255, 0
	circle x - 10, y - 10, x + 10, y + 10, 0

	return

/****************************************************************************
 *	メッセージ処理
 ****************************************************************************/

*OnCommand
	wNotifyCode = wparam >> 16
	wID = wparam << 16 >> 16
	hwndCtl = lparam

	switch wID
	case CMD_LOAD
		/* 読み込むファイル名を取得 */
		dialog "c", 16, "C言語ソースファイル"
		if stat == 0 {
			swbreak
		}
		path = refstr
		/* ファイルを読み込む */
		noteload path
		/* いったんノードを空にし、ノードを追加して行く */
		node_num = 0
		for i, 0, notemax
			noteget s, i
			/* 本ツールから参照するデータは、先頭からのコメント行としてある */
			if(strmid(s, 0, 2) == "//") {
				s = strmid(s, 2, strlen(s) - 2)
				split s, ",", t, x, y
				node_t(node_num) = double(t)
				node_x(node_num) = double(x)
				node_y(node_num) = double(y)
				node_num++
			}
		next
		/* 読み込んだデータで、状態を初期化する */
		gosub *UpdateNodeT
		gosub *UpdateNCS
		node_sel = -1
		seg_sel = -1
		t_sel = 0.0
		swbreak
	case CMD_SAVE
		if node_num == 0 {
			dialog "保存するノードが有りません。", 1, APPNAME
			swbreak
		}
		/* 書き出すファイル名を取得 */
		dialog "c", 17, "C言語ソースファイル"
		if stat == 0 {
			swbreak
		}
		path = refstr
		if getpath(path, 2/*拡張子のみ(.???)*/) == "" {
			path += ".c" /* 拡張子が省略されたら、デフォルト拡張子を付加する */
		}
		/* 本ツールから参照するデータを、先頭からのコメント行として追加する */
		note = ""
		for i, 0, node_num
			noteadd "//" + node_t(i) + "," + node_x(i) + "," + node_y(i)
		next
		/* C言語ソースとしてコンパイルするデータを、非コメント行として追加する */
		seg_num = node_num - 1
		if seg_num >= 1 {
			/* 画面中心を(0,0)とし、スケールしない座標系でNCS補間係数を作り直す。
			 * P/ECEの画面を、(-64,-44)-(64,44)、または、(-44,-64)-(44,64) と見なした座標系である。
			 * P/ECEで利用する時は、NCS補間を行った後、(0,0) が画面中心になるようシフトすれば良い。
			 */
			ddim tmp_node_x, node_num
			ddim tmp_node_y, node_num
			for i, 0, node_num
				tmp_node_x(i) = (node_x(i) - CANVAS_CENTER) / CANVAS_SCALE
				tmp_node_y(i) = (node_y(i) - CANVAS_CENTER) / CANVAS_SCALE
			next
			newmod tmp_ncs_x, NCS, node_t, tmp_node_x, node_num
			newmod tmp_ncs_y, NCS, node_t, tmp_node_y, node_num

			name = getpath(path, 1/*拡張子無し*/ | 8/*ディレクトリ無し*/ | 16/*小文字化*/)
			noteadd "typedef struct _NCS {"
			noteadd "  int n_seg;"
			noteadd "  float seg[" + seg_num + "][5];"
			noteadd "} NCS;"
			noteadd "static const NCS " + name + "_x = { " + seg_num + ", {"
			for i, 0, seg_num
				t = NCS_get_t(tmp_ncs_x, i)
				a = NCS_get_a(tmp_ncs_x, i)
				b = NCS_get_b(tmp_ncs_x, i)
				c = NCS_get_c(tmp_ncs_x, i)
				d = NCS_get_d(tmp_ncs_x, i)
				noteadd "  {" + t + "," + a + "," + b + "," + c + "," + d + "},"
			next
			noteadd "} };"
			noteadd "static const NCS " + name + "_y = { " + seg_num + ", {"
			for i, 0, seg_num
				t = NCS_get_t(tmp_ncs_y, i)
				a = NCS_get_a(tmp_ncs_y, i)
				b = NCS_get_b(tmp_ncs_y, i)
				c = NCS_get_c(tmp_ncs_y, i)
				d = NCS_get_d(tmp_ncs_y, i)
				noteadd "  {" + t + "," + a + "," + b + "," + c + "," + d + "},"
			next
			noteadd "} };"
			noteadd "const NCS* const " + name + "[2] = {"
			noteadd "  &" + name + "_x,"
			noteadd "  &" + name + "_y,"
			noteadd "};"

			delmod tmp_ncs_x
			delmod tmp_ncs_y
		}
		/* ファイルに書き出す */
		notesave path
		swbreak
	case CMD_EXIT
		sendmsg hwnd, WM_CLOSE
		swbreak
	case CMD_HELP
		s  = "[使い方]\n\n"
		s += "点を移動するには…\n"
		s += "→点を左ボタンでドラッグしてください。\n"
		s += "※ドラッグ中は曲線を簡略計算しており、軌道が不正確です。\n\n"
		s += "点を先頭、または、末尾に追加するには…\n"
		s += "→先頭の点、または、末尾の点の近くで右クリックしてください。\n\n"
		s += "点を線の途中に追加するには…\n"
		s += "→線の上で右クリックしてください。\n\n"
		s += "点を削除するには…\n"
		s += "→点の上で右クリックするか、画面外へ左ドラッグしてください。\n"
		dialog s, 0, APPNAME
		swbreak
	case CMD_ABOUT
		s = "NcsEdit - 自然3次スプラインエディター\n" + APPVER
		dialog s, 0, APPNAME
		swbreak
	swend

	return

/*--------------------------------------------------------------------------*/

*OnLButtonDown
	fwKeys = wparam
	xPos = double(lparam << 16 >> 16)
	yPos = double(lparam >> 16)

	if node_sel != -1 {
		SetCapture hwnd
	}

	return

/*--------------------------------------------------------------------------*/

*OnLButtonUp
	fwKeys = wparam
	xPos = double(lparam << 16 >> 16)
	yPos = double(lparam >> 16)

	GetCapture
	if stat == hwnd {
		ReleaseCapture
		x = node_x(node_sel)
		y = node_y(node_sel)
		if (x < 0) or (x > SCREEN_SIZE - 1) or (y < 0) or (y > SCREEN_SIZE - 1) {
			/* ノード削除 */
			for i, node_sel, node_num - 1
				node_x(i) = node_x(i + 1)
				node_y(i) = node_y(i + 1)
			next
			node_num--
			node_sel = -1
		}
		gosub *UpdateNodeT
		gosub *UpdateNCS
		gosub *UpdateSel
	}

	return

/*--------------------------------------------------------------------------*/

*OnMouseMove
	fwKeys = wparam
	xPos = double(lparam << 16 >> 16)
	yPos = double(lparam >> 16)

	GetCapture
	if stat == hwnd {
		node_x(node_sel) = xPos
		node_y(node_sel) = yPos
		gosub *UpdateNodeT
	} else {
		gosub *UpdateSel
	}

	return

/*--------------------------------------------------------------------------*/

*OnRButtonDown
	fwKeys = wparam
	xPos = double(lparam << 16 >> 16)
	yPos = double(lparam >> 16)

	GetCapture
	if stat == hwnd {
		return
	}
	if node_sel == -1 {
		/* ノード追加 */
		if node_num == MAX_NODE {
			return
		}
		if seg_sel == -1 {
			if node_num <= 1 {
				new_pos = 0
			} else {
				dx = node_x(0) - xPos
				dy = node_y(0) - yPos
				d1 = dx * dx + dy * dy
				dx = node_x(node_num - 1) - xPos
				dy = node_y(node_num - 1) - yPos
				d2 = dx * dx + dy * dy
				if d1 <= d2 {
					new_pos = 0
				} else {
					new_pos = node_num
				}
			}
			x = xPos
			y = yPos
		} else {
			new_pos = seg_sel + 1
			x = NCS_interpolate(ncs_x, t_sel)
			y = NCS_interpolate(ncs_y, t_sel)
		}
		for i, node_num, new_pos, -1
			node_x(i) = node_x(i - 1)
			node_y(i) = node_y(i - 1)
		next
		node_x(new_pos) = x
		node_y(new_pos) = y
		node_num++
	} else {
		/* ノード削除 */
		for i, node_sel, node_num - 1
			node_x(i) = node_x(i + 1)
			node_y(i) = node_y(i + 1)
		next
		node_num--
		node_sel = -1
	}
	gosub *UpdateNodeT
	gosub *UpdateNCS
	gosub *UpdateSel

	return

/****************************************************************************
 *	その他
 ****************************************************************************/

*UpdateNodeT
	if node_num == 0 {
		return
	}
	ddim d, MAX_NODE
	if node_num >= 2 {
		sum = 0.0
		for i, 0, node_num - 1
			dx = node_x(i + 1) - node_x(i)
			dy = node_y(i + 1) - node_y(i)
			d(i) = sqrt(dx * dx + dy * dy)
			if d(i) == 0.0 {
				d(i) = 1.0
			}
			sum += d(i)
		next
		for i, 0, node_num - 1
			d(i) /= sum
		next
	}
	t = 0.0
	for i, 0, node_num
		node_t(i) = t
		t += d(i)
	next

	return

/*--------------------------------------------------------------------------*/

*UpdateNCS
	if UpdateNCS_init != 0 {
		delmod ncs_x
		delmod ncs_y
		UpdateNCS_init = 0
	}
	if node_num == 0 {
		return
	}
	newmod ncs_x, NCS, node_t, node_x, node_num
	newmod ncs_y, NCS, node_t, node_y, node_num
	UpdateNCS_init = 1

	return

/*--------------------------------------------------------------------------*/

/* [in] xPos,yPos */
*UpdateSel
	t_sel = 0.0

	node_sel = -1
	for i, 0, node_num
		x = node_x(i)
		y = node_y(i)
		if (absf(x - xPos) <= NODE_HALF) and (abs(y - yPos) <= NODE_HALF) {
			node_sel = i
			_break
		}
	next

	seg_sel = -1
	if node_num <= 1 {
		return
	}
	d_min = DBL_MAX
	t = 0.0
	repeat
		t += SEG_DIV / (node_num - 1)
		if t >= 1.0 {
			break
		}
		x = NCS_interpolate(ncs_x, t)
		y = NCS_interpolate(ncs_y, t)
		dx = absf(x - xPos)
		dy = absf(y - yPos)
		if (dx <= NODE_HALF) and (dy <= NODE_HALF) {
			d = sqrt(dx * dx + dy * dy)
			if d < d_min {
				d_min = d
				t_sel = t
			}
		}
	loop
	if t_sel != 0.0 {
		for i, 0, node_num - 1
			if t_sel < node_t(i + 1) {
				seg_sel = i
				_break
			}
		next
	}

	return

