/*
 *	clipncss.h
 *
 *	ステートレスNCS補間シーケンサ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2016 Naoyuki Sawa
 *
 *	* Mon Mar 21 21:34:20 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- clipncs.cのNCS_new()をベースに、以下の変更を行って作成しました。
 *	  ・NCS_new()は全体を通じてfloat演算でした。
 *	    当モジュールは時刻をuint16_t,座標をint16_t,演算はdouble,補間係数の格納は1次以上の成分をfp16,0次の成分(=座標)をint16_tとしました。
 *	    このように、それぞれの箇所に適切な型を選択する事によって、演算精度の向上とデータ容量を低減するようにしました。(効果は未検証です。)
 *	  ・NCS_new()は実行時に補間係数を求める方法しか有りませんでした。
 *	    当モジュールはNCS_new()と同様に実行時に補間係数を求める事も出来ますし、実行前にツールを使って補間係数を求めておく事も出来ます。
 *	    節点が十数個以上ぐらいになると、補間係数の計算には数秒〜数十秒以上時間が掛かるので、実行時に補間係数を求めるのは難しいからです。
 *	    当モジュールの方法ならば、ビルド時間が許す限り多くの節点の補間データを作成しておくことが可能です。(ROM化を考えた場合も有利です。)
 *	    時間が掛かるのはあくまでも補間係数を求める処理だけであり、節点の数が増えても実行時の補間処理の時間には影響有りません。
 *	  ・NCS_new()は一つの構造体ごとに一つの軸しか補間出来ませんでした。
 *	    複数の軸を補間するためには複数の構造体を作成する必要が有り、メモリの無駄が生じていました。
 *	    当モジュールは一つの構造体で複数の軸を管理出来、ノード毎に一つの時刻を共有する複数の軸の補間を行うので、容量と速度を改善出来ます。
 *	- 以上のような改善を行ったのですが、とは言え、アルゴリズム自体はclipncs.cのNCS_new()と全く同じです。
 *	  アルゴリズムの詳細については、clipncs.cのNCS_new()を参照して下さい。
 *	* Thu Mar 24 21:04:15 JST 2016 Naoyuki Sawa
 *	- NcsSeq_GetVal_subr()の丸め処理が、+∞方向に偏っていた問題を修正しました。
 *	  単純に0.5を足して切り捨てるのは間違いで、正負を考慮して丸めを行うのが正しい処理でした。
 *	* Mon Mar 28 21:40:28 JST 2016 Naoyuki Sawa
 *	- NcsSeq_GetVal_subr()の中で、積和演算処理にfma()を使うように変更しました。
 *	  コードサイズが僅かに減りました。結果は変更前と同じです。
 *	* Thu Mar 31 21:48:20 JST 2016 Naoyuki Sawa
 *	- ST_NcsSeq構造体にtEndフィールドを追加しました。
 *	- NcsSeq_GetLen(),NcsSeqNo_GetLen()を追加しました。
 */
#ifndef __CLIP_NCSS_H__
#define __CLIP_NCSS_H__
#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus
/****************************************************************************
 *	定数、マクロ、構造体
 ****************************************************************************/
//一つの区間の、一つの軸の補間係数
typedef struct _ST_NcsAxs {
	  fp16		a;			//+ 0, 2  補間係数(dt^3)
	  fp16		b;			//+ 2, 2  補間係数(dt^2)
	  fp16		c;			//+ 4, 2  補間係数(dt^1)
	 int16_t	d;			//+ 6, 2  補間係数(dt^0)
} ST_NcsAxs;					//= 8
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//一つの区間
typedef struct _ST_NcsSeg {
	uint16_t	t;			//+ 0, 2  区間の開始時刻	0〜(UINT16_MAX-1)
	ST_NcsAxs	TBL_Axs[1/*nAxs*/];	//+ 2, 8N
} ST_NcsSeg;					//= 2+ 8N
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//一連の区間
typedef struct _ST_NcsSeq {
	uint16_t	tEnd;			//+ 0, 2  最終時刻+1		0〜UINT16_MAX		「'スクリプトで指定した最後の節点'の時刻」です。言い換えると「最後の区間の最終時刻+1」です。このフィールドの値は、NcsSeq_GetLen()がそのままアプリケーションに返すためだけに使用し、NcsSeq_GetVal()の補間処理の計算には使用しません。
	uint8_t		nSeg_1;			//+ 2, 1  区間の数-1		0〜UINT8_MAX
	uint8_t		nAxs_1;			//+ 3, 1  軸の数-1		0〜UINT8_MAX
	ST_NcsSeg	TBL_Seg[1/*nSeg*/];	//+ 4, ?  区間の配列
} ST_NcsSeq;					//= 4+ ?
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
//以下の関数は、実行時にST_NcsSeqを作成する場合に、使用する関数です。
ST_NcsSeq* NcsSeq_New(int (*fnGetTim)(int iNode, void* arg), int (*fnGetPos)(int iNode, int iAxs, void* arg), int nNode, int nAxs, void* arg);
void NcsSeq_Free(ST_NcsSeq* pNcsSeq);
//以下の関数は、実行時にST_NcsSeqを作成する場合と、予めdNcsSeqC.exeを使ってST_NcsSeqを生成しておく場合の、両方で使用する関数です。
int NcsSeq_GetVal(const ST_NcsSeq* pNcsSeq, int t, int iAxs);
void NcsSeq_GetValAll(const ST_NcsSeq* pNcsSeq, int t, int* aVal/*[nAxs]*/);
//最終時刻を取得します。
// - この関数は、アプリケーションが、「'スクリプトで指定した最後の節点'の時刻」を知りたい場合だけに使用する関数です。
//   NcsSeq_GetVal()には、最初の節点の時刻以前の時刻や最初の節点の時刻(=最終時刻)以後の時刻を指定する事も可能であり、最初の区間,及び,最後の区間の補間係数を使って求められます。
int NcsSeq_GetLen(const ST_NcsSeq* pNcsSeq);
/****************************************************************************
 *	dNcsSeqC.exeが出力したバイナリ形式を扱うためのユーティリティ関数
 ****************************************************************************/
#define USE_NCSSEQ_BIN	//ユーティリティ関数が不要ならばこのシンボル定義をコメントアウトしてください。
#ifdef  USE_NCSSEQ_BIN
int NcsSeqNo_GetVal(const void* pNcsSeqBin, int iNcsSeqNo, int t, int iAxs);
void NcsSeqNo_GetValAll(const void* pNcsSeqBin, int iNcsSeqNo, int t, int* aVal/*[nAxs]*/);
int NcsSeqNo_GetLen(const void* pNcsSeqBin, int iNcsSeqNo);
#endif//USE_NCSSEQ_BIN
#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus
#endif//__CLIP_NCSS_H__
