//------------------------------------------------------------
// $Id: boundtest.cc,v 1.3 2000/05/21 15:42:18 yamauchi Exp $
//------------------------------------------------------------
//	boundTest.cc
//	test ray and vector with some box range
//	1998年1月27日(火)
//	(C) Copyright Hitoshi YAMAUCHI
//
//	位置ベクトル、ray とある box の範囲とのテスト。ここでの box は 
//	x, y, z 軸に沿った箱のことを言う 
//------------------------------------------------------------
#include <assert.h>
#include "mpi2.h"
#include "Vector3.h"
#include "constants.h"


//#define TEST_isHitBoundingBox 1
//#ifndef TEST_isHitBoundingBox

// #ifndef ONLY_VOLREN
// // stand alone 
// #  ifdef FOR_STANDALONE
// #    include "StandAloneModeObjects.h"
// #  else
// #    include "ProcObj.h"
// #  endif			// #ifdef FOR_STANDALONE
// #endif				// #ifndef ONLY_VOLREN

//------------------------------------------------------------
// 交差判定 bounding box 
//	An Introduction to Ray Tracing, Roman Kuchkuda p.p. 1039--1060
// 返り値は Hit した面で non-hit で DIR_ILLEGAL を返す
// ちょうど端に当たった場合でも空間の小さい方ならば hit とする。
//	つまり hit の範囲は、[min, max)
//------------------------------
DIR_3D isHitBoundingBox(const Vector3& minBox,    
			const Vector3& maxBox, 
			const Vector3& rayOrigin,
			const Vector3& rayDir,
			Vector3 *pHitPoint)
{
    static DIR_3D hitDirection[9] = {
	DIR_XPLUS,
	DIR_XMINUS,
	DIR_YPLUS,
	DIR_YMINUS,
	DIR_ZPLUS,
	DIR_ZMINUS,
	DIR_HERE,		
	DIR_3D_NUM,
	DIR_ILLEGAL,
    };
    
    int i;
    Vector3 boxCenter	= (maxBox + minBox) / 2.0;
    Vector3 boxHalfSize	= (maxBox - minBox) / 2.0;
    Vector3 vv = rayDir;
    vv.VNormalize();		// 必ず正規化しているのなら省略可能 improve me
    double distance = PlusInfty;
    DIR_3D hitSide  = hitDirection[DIR_ILLEGAL];
    for(i = 0; i < 3; i++)
    {
	// check each faces (副空間は X,Y,Z 方向に沿って存在するため、
	// 各成分のうち視線方向ベクトルがあるものだけ試す)  
	//   1e-5 は誤差対策。絶対値が 0.00001 より大の成分がある時に 
	//   Test する
	if(fabs(vv.get(i)) > 1e-5)
	{			   
	    assert(boxHalfSize.get(i) > 0.0); // Box の大きさなので > 0.0
		
	    // 箱中心から Plus 方向の面との交差試験
	    // 	    double tmpDist = 
	    // 		(boxHalfSize.get(i) - boxCentToVp.get(i)) / vv.get(i);
	    double tmpDist = 
		((boxCenter.get(i) + boxHalfSize.get(i)) - rayOrigin.get(i)) 
		    / vv.get(i);
	    if((0.0 < tmpDist) && (tmpDist < distance))
	    {
		static double hitPos[3];
		hitPos[i] = boxCenter.get(i) + boxHalfSize.get(i);
		// 		hitPos[(i + 1) % 3] = 
		// 		    fabs(boxCentToVp.get((i + 1) % 3) + 
		// 			 tmpDist * vv.get((i + 1) % 3));
		// 		hitPos[(i + 2) % 3] = 
		// 		    fabs(boxCentToVp.get((i + 2) % 3) + 
		// 			 tmpDist * vv.get((i + 2) % 3));
		hitPos[(i + 1) % 3] = 
		    rayOrigin.get((i + 1) % 3) + tmpDist * vv.get((i + 1) % 3);
		hitPos[(i + 2) % 3] = 
		    rayOrigin.get((i + 2) % 3) + tmpDist * vv.get((i + 2) % 3);
		Vector3 relaPos(hitPos[0], hitPos[1], hitPos[2]);
		relaPos = relaPos - boxCenter;

		// x 方向のテストをしているとして、y, z が範囲内にある
		// かを調べる。その際、[min, max) の範囲にあるかをみる
		// 変数に一時的に代入しているのはわかりやすさのため。
		// コンパイラさん、最適化頼みます 
		// 変数名の由来  
		//	bhs[YZ] = box half Size
		//      r[YZ]   = relative position [YZ] from box center
		// 以下の絶対値を用いたコードでは [min, max) が
		// (min, max) の範囲となって線が画像にあらわれてしまう
		// 		if((fabs(relaPos.get((i + 1) % 3)) < 
		// 		    boxHalfSize.get((i + 1) % 3))	&& 
		// 		   (fabs(relaPos.get((i + 2) % 3)) <
		// 		    boxHalfSize.get((i + 2) % 3)))
		double bhsY  = boxHalfSize.get((i + 1) % 3);
		double bhsZ  = boxHalfSize.get((i + 2) % 3);
		double rposY = relaPos.get((i + 1) % 3);
		double rposZ = relaPos.get((i + 2) % 3);
		if((((-bhsY) <= rposY) && (rposY < bhsY)) && 
		   (((-bhsZ) <= rposZ) && (rposZ < bhsZ)))
		{
		    hitSide  = hitDirection[i * 2];
		    distance = tmpDist;
		    pHitPoint->set(hitPos[0], hitPos[1], hitPos[2]);
		}
	    }	   
	    
	    // 箱中心から Minus 方向の面
	    // 	    tmpDist = 
	    // 		(-boxHalfSize.get(i) - boxCentToVp.get(i)) / vv.get(i);
	    tmpDist = 
		((boxCenter.get(i) - boxHalfSize.get(i)) - rayOrigin.get(i)) 
		    / vv.get(i);
	    if((tmpDist > 0) && (tmpDist < distance))
	    {
		double hitPos[3];
		hitPos[i] = boxCenter.get(i) - boxHalfSize.get(i);
		// 		hitPos[(i + 1) % 3] = 
		// 		    fabs(boxCentToVp.get((i + 1) % 3) + 
		// 			 tmpDist * vv.get((i + 1) % 3));
		// 		hitPos[(i + 2) % 3] = 
		// 		    fabs(boxCentToVp.get((i + 2) % 3) + 
		// 			 tmpDist * vv.get((i + 2) % 3));
		hitPos[(i + 1) % 3] = 
		    rayOrigin.get((i + 1) % 3) + tmpDist * vv.get((i + 1) % 3);
		hitPos[(i + 2) % 3] = 
		    rayOrigin.get((i + 2) % 3) + tmpDist * vv.get((i + 2) % 3);
		Vector3 relaPos(hitPos[0], hitPos[1], hitPos[2]);
		relaPos = relaPos - boxCenter;

		double bhsY  = boxHalfSize.get((i + 1) % 3);
		double bhsZ  = boxHalfSize.get((i + 2) % 3);
		double rposY = relaPos.get((i + 1) % 3);
		double rposZ = relaPos.get((i + 2) % 3);
		if((((-bhsY) <= rposY) && (rposY < bhsY)) && 
		   (((-bhsZ) <= rposZ) && (rposZ < bhsZ)))
		{
		    hitSide  = hitDirection[i * 2];
		    distance = tmpDist;
		    pHitPoint->set(hitPos[0], hitPos[1], hitPos[2]);
		}
	    }	   
	}
    }	
    if(distance == PlusInfty)
    {
	return(DIR_ILLEGAL);	
    }else
    {
	return(hitSide);
    }
}

//------------------------------------------------------------
// pos が [min, max) の中にあるか 
//	片方を閉区間片方を開区間にしたのは連続した体積を考えたため
//	(境界のものは min の方に入る)
// 	体積は軸にそって存在するので判定は各軸ごとに行なえば良い
//------------------------------------------------------------
// changed from isViewPointInDefineSpace
YorN isPosInBox(const Vector3& pos, 
		const Vector3& minpos, const Vector3& maxpos)
{
    int i;
    for(i = 0; i < 3; i++)
    {
	if((pos.get(i) < minpos.get(i)) || (maxpos.get(i) < pos.get(i)))
	{
	    return(NO);
	}
    }
    return(YES);
}

YorN isPosInBoxWithRes(const Vector3& pos, 
		       const Vector3& minpos, const Vector3& maxpos,
		       double err)
{
    assert(err > 0.0);
    int i;
    for(i = 0; i < 3; i++)
    {
	if(((pos.get(i) + err) < minpos.get(i)) ||
	   (maxpos.get(i) < (pos.get(i) - err)))
	{
	    return(NO);
	}
    }
    return(YES);
}
