/*
 *	clipncs.h
 *
 *	曲線補間
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Tue Sep 22 12:42:29 JST 2009 Naoyuki Sawa
 *	- 新規作成。
 *	* Sat Sep 26 16:40:28 JST 2009 Naoyuki Sawa
 *	- ferguson_coons_interpolate()、CRS_interpolate()を追加しました。
 *	- もう、NCS(自然3次スプライン)だけではなくなったので、本当はモジュール名"clipncs.h"を変えるべきなのですが、
 *	  他のドキュメントからこのモジュール名を参照している箇所が混乱するのを防ぐため、このままとすることにします。
 *	* Sun Oct 04 22:59:17 JST 2009 Naoyuki Sawa
 *	- NCS構造体データを出力するツール、tool/NcsEditを作成しました。
 *	  本モジュールには変更有りません。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 *	* Thu Feb 25 21:08:23 JST 2016 Naoyuki Sawa
 *	- CRS_interpolate_circular()を追加しました。
 */
#ifndef __CLIP_NCS_H__
#define __CLIP_NCS_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

/****************************************************************************
 *	自然3次スプライン(NCS : Natural cubic spline)
 ****************************************************************************/

/* アセンブラコードから直接参照するため、レイアウト固定です!! */
typedef struct _NCS_SEG {
	float t;	/* + 0,4: 区間の開始時刻 */
	float a;	/* + 4,4: 補間係数(t^3) */
	float b;	/* + 8,4: 補間係数(t^2) */
	float c;	/* +12,4: 補間係数(t^1) */
	float d;	/* +16,4: 補間係数(t^0) */
} NCS_SEG;		/* =20 */

/* アセンブラコードから直接参照するため、レイアウト固定です!! */
typedef struct _NCS {
	int n_seg;			/* +0,4  : 区間数 */
	NCS_SEG seg[0/*n_seg*/];	/* +4,20N: 区間の配列 */
} NCS;					/* =4+20N */

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

/* NCS補間係数を作成します。
 * [in]
 *	t		時刻の配列。昇順であること。
 *	t_stride	時刻の要素間距離。[バイト]
 *	p		座標の配列。
 *	p_stride	座標の要素間距離。[バイト]
 *	n		時刻と座標の配列の要素数。1以上であること。
 * [out]
 *	戻り値		作成したNCS補間係数へのポインタ。
 * [note]
 *	* 自然3次スプラインの資料を、keep/自然3次スプライン補間.7zに保存しました。
 *	  本関数は、上述の資料のアルゴリズムに従って、実装しました。
 *
 *	* 作成したNCS構造体の中に、引数tおよびpへのポインタ参照は格納されません。
 *	  補間に必要な情報は、すべて、NCS構造体の中に構築されます。
 *	  本関数が処理を返した後、呼び出し側は、引数tとpの内容を破壊しても構いません。
 *
 *	* 以下に、使用例を示します。
 *
 *	- <例1> tとpを構造体にまとめ、t_strideとp_strideに同じ値を指定する方法:
 *
 *		const struct {
 *			float t;
 *			vec2f p;
 *		} node[] = {
 *			{ 1.0f, {  20.0f, 20.0f } },
 *			{ 2.5f, { 100.0f, 40.0f } },
 *			{ 3.0f, {  70.0f, 70.0f } },
 *			{ 5.0f, {  40.0f, 10.0f } },
 *
 *		};
 *		void sample1() {
 *			NCS* ncs_x = NCS_new(&node[0].t,   sizeof node[0],
 *			                     &node[0].p.x, sizeof node[0],
 *			                     ARRAY_SIZE(node));
 *			NCS* ncs_y = NCS_new(&node[0].t,   sizeof node[0],
 *			                     &node[0].p.y, sizeof node[0],
 *			                     ARRAY_SIZE(node));
 *			float t;
 *			for(t = 0.0f; t <= 6.0f; t += 0.01f) {
 *				float x = NCS_interpolate(ncs_x, t);
 *				float y = NCS_interpolate(ncs_y, t);
 *				render_point(&render, x, y, 3);
 *			}
 *			NCS_free(ncs_x);
 *			NCS_free(ncs_y);
 *		}
 *
 *	- <例2> tとpを個別の配列とし、t_strideとp_strideに別の値を指定する方法:
 *
 *		const float T[] = { 1.0f, 2.5f, 3.0f, 5.0f };
 *		const vec2f P[] = {
 *			{  20.0f, 20.0f },
 *			{ 100.0f, 40.0f },
 *			{  70.0f, 70.0f },
 *			{  40.0f, 10.0f },
 *		};
 *		void sample2() {
 *			NCS* ncs_x = NCS_new(&T[0],   sizeof T[0],
 *			                     &P[0].x, sizeof P[0],
 *			                     ARRAY_SIZE(T));
 *			NCS* ncs_y = NCS_new(&T[0],   sizeof T[0],
 *			                     &P[0].y, sizeof P[0],
 *			                     ARRAY_SIZE(T));
 *			float t;
 *			for(t = 0.0f; t <= 6.0f; t += 0.01f) {
 *				float x = NCS_interpolate(ncs_x, t);
 *				float y = NCS_interpolate(ncs_y, t);
 *				render_point(&render, x, y, 3);
 *			}
 *			NCS_free(ncs_x);
 *			NCS_free(ncs_y);
 *		}
 */
NCS* NCS_new(const float t[/*n*/], int t_stride, const float p[/*n*/], int p_stride, int n);

/* NCS補間係数を開放します。
 * [in]
 *	self		NCS補間係数へのポインタ。
 */
void NCS_free(NCS* self);

/* NCS補間を行った座標を取得します。
 * [in]
 *	self		NCS補間係数へのポインタ。
 *	t		時刻。
 *			NCS_new()に指定した時刻の配列の、最初の時刻未満や、最後の時刻超過でも構いません。
 * [out]
 *	戻り値		座標。
 */
float NCS_interpolate(NCS* self, float t);

/****************************************************************************
 *	Ferguson/Coons(ファーガソン/クーンズ)曲線補間
 ****************************************************************************/

/* Ferguson/Coons曲線補間を行った座標を取得します。
 * [in]
 *	p0		始点座標。
 *	p1		終点座標。
 *	v0		始点の接線ベクトル。
 *	v1		終点の接線ベクトル。
 *	t		時刻。
 *			時刻0.0が始点、時刻1.0が終点に相当します。
 *			0.0未満や、1.0超過の時刻を指定することも可能です。
 * [out]
 *	戻り値		座標。
 * [note]
 *	* 以下に、使用例を示します。
 *
 *		const vec2f p0 = {  40.0f, 30.0f };
 *		const vec2f p1 = {  80.0f, 70.0f };
 *		const vec2f v0 = {  30.0f, 40.0f };
 *		const vec2f v1 = { -20.0f, 30.0f };
 *		void sample() {
 *			float t;
 *			for(t = -1.0f; t <= 2.0f; t += 0.01f) {
 *				float x = ferguson_coons_interpolate(p0.x, p1.x, v0.x, v1.x, t);
 *				float y = ferguson_coons_interpolate(p0.y, p1.y, v0.y, v1.y, t);
 *				render_point(&render, x, y, 3);
 *			}
 *		}
 */
float ferguson_coons_interpolate(float p0, float p1, float v0, float v1, float t);

/****************************************************************************
 *	キャットマルロムスプライン(CRS : Catmull-Rom spline)
 ****************************************************************************/

/* Catmull-Romスプライン補間を行った座標を取得します。
 * [in]
 *	p,p_stride,n	NCS_new()の引数使用と同じです。NCS_new()のコメントを参照してください。
 *	t		時刻。
 *			時刻0.0が最初の節点、時刻(n-1)が最後の節点に相当します。
 *			0.0未満や、(n-1)超過の時刻を指定することも可能です。
 * [out]
 *	戻り値		座標。
 * [note]
 *	* 以下に、使用例を示します。
 *
 *		const vec2f P[] = {
 *			{  20.0f, 20.0f },
 *			{ 100.0f, 40.0f },
 *			{  70.0f, 70.0f },
 *			{  40.0f, 10.0f },
 *		};
 *		void sample() {
 *			float t;
 *			for(t = -1.0f; t <= 4.0f; t += 0.01f) {
 *				float x = CRS_interpolate(&P[0].x, sizeof P[0], ARRAY_SIZE(P), t);
 *				float y = CRS_interpolate(&P[0].y, sizeof P[0], ARRAY_SIZE(P), t);
 *				render_point(&render, x, y, 3);
 *			}
 *		}
 */
float CRS_interpolate(const float* p, int p_stride, int n, float t);

//{{2016/02/25追加:CRS_interpolate_circular()を追加しました。
//与えられた節点配列を、円環状に解釈する事以外は、CRS_interpolate()と同じです。
// - 例えば、節点{A,B,C}を与えた場合、(t=0.0)が節点A、(t=1.0)が節点B、(t=2.0)が節点C、(t=3.0)が節点Aに対応します。
//   (t<0.0),及び,(t>3.0)の範囲に対しても、節点A,B,C,A,B,C,…の繰り返しとして計算します。
// - わかりやすく説明すると…
//   ・CRS_interpolate()の場合は、(t<0),及び,(t>(n-1))の範囲を与えると、両端の辺の方向を延長した無限大の方向に飛んで行きます。
//   ・CRS_interpolate_circular()の場合は、(t<0),及び,(t>(n-1))の範囲を与えても、(0<=t<=(n-1))の範囲と同じ動きが繰り返します。
//   一般な用途では、今回追加したCRS_interpolate_circular()の方が使い易いかも知れません。
float CRS_interpolate_circular(const float* p, int p_stride, int n, float t);
//}}2016/02/25追加:CRS_interpolate_circular()を追加しました。

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif /*__CLIP_NCS_H__*/
