//-----------------------------------------------------------
// $Id: PRC_searchNextSpc.cc,v 1.3 2000/06/14 13:29:37 yamauchi Exp $
// Copyright (C) 1998 徳永 麻由美 TOKUNAGA Mayumi
//
// Proximity Skip による移動
// searcNextSpcPRC.cc
//-----------------------------------------------------------

#include "mpi2.h"
#include "IntVec3.h"
#include "PRC_searchNextSpc.h"

inline IntVec3 getNextSpcPos(const IntVec3& nowSpcpos, IntVec3 relapos)
{
    return(IntVec3(nowSpcpos.get(IntVec3::X) + relapos.get(IntVec3::X),
		   nowSpcpos.get(IntVec3::Y) + relapos.get(IntVec3::Y),
		   nowSpcpos.get(IntVec3::Z) + relapos.get(IntVec3::Z)));
}    
inline static IntVec3 getRelativePosition(const int dir, const int relapos)
{
    IntVec3	pos;

    switch (dir)
    {
    case 0:
	pos.set(relapos, 0, 0);
	break;
    case 1:
	pos.set(0, relapos, 0);
	break;
    case 2:
	pos.set(0, 0, relapos);
	break;
    }
    return pos;
}

//------------------------------
// doubleの符号を double で返す (sub. of calcDDA)
//------------------------------
inline static double dSignD(double x) 
{
    if(x < 0.0)
    {
	return(-1.0);
    }else if(x > 0.0)
    {
	return(1.0);
    }else 
    {
	return(0.0);
    }
}

//------------------------------------------------------------
// searchNextSpcPRC implementation
//------------------------------------------------------------
// constructor
//------------------------------
searchNextSpcPRC::
searchNextSpcPRC(void)
{
    this->mPartVolMin.set(-1.0, -2.0, -3.0);
    this->mCellLength	= -1.0;
    int i;
    for(i = 0; i < 3; i++)
    {
	this->mSpcIdxMin[i] = -1;
	this->mSpcIdxMax[i] = -1;
    }
    this->mRayPropSkipMethod	= ELSE_rayprop;
    this->mNetworkType		= ELSE_network;

    // derived variables
    this->mCellHalf	= -1.0;
}

//------------------------------
// destructor
//------------------------------
searchNextSpcPRC::
~searchNextSpcPRC(void)
{
    // void 
}

//------------------------------
// 次空間へ移動できるかをテストする。定義空間内なら yes 
// 	IN : pSpcPos に元の位置を入れてが関数を呼ぶ。
//	IN : relapos 移動距離
// テストのみ副作用なし
//------------------------------
YorN searchNextSpcPRC::
testCanGo(IntVec3* pSpcPos, IntVec3 relapos)
{
    if (pSpcPos->get(IntVec3::X) + relapos.get(IntVec3::X) >=
	this->mSpcIdxMax[0] ||
	pSpcPos->get(IntVec3::X) + relapos.get(IntVec3::X)<
	this->mSpcIdxMin[0] ||
	pSpcPos->get(IntVec3::Y) + relapos.get(IntVec3::Y)>=
	this->mSpcIdxMax[1] ||
	pSpcPos->get(IntVec3::Y) + relapos.get(IntVec3::Y)<
	this->mSpcIdxMin[1] ||
	pSpcPos->get(IntVec3::Z) + relapos.get(IntVec3::Z)>=
	this->mSpcIdxMax[2] ||
	pSpcPos->get(IntVec3::Z) + relapos.get(IntVec3::Z)<
	this->mSpcIdxMin[2] )
    {
	return(NO);
    }else
    {
	return(YES);
    }
}



//------------------------------------------------------
// PRC or DDA 切替え用
//------------------------------------------------------
IntVec3 searchNextSpcPRC::
getNextSubspaceRelative(const int skipnum,DDAInfoClass *ddainfo,IntVec3 spc_pos)
{
    IntVec3     relapos;
    IntVec3 tmp = spc_pos;
    if (skipnum < 2)
    {
	DIR_3D dir = this->getDirByDDA(ddainfo);
	this->goNextSpace(&tmp, dir);
	relapos.set(tmp.get(0) - spc_pos.get(0), tmp.get(1) - spc_pos.get(1),
                    tmp.get(2) - spc_pos.get(2));
    }
    else 
    {
	relapos = getSpcByPRC(skipnum - 1, ddainfo);
    }	
    return(relapos);
}
//-------------------------------------------------------
// 次空間を探索する
//      Proximity clouds 法を適用
//-------------------------------------------------------
YorN searchNextSpcPRC::
moveRayToNextCalcSubspaceWithSkip(Ray *pRay)
{
    DDAInfoClass ddainfo = pRay->getSpcDDAInfo();
    IntVec3      spcpos  = pRay->getSpcPos();

    // 1.レイが次に行く次の空間を求める
    // この関数は現在の空間位置とスキップ距離の情報から新しい空間位置を決める
    if(ddainfo.isDDAinited() == NO)
    {
	this->initializeDDA(&ddainfo, pRay->getPos(), pRay->getDir(),
			    spcpos);
    }
    YorN isNextSpaceInDefspc = YES;
    while(1)
    {
	int idx[3];
	spcpos.getAsArray(idx);
	int skipnum = this->mObjInSpcMap.get(idx).get();
	IntVec3 relapos = 
		    this->getNextSubspaceRelative(skipnum, &ddainfo, spcpos); 
	isNextSpaceInDefspc = this->testAndGoIfItCan(&spcpos, relapos);
	if(isNextSpaceInDefspc == YES)
	{
	    if(this->isObject(spcpos) == YES)
	    {
		pRay->setSpcDDAInfo(ddainfo);
		pRay->setSpcPos(spcpos);
		return(YES);
		break;
	    }
	}else
	{
	    // 何も存在しないまま定義空間を外れてしまった．もう行く場所はない
	    // isNextSpaceInDefspc == NO となって loop を抜ける
	    break;
	}
    }
    pRay->setSpcDDAInfo(ddainfo);
    pRay->setSpcPos(spcpos);
    return(isNextSpaceInDefspc);
}
                 				 
//------------------------------------------------------------
// 次空間探索を行う
//	次空間探索を行い、次空間の方向を返す
//	しかし、次空間の方向がまだ定義空間内部かどうかは不明
//------------------------------------------------------------
IntVec3 searchNextSpcPRC::
getSpcByPRC(const int skipnum, DDAInfoClass *pDInfo)
{
    const double CellLen	= this->mCellLength;
    //    const double CellHalf 	= this->mCellHalf;

    // DDA info is already initialized
    assert(pDInfo->isDDAinited() == YES);
    
    int travAxis = pDInfo->getTravAxis();// トラバース方向軸    
    
    int OtherAxis1 = (travAxis + 1) % 3; // 進行軸以外軸 1
    int OtherAxis2 = (travAxis + 2) % 3; // 進行軸以外軸 2

    // 進行軸以外の軸方向にも進むか 相対位置を移動させてみる
    const Vector3 relaPos(pDInfo->getRelativePos());
    const Vector3 DDAVec(pDInfo->getDDAVec());

    // 相対位置計算
    double t1 = relaPos.get(OtherAxis1) + skipnum * DDAVec.get(OtherAxis1); 
    double t2 = relaPos.get(OtherAxis2) + skipnum * DDAVec.get(OtherAxis2); 
    
    // 移動した後の相対位置の絶対値   
    const double absT1 = fabs(t1);
    const double absT2 = fabs(t2);
    
    int it1 = (int) (dSignD(t1) * (absT1 + CellLen /2.0) / CellLen);
    int it2 = (int) (dSignD(t2) * (absT2 + CellLen /2.0) / CellLen);

    t1 = t1 - it1 * CellLen;  
    t2 = t2 - it2 * CellLen;

    IntVec3 relaID = getRelativePosition(OtherAxis1, it1) + 
                     getRelativePosition(OtherAxis2, it2) +
		     getRelativePosition(travAxis, (int)(skipnum *
			  dSignD(pDInfo->getDDAVec().get(travAxis))));
    pDInfo->changeRelativePos(t1, OtherAxis1, t2, OtherAxis2);

    return(relaID);
}

//------------------------------
// 次空間へ移動する。
// 	IN OUT: pSpcPos に元の位置を入れて関数を呼ぶ。
//	IN    : dir 移動方向
// 空間を移動する
//------------------------------
void searchNextSpcPRC::
goNextSpacePRC(IntVec3* pSpcPos, IntVec3 relapos) const
{
    int x = pSpcPos->get(IntVec3::X) + relapos.get(IntVec3::X);
    int y = pSpcPos->get(IntVec3::Y) + relapos.get(IntVec3::Y);
    int z = pSpcPos->get(IntVec3::Z) + relapos.get(IntVec3::Z);
    
    pSpcPos ->set(x, y, z);
}    

