//
//	Tiny2D.c
//
//	簡易物理エンジン
//
//	* Sun Apr 30 22:20:58 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//	  □参考資料:「［書籍連動］「ゲームで学ぶJavaScript入門」物理エンジンの詳細解説」(https://thinkit.co.jp/series/4770)
//	  上記の記事の「Tiny2D.js」(https://thinkit.co.jp/sites/default/files/file_attachement/8467_Physics.zip)を参考にして、かなり変更して作成しました。
//	- 特に大きな変更点は、物理エンジンのインスタンスを廃止した事です。
//	  オリジナル版のTiny2D.jsでは、重力加速度とエンティティリストを保持する、物理エンジンクラスが定義されていました。
//	  周期処理の関数step()は、物理エンジンクラスのメソッドでした。
//	  同じように実装しても構わなかったのですが、アプリケーションがエンティティリストを保持する方が自由度が高くなると思い、構造を変更する事にしました。
//	  当実装では、物理エンジンクラスは存在せず、エンティティリストと重力加速度は、周期処理の関数step()の引数としました。
//	* Mon May 01 22:50:38 JST 2017 Naoyuki Sawa
//	- 「線分付近の円が他の円との衝突で座標補正された時に線分をすり抜ける問題」を低減しました。
//	  詳細は、Tiny2D.cのTINY2D_AVOID_LINE_THROUGHシンボルの定義箇所、及び、Tiny2D_CircleEntity_collWithCircle()の実装を参照して下さい。
//
#include "clip.h"
#define TINY2D_AVOID_LINE_THROUGH	//このシンボルを定義すると、「線分付近の円が他の円との衝突で座標補正された時に線分をすり抜ける問題」を低減します。左記の問題が許容出来る場合は、このシンボルを定義しない方が処理負荷が軽いです。2017/05/01現在は、左記の問題が許容できないと判断して、このシンボルを定義しています。
//*****************************************************************************
//	ローカル関数宣言
//*****************************************************************************
#undef  pow2	//念のため
#define pow2(x) Tiny2D_pow2(x)
static double Tiny2D_pow2(double x) { return x * x; }
//-----------------------------------------------------------------------------
#ifdef  TINY2D_AVOID_LINE_THROUGH
static void Tiny2D_CircleEntity_collWithCircle(Tiny2D_CircleEntity* This, Tiny2D_CircleEntity* Peer, GSList* entities);
#else //TINY2D_AVOID_LINE_THROUGH
static void Tiny2D_CircleEntity_collWithCircle(Tiny2D_CircleEntity* This, Tiny2D_CircleEntity* Peer);
#endif//TINY2D_AVOID_LINE_THROUGH
static void Tiny2D_CircleEntity_collWithLine(Tiny2D_CircleEntity* This, Tiny2D_LineEntity* Peer);
static void Tiny2D_CircleEntity_collWithRect(Tiny2D_CircleEntity* This, Tiny2D_RectEntity* Peer);
//*****************************************************************************
//	ベクトル
//*****************************************************************************
//コンストラクタ
Tiny2D_Vec Tiny2D_Vec_new(double x, double y) {
	Tiny2D_Vec This;
	This.x = x;
	This.y = y;
	return This;
}
//-----------------------------------------------------------------------------
//加算
Tiny2D_Vec Tiny2D_Vec_add(Tiny2D_Vec v1, Tiny2D_Vec v2) {
	return Tiny2D_Vec_new(v1.x + v2.x, v1.y + v2.y);
}
//-----------------------------------------------------------------------------
//減算
Tiny2D_Vec Tiny2D_Vec_sub(Tiny2D_Vec v1, Tiny2D_Vec v2) {
	return Tiny2D_Vec_new(v1.x - v2.x, v1.y - v2.y);
}
//-----------------------------------------------------------------------------
//乗算
Tiny2D_Vec Tiny2D_Vec_mul(Tiny2D_Vec v, double d) {
	return Tiny2D_Vec_new(v.x * d, v.y * d);
}
//-----------------------------------------------------------------------------
//除算
Tiny2D_Vec Tiny2D_Vec_div(Tiny2D_Vec v, double d) {
	return Tiny2D_Vec_new(v.x / d, v.y / d);
}
//-----------------------------------------------------------------------------
//内積
double Tiny2D_Vec_dot(Tiny2D_Vec v1, Tiny2D_Vec v2) {
	return (v1.x * v2.x) + (v1.y * v2.y);
}
//-----------------------------------------------------------------------------
//外積
double Tiny2D_Vec_cross(Tiny2D_Vec v1, Tiny2D_Vec v2) {
	return (v1.x * v2.y) - (v1.y * v2.x);
}
//-----------------------------------------------------------------------------
//距離の2乗
double Tiny2D_Vec_dist2(Tiny2D_Vec v1, Tiny2D_Vec v2) {
	return Tiny2D_Vec_length2(Tiny2D_Vec_sub(v1, v2));
}
//-----------------------------------------------------------------------------
//距離
double Tiny2D_Vec_dist(Tiny2D_Vec v1, Tiny2D_Vec v2) {
	return sqrt(Tiny2D_Vec_dist2(v1, v2));
}
//-----------------------------------------------------------------------------
//長さの2乗
double Tiny2D_Vec_length2(Tiny2D_Vec v) {
	return Tiny2D_Vec_dot(v, v);
}
//-----------------------------------------------------------------------------
//長さ
double Tiny2D_Vec_length(Tiny2D_Vec v) {
	return sqrt(Tiny2D_Vec_length2(v));
}
//-----------------------------------------------------------------------------
//正規化
Tiny2D_Vec Tiny2D_Vec_normal(Tiny2D_Vec v) {
	double length = Tiny2D_Vec_length(v);
	if(length) { v = Tiny2D_Vec_div(v, length); }
	return v;
}
//-----------------------------------------------------------------------------
//反射ベクトルを求めます。
//[in]
//	n		反射面の法線ベクトル。
//			反射面と垂直でさえあれば、逆向きでも問題有りません。
//			当関数の処理の中で相殺されて、結果は同じになります。
//	v		速度ベクトル。
//			vreflect()の引数Eとは、逆向きである事に注意して下さい。
//[out]
//	戻り値		反射ベクトル。
//[note]
//	- 既存のvreflect()関数を元にして作成しました。
//	  詳細は、clipfix.hのvreflect()関数の説明を参照して下さい。
//	  使い易さのために、当関数の引数vは、逆向きvreflect()関数の引数Eと逆向きにしました。
Tiny2D_Vec Tiny2D_Vec_reflect(Tiny2D_Vec n, Tiny2D_Vec v) {
	return Tiny2D_Vec_sub(v, Tiny2D_Vec_mul(n, Tiny2D_Vec_dot(v, n) * 2.0));
	//                                         ~~~~~~~~~~~~~~~~~~~~              (v・n)
	//                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~       2(v・n)
	//                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      2(v・n)n
	//     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ v - 2(v・n)n
}
//*****************************************************************************
//	円エンティティ
//*****************************************************************************
//円エンティティを動的に作成する。
//[in]
//	x,y			中心座標。
//	radius			半径。
//	restitution		反発係数。(0.0〜1.0)
//	deceleration		減速度合。(0.0〜1.0)	0.0ならば固定円、0.0以外ならば動く円になります。
//[out]
//	戻り値			円エンティティ。
//[note]
//	- 当関数で作成した円エンティティは、アプリケーション側にてfree()で解放して下さい。
//	- 動く円を作成した場合、accel,velocity,onhitフィールドはアプリケーション側が直接設定して下さい。
Tiny2D_CircleEntity* Tiny2D_CircleEntity_new(double x, double y, double radius, double restitution, double deceleration) {
	Tiny2D_CircleEntity* This = malloc(sizeof(Tiny2D_CircleEntity));
	Tiny2D_CircleEntity_init(This, x, y, radius, restitution, deceleration);
	return This;
}
//-----------------------------------------------------------------------------
//円エンティティを静的に初期化する。
//[in]
//	This			初期化するメモリ。
//	x,y			中心座標。
//	radius			半径。
//	restitution		反発係数。(0.0〜1.0)
//	deceleration		減速度合。(0.0〜1.0)	0.0ならば固定円、0.0以外ならば動く円になります。
//[note]
//	- 動く円を作成した場合、accel,velocity,onhitフィールドはアプリケーション側が直接設定して下さい。
void Tiny2D_CircleEntity_init(Tiny2D_CircleEntity* This, double x, double y, double radius, double restitution, double deceleration) {
	if(radius <= 0.0) { DIE(); }					//引数が不正
	if((restitution < 0.0) || (restitution > 1.0)) { DIE(); }	//引数が不正
	if((deceleration < 0.0) || (deceleration > 1.0)) { DIE(); }	//引数が不正
	memset(This, 0, sizeof(Tiny2D_CircleEntity));
	This->shape		= Tiny2D_ShapeCircle;
	This->p			= Tiny2D_Vec_new(x, y);
	This->radius		= radius;
	This->restitution	= restitution;
	This->deceleration	= deceleration;
//不要	This->accel		= Tiny2D_Vec_new(0.0, 0.0);
//不要	This->velocity		= Tiny2D_Vec_new(0.0, 0.0);
}
//-----------------------------------------------------------------------------
//衝突判定,及び,衝突処理を行う。(円⇔円)
//[in]
//	This			自分のエンティティ。(動く円,又は,固定円)
//	Peer			相手のエンティティ。(動く円,又は,固定円)
#ifdef  TINY2D_AVOID_LINE_THROUGH
//	entities		エンティティのリスト。
static void Tiny2D_CircleEntity_collWithCircle(Tiny2D_CircleEntity* This, Tiny2D_CircleEntity* Peer, GSList* entities) {
#else //TINY2D_AVOID_LINE_THROUGH
static void Tiny2D_CircleEntity_collWithCircle(Tiny2D_CircleEntity* This, Tiny2D_CircleEntity* Peer) {
#endif//TINY2D_AVOID_LINE_THROUGH
	Tiny2D_Vec n, t, v;
	double n1, n2, t1, t2, d;
	//Thisが固定円ならば、ThisとPeerを入れ替える。
	if(!This->deceleration) { XCHG(This, Peer); }
	//両方とも固定円だったならば、何もせずに帰る。
	if(!This->deceleration) { return; }	//ここまで
	//(2つの円の中心距離の2乗≧2つの円の半径の合計の2乗)ならば、衝突していないので何もせずに帰る。
	if(Tiny2D_Vec_dist2(This->p, Peer->p) >= pow2(This->radius + Peer->radius)) { return; }	//ここまで
	//動く円と動く円の衝突ならば…
	if(Peer->deceleration) {
		//相手と自分の衝突点の法線ベクトルを求める。(向きは逆でも構わない。)
		n = Tiny2D_Vec_sub(This->p, Peer->p);
		n = Tiny2D_Vec_normal(n);
		//相手と自分の衝突点の接線ベクトルを求める。(向きは逆でも構わない。)
		t = Tiny2D_Vec_new(-n.y, n.x);
		//自分の速度ベクトルを、法線成分と接線成分に分解する。
		n1 = Tiny2D_Vec_dot(This->velocity, n);
		t1 = Tiny2D_Vec_dot(This->velocity, t);
		//相手の速度ベクトルを、法線成分と接線成分に分解する。
		n2 = Tiny2D_Vec_dot(Peer->velocity, n);
		t2 = Tiny2D_Vec_dot(Peer->velocity, t);
		//相手の法線成分と自分の接線成分を合成して、自分の反射ベクトルとする。
		This->velocity = Tiny2D_Vec_add(Tiny2D_Vec_mul(n, n2), Tiny2D_Vec_mul(t, t1));
		//自分の接線成分と相手の接線成分を合成して、相手の反射ベクトルとする。
		Peer->velocity = Tiny2D_Vec_add(Tiny2D_Vec_mul(n, n1), Tiny2D_Vec_mul(t, t2));
		//反発係数を掛ける。
		This->velocity = Tiny2D_Vec_mul(This->velocity, This->restitution * Peer->restitution);
		Peer->velocity = Tiny2D_Vec_mul(Peer->velocity, This->restitution * Peer->restitution);
		//衝突前の座標に戻す。
		d = (This->radius + Peer->radius) - Tiny2D_Vec_dist(This->p, Peer->p);
		v = Tiny2D_Vec_mul(n, d / 2.0);
#ifdef  TINY2D_AVOID_LINE_THROUGH
		{
			Tiny2D_Vec s1, v1, v2, w;
			double t, t1, t2;
			int i, hit1 = 0, hit2 = 0;								//自分と相手の'補正無しフラグ'をクリアしておく。1ループ目と2ループ目の間でリセットしない事に注意して下さい。
			//処理内容は、以下の通りです。
			//│以下の処理を、最大、2ループ繰り返す。
			//│1ループ目では、自分と相手について、'半分の補正'で線分を跨ぐかを判定する。
			//│両方とも線分を跨いだ場合は、自分にも相手にも補正を行わず、2ループ目も実行しない。
			//│両方とも線分を跨がなかった場合は、自分と相手に半分づつ補正を行い、2ループ目を実行しない。
			//│片方だけ線分を跨がなかった場合は、跨がなかった方に半分の補正を行い、2ループ目を実行する。
			//│補正を行った側について、2ループ目でもう一度'半分の補正'で線分を跨ぐかを判定する。
			//│線分を跨がなかった場合は、もう一度'半分の補正'を行う。(つまり片側に全部の補正を行う。)
			//│線分を跨いだ場合は、2ループ目での補正を行わない。
			for(i = 0; i < 2; i++) {
				GSList* list = entities;
				while(list) {
					Tiny2D_Entity* e = list->data;
					if(e->shape == Tiny2D_ShapeLine) {
						Tiny2D_LineEntity* l = &e->line;
					//{{Tiny2D_CircleEntity_collWithLine()の「衝突判定」の処理を元にして作成しました。
						if(!hit1) {							//自分がまだ補正無しになっていなければ…
							s1 = This->p;						//円の補正前の中心座標
							v1 = v;							//円の補正前の中心座標から補正後の中心座標へのベクトル(=自分の補正ベクトル)
							v2 = l->v;						//線分の始点から終点へのベクトル
							w  = Tiny2D_Vec_sub(l->s, s1);				//円の補正前の中心座標から線分の始点へのベクトル
							t  = Tiny2D_Vec_cross(v1, v2);
							if(t) {							//速度ベクトルと線分が平行でなく…
								t1 = Tiny2D_Vec_cross(w, v2) / t;
								if((t1 >= 0.0) && (t1 <= 1.0)) {		//補正の範囲内で…
									t2 = Tiny2D_Vec_cross(w, v1) / t;
									if((t2 >= 0.0) && (t2 <= 1.0)) {	//線分の範囲内ならば…
										hit1 = 1;			//線分と補正ベクトルが交差しているので、補正無しとする。
										if(hit2) { break; }		//自分と相手が両方とも補正無しになったら、残りのエンティティとの衝突判定は不要です。
									}
								}
							}
						}
						if(!hit2) {							//相手がまだ補正無しになっていなければ…
							s1 = Peer->p;						//円の補正前の中心座標
							v1 = Tiny2D_Vec_new(-v.x, -v.y);			//円の補正前の中心座標から補正後の中心座標へのベクトル(=相手の補正ベクトル)
							v2 = l->v;						//線分の始点から終点へのベクトル
							w  = Tiny2D_Vec_sub(l->s, s1);				//円の補正前の中心座標から線分の始点へのベクトル
							t  = Tiny2D_Vec_cross(v1, v2);
							if(t) {							//速度ベクトルと線分が平行ならば衝突無しなので、補正有り。
								t1 = Tiny2D_Vec_cross(w, v2) / t;
								if((t1 >= 0.0) && (t1 <= 1.0)) {		//補正の範囲外ならば衝突無しなので、補正有り。
									t2 = Tiny2D_Vec_cross(w, v1) / t;
									if((t2 >= 0.0) && (t2 <= 1.0)) {	//線分の範囲外ならば衝突無しなので、補正有り。
										hit2 = 1;			//線分と補正ベクトルが交差しているので、補正無し。
										if(hit1) { break; }		//自分と相手が両方とも補正無しになったら、残りのエンティティとの衝突判定は不要です。
									}
								}
							}
						}
					//}}Tiny2D_CircleEntity_collWithLine()の「衝突判定」の処理を元にして作成しました。
					}
					list = g_slist_next(list);
				}
				if(hit1) {									//自分が補正無しならば…
					if(hit2) {								//相手が補正無しならば…
						break;	//ここまで
					} else {								//相手が補正有りならば…
						Peer->p = Tiny2D_Vec_sub(Peer->p, v);				//相手に対して'半分の補正'を行う。
						//1ループ目ならば、2ループ目へ。自分の補正を行わなかった分、相手の補正を2倍行えるかを判定する。
					}
				} else {									//自分が補正有りならば…
					if(hit2) {								//相手が補正無しならば…
						This->p = Tiny2D_Vec_add(This->p, v);				//自分に対して'半分の補正'を行う。
						//1ループ目ならば、2ループ目へ。相手の補正を行わなかった分、自分の補正を2倍行えるかを判定する。
					} else {								//相手が補正有りならば…
						This->p = Tiny2D_Vec_add(This->p, v);				//自分に対して'半分の補正'を行う。
						Peer->p = Tiny2D_Vec_sub(Peer->p, v);				//相手に対して'半分の補正'を行う。
						break;	//ここまで
					}
				}
			}
		}
#else //TINY2D_AVOID_LINE_THROUGH
		This->p = Tiny2D_Vec_add(This->p, v);
		Peer->p = Tiny2D_Vec_sub(Peer->p, v);
#endif//TINY2D_AVOID_LINE_THROUGH
	//動く円と固定円の衝突ならば…
	} else {
		//相手の中心座標から自分の中心座標への単位ベクトルを求める。(向きは逆でも構わない。)
		n = Tiny2D_Vec_sub(This->p, Peer->p);
		n = Tiny2D_Vec_normal(n);
		//反射ベクトルを求める。
		This->velocity = Tiny2D_Vec_reflect(n, This->velocity);
		//反発係数を掛ける。
		This->velocity = Tiny2D_Vec_mul(This->velocity, This->restitution * Peer->restitution);
		//衝突前の座標に戻す。
		d = (This->radius + Peer->radius) - Tiny2D_Vec_dist(This->p, Peer->p);
		v = Tiny2D_Vec_mul(n, d);
		This->p = Tiny2D_Vec_add(This->p, v);
	}
	//衝突時のコールバックを呼び出す。
	if(This->onhit) { (*This->onhit)(This, (Tiny2D_Entity*)Peer); }
	if(Peer->onhit) { (*Peer->onhit)(Peer, (Tiny2D_Entity*)This); }
}
//-----------------------------------------------------------------------------
//衝突判定,及び,衝突処理を行う。(円⇔矩形)
//[in]
//	This			自分のエンティティ。(動く円,又は,固定円)
//	Peer			相手のエンティティ。(矩形)
static void Tiny2D_CircleEntity_collWithRect(Tiny2D_CircleEntity* This, Tiny2D_RectEntity* Peer) {
	Tiny2D_Vec h, n, v;
	//Thisが固定円ならば、何もせずに帰る。
	if(!This->deceleration) { return; }	//ここまで
	//矩形の4辺上で最も円に近い座標hを求める。(衝突有りならばこれが衝突点となる。)
	h.x = clamp(This->p.x, Peer->vmin.x, Peer->vmax.x);
	h.y = clamp(This->p.y, Peer->vmin.y, Peer->vmax.y);
	//座標hが円の内側でなければ衝突無しなので、何もせずに帰る。
	if(Tiny2D_Vec_dist2(h, This->p) >= pow2(This->radius)) { return; }	//ここまで
	//衝突点から円の中心座標への単位ベクトルを求める。(向きは逆でも構わない。)
	n = Tiny2D_Vec_sub(This->p, h);
	n = Tiny2D_Vec_normal(n);
	//反射ベクトルを求める。
	This->velocity = Tiny2D_Vec_reflect(n, This->velocity);
	//反発係数を掛ける。
	This->velocity = Tiny2D_Vec_mul(This->velocity, This->restitution * Peer->restitution);
	//衝突前の座標に戻す。
	v = Tiny2D_Vec_sub(h, This->p);
	v = Tiny2D_Vec_normal(v);
	v = Tiny2D_Vec_mul(v, This->radius);
	This->p = Tiny2D_Vec_sub(h, v);
	//衝突時のコールバックを呼び出す。
	if(This->onhit) { (*This->onhit)(This, (Tiny2D_Entity*)Peer); }
}
//-----------------------------------------------------------------------------
//衝突判定,及び,衝突処理を行う。(円⇔線分)
//[in]
//	This			自分のエンティティ。(動く円,又は,固定円)
//	Peer			相手のエンティティ。(線分)
static void Tiny2D_CircleEntity_collWithLine(Tiny2D_CircleEntity* This, Tiny2D_LineEntity* Peer) {
	Tiny2D_Vec s1, v1, v2, v, n;
	double t, t1, t2;
	//Thisが固定円ならば、何もせずに帰る。
	if(!This->deceleration) { return; }	//ここまで
	//--- 衝突判定 ---
	//□参考資料:「2D衝突編 その10 線分と線分の衝突」(http://marupeke296.com/COL_2D_No10_SegmentAndSegment.html)
	s1 = Tiny2D_Vec_sub(This->p, This->velocity);	//円の移動前の中心座標
	v1 = This->velocity;				//円の移動前の中心座標から移動後の中心座標へのベクトル(=速度ベクトル)
	v2 = Peer->v;					//線分の始点から終点へのベクトル
	v  = Tiny2D_Vec_sub(Peer->s, s1);		//円の移動前の中心座標から線分の始点へのベクトル
	t  = Tiny2D_Vec_cross(v1, v2);
	if(!t) { return; }				//速度ベクトルと線分が平行ならば衝突無しなので、ここまで。
	t1 = Tiny2D_Vec_cross(v, v2) / t;
	if((t1 < 0.0) || (t1 > 1.0)) { return; }	//移動の範囲外ならば衝突無しなので、ここまで。
	t2 = Tiny2D_Vec_cross(v, v1) / t;
	if((t2 < 0.0) || (t2 > 1.0)) { return; }	//線分の範囲外ならば衝突無しなので、ここまで。
	//--- 衝突処理 ---
	//線分に垂直な単位ベクトルを求める。(向きは逆でも構わない。)
	n = Tiny2D_Vec_new(-Peer->v.y, Peer->v.x);
	n = Tiny2D_Vec_normal(n);
	//反射ベクトルを求める。
	This->velocity = Tiny2D_Vec_reflect(n, This->velocity);
	//反発係数を掛ける。
	This->velocity = Tiny2D_Vec_mul(This->velocity, This->restitution * Peer->restitution);
	//衝突前の座標に戻す。
	This->p = s1;
	//衝突時のコールバックを呼び出す。
	if(This->onhit) { (*This->onhit)(This, (Tiny2D_Entity*)Peer); }
}
//*****************************************************************************
//	矩形エンティティ
//*****************************************************************************
//矩形エンティティを動的に作成する。
//[in]
//	x,y			左上座標。
//	w,h			矩形のサイズ。
//	restitution		反発係数。(0.0〜1.0)
//[out]
//	戻り値			矩形エンティティ。
//[note]
//	- 当関数で作成した矩形エンティティは、アプリケーション側にてfree()で解放して下さい。
Tiny2D_RectEntity* Tiny2D_RectEntity_new(double x, double y, double w, double h, double restitution) {
	Tiny2D_RectEntity* This = malloc(sizeof(Tiny2D_RectEntity));
	Tiny2D_RectEntity_init(This, x, y, w, h, restitution);
	return This;
}
//-----------------------------------------------------------------------------
//矩形エンティティを静的に初期化する。
//[in]
//	This			初期化するメモリ。
//	x,y			左上座標。
//	w,h			矩形のサイズ。
//	restitution		反発係数。(0.0〜1.0)
void Tiny2D_RectEntity_init(Tiny2D_RectEntity* This, double x, double y, double w, double h, double restitution) {
	if((w <= 0.0) || (h <= 0.0)) { DIE(); }				//引数が不正
	if((restitution < 0.0) || (restitution > 1.0)) { DIE(); }	//引数が不正
	memset(This, 0, sizeof(Tiny2D_RectEntity));
	This->shape		= Tiny2D_ShapeRect;
	This->vmin		= Tiny2D_Vec_new(x, y);
	This->vmax		= Tiny2D_Vec_new(x + w, y + h);
	This->restitution	= restitution;
}
//*****************************************************************************
//	線分エンティティ
//*****************************************************************************
//線分エンティティを動的に作成する。
//[in]
//	x1,y1			始点座標。
//	x2,y2			終点座標。
//	restitution		反発係数。(0.0〜1.0)
//[out]
//	戻り値			線分エンティティ。
//[note]
//	- 当関数で作成した線分エンティティは、アプリケーション側にてfree()で解放して下さい。
Tiny2D_LineEntity* Tiny2D_LineEntity_new(double x1, double y1, double x2, double y2, double restitution) {
	Tiny2D_LineEntity* This = malloc(sizeof(Tiny2D_LineEntity));
	Tiny2D_LineEntity_init(This, x1, y1, x2, y2, restitution);
	return This;
}
//-----------------------------------------------------------------------------
//線分エンティティを静的に初期化する。
//[in]
//	This			初期化するメモリ。
//	x1,y1			始点座標。
//	x2,y2			終点座標。
//	restitution		反発係数。(0.0〜1.0)
void Tiny2D_LineEntity_init(Tiny2D_LineEntity* This, double x1, double y1, double x2, double y2, double restitution) {
	if((x1 == x2) && (y1 == y2)) { DIE(); }				//引数が不正
	if((restitution < 0.0) || (restitution > 1.0)) { DIE(); }	//引数が不正
	memset(This, 0, sizeof(Tiny2D_LineEntity));
	This->shape		= Tiny2D_ShapeLine;
	This->s			= Tiny2D_Vec_new(     x1,      y1);
	This->v			= Tiny2D_Vec_new(x2 - x1, y2 - y1);
	This->restitution	= restitution;
}
//*****************************************************************************
//	物理エンジン
//*****************************************************************************
//周期処理を実行する。
//[in]
//	entities		エンティティのリスト。
//	elapsed			経過時間。[ミリ秒]
//	gravityX,gravityY	引力。[ピクセル/秒^2]
void Tiny2D_Engine_step(GSList* entities, double elapsed, double gravityX, double gravityY) {
	//エンティティを移動する。
	{
		Tiny2D_Vec gravity = Tiny2D_Vec_new(gravityX * elapsed, gravityY * elapsed);				//                (重力ベクトル   * 経過時間)	共通	────┐
		GSList* list = entities;										//								│
		while(list) {												//								│
			Tiny2D_Entity* e = list->data;									//								│
			if(e->shape == Tiny2D_ShapeCircle) {								//								│
				Tiny2D_CircleEntity* c = &e->circle;							//								│
				if(c->deceleration) {									//								│
					c->velocity = Tiny2D_Vec_mul(c->velocity, pow(c->deceleration, elapsed));	//速度ベクトル *= (減速度合       ^ 経過時間)	個別		│
					c->velocity = Tiny2D_Vec_add(c->velocity, Tiny2D_Vec_mul(c->accel, elapsed));	//速度ベクトル += (加速度ベクトル * 経過時間)	個別		│
					c->velocity = Tiny2D_Vec_add(c->velocity, gravity);				//速度ベクトル += (重力ベクトル   * 経過時間)	共通	←───┘
					c->p = Tiny2D_Vec_add(c->p, c->velocity);
				}
			}
			list = g_slist_next(list);
		}
	}
	//衝突判定,及び,衝突処理を行う。
	{
		GSList* list1 = entities;
		while(list1) {
			GSList* list2 = g_slist_next(list1);
			Tiny2D_Entity* e1 = list1->data;
			while(list2) {
				Tiny2D_Entity* e2 = list2->data;
				if(e1 == e2) { DIE(); }	//同じエンティティがリストに二重に登録されている。呼び出し側のバグです。
				switch(e1->shape) {
				case Tiny2D_ShapeCircle:
					switch(e2->shape) {
					default:DIE();
#ifdef  TINY2D_AVOID_LINE_THROUGH
					case Tiny2D_ShapeCircle: Tiny2D_CircleEntity_collWithCircle(&e1->circle, &e2->circle, entities); break;	//円⇔円
#else //TINY2D_AVOID_LINE_THROUGH
					case Tiny2D_ShapeCircle: Tiny2D_CircleEntity_collWithCircle(&e1->circle, &e2->circle); break;	//円⇔円
#endif//TINY2D_AVOID_LINE_THROUGH
					case Tiny2D_ShapeRect:   Tiny2D_CircleEntity_collWithRect(  &e1->circle, &e2->rect  ); break;	//円⇔矩形
					case Tiny2D_ShapeLine:   Tiny2D_CircleEntity_collWithLine(  &e1->circle, &e2->line  ); break;	//円⇔線分
					}
					break;
				case Tiny2D_ShapeRect:
					switch(e2->shape) {
					default:DIE();
					case Tiny2D_ShapeCircle: Tiny2D_CircleEntity_collWithRect(  &e2->circle, &e1->rect  ); break;	//円⇔矩形
					case Tiny2D_ShapeRect:   /** no job **/                                                break;
					case Tiny2D_ShapeLine:   /** no job **/                                                break;
					}
					break;
				case Tiny2D_ShapeLine:
					switch(e2->shape) {
					default:DIE();
					case Tiny2D_ShapeCircle: Tiny2D_CircleEntity_collWithLine(  &e2->circle, &e1->line  ); break;	//円⇔線分
					case Tiny2D_ShapeRect:   /** no job **/                                                break;
					case Tiny2D_ShapeLine:   /** no job **/                                                break;
					}
					break;
				}
				list2 = g_slist_next(list2);
			}
			list1 = g_slist_next(list1);
		}
	}
}
//*****************************************************************************
//	使用例
//*****************************************************************************
#if 0
GSList* entities;
static void onhit(Tiny2D_CircleEntity* This, Tiny2D_Entity* Peer) { printf("onhit: %d <-> %d\n", This->shape, Peer->shape); }
int cpp_main(int argc, char* argv[]) {
	if(RRS_i_job(pRRS) != (RRS_n_job(pRRS) - 1)) { DIE(); }	//最後のジョブがUIジョブである事を確認する。
	if(!setjmp(env_exit)) {
		//固定円
		entities = g_slist_append(entities, Tiny2D_CircleEntity_new(DISP_X * 0.40, DISP_Y * 0.25, DISP_X * 0.02, 1.0, 0.0));
		entities = g_slist_append(entities, Tiny2D_CircleEntity_new(DISP_X * 0.60, DISP_Y * 0.25, DISP_X * 0.02, 1.0, 0.0));
		entities = g_slist_append(entities, Tiny2D_CircleEntity_new(DISP_X * 0.20, DISP_Y * 0.40, DISP_X * 0.02, 1.0, 0.0));
		entities = g_slist_append(entities, Tiny2D_CircleEntity_new(DISP_X * 0.80, DISP_Y * 0.40, DISP_X * 0.02, 1.0, 0.0));
		entities = g_slist_append(entities, Tiny2D_CircleEntity_new(DISP_X * 0.50, DISP_Y * 0.90, DISP_X * 0.02, 1.0, 0.0));
		//矩形
		entities = g_slist_append(entities, Tiny2D_RectEntity_new(DISP_X * 0.05, DISP_Y * 0.70, DISP_X * 0.40, DISP_Y * 0.10, 0.9));
		entities = g_slist_append(entities, Tiny2D_RectEntity_new(DISP_X * 0.55, DISP_Y * 0.70, DISP_X * 0.40, DISP_Y * 0.10, 0.9));
		//線分
		entities = g_slist_append(entities, Tiny2D_LineEntity_new(DISP_X * 0.27, DISP_Y * 0.30, DISP_X * 0.45, DISP_Y * 0.55, 1.0));
		entities = g_slist_append(entities, Tiny2D_LineEntity_new(DISP_X * 0.73, DISP_Y * 0.30, DISP_X * 0.55, DISP_Y * 0.55, 1.0));
		for(;;) {
			schedule();
			if(!(now % SEC(0.3))) {
				Tiny2D_CircleEntity* e = Tiny2D_CircleEntity_new(RND32_RANGE(seed, 0, DISP_X), 0.0, DISP_X * 0.015, 1.0, 0.9);
				e->velocity.x = RND32_RANGE(seed, -DISP_X * 0.005, DISP_X * 0.005);
				e->onhit = onhit;
				entities = g_slist_append(entities, e);
			}
			Tiny2D_Engine_step(entities, 1.0 / FPS, 0.0, DISP_Y * 0.01);
			{
				GSList* list = entities;
				while(list) {
					Tiny2D_Entity* _e = (Tiny2D_Entity*)list->data;
					list = g_slist_next(list);
					switch(_e->shape) {
					default:DIE();
					case Tiny2D_ShapeCircle:
						{
							Tiny2D_CircleEntity* e = &_e->circle;
							CbkQue_draw_oval(pCbkQue, 0, e->p.x - e->radius, e->p.y - e->radius, e->radius * 2.0, e->radius * 2.0);
							if((e->p.y > DISP_Y) && (e->velocity.y > 0.0)) {
								entities = g_slist_remove(entities, e);
								free(e);
							}
						}
						break;
					case Tiny2D_ShapeRect:
						{
							Tiny2D_RectEntity* e = &_e->rect;
							CbkQue_draw_box(pCbkQue, 0, e->vmin.x, e->vmin.y, e->vmax.x - e->vmin.x, e->vmax.y - e->vmin.y);
						}
						break;
					case Tiny2D_ShapeLine:
						{
							Tiny2D_LineEntity* e = &_e->line;
							CbkQue_draw_line(pCbkQue, 0, e->s.x, e->s.y, e->s.x + e->v.x, e->s.y + e->v.y);
						}
						break;
					}
				}
			}
		}
	}
	return EXIT_SUCCESS;
}
#endif
