/*
 * $Id: SearchNextSpcDDA.java,v 1.6 2000/06/15 13:55:50 yamauchi Exp $
 *     (M\pi)^2 : Search next Space by 3DDDA 
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source searchNextSpcDDA.h searchNextSpcDDA.cc
 *
 *	次空間探索用 3DDDA の実装 : ここで実装したアルゴリズムが最良と
 *	いうわけではないようだ．
 * 
 *    boolean moveRayToNextCalcSubspace(RayClass *pRay);
 *	pRay はこの関数が終了する時には次に交差判定を行う空間にいる。
 *	返り値 : 次の交差判定空間が定義空間外であれば NO
 *		 定義空間内であれば true を返す
 *
 * 1999年8月 8日(日)
 */

package jp.ac.tohoku.is.archi.mpi2.dda;

import  jp.ac.tohoku.is.archi.mpi2.util.*;
import  jp.ac.tohoku.is.archi.mpi2.obj.Ray;
import  jp.ac.tohoku.is.archi.mpi2.sysparam.*;

/**
 * 内部状態のある interface などを考えていたがそれは普通のクラスだ。
 *     C++ で virtual なものを派生した時には constructor と destructor 
 *     の扱いが繁雑すぎる。
 *------------------------------------------------------------
 * 3DDDA 法を用いた次空間探索
 */
public class SearchNextSpcDDA implements Mpi2Enum{
    // 3DDDA の計算に必要となるデータ
    private Vector3  mDefSpcMin;  // DDA を適用する空間の最小位置
    private double   mCellLength; // DDA で traverse するセルの大きさ
    private int	[]   mSpcIdxMin;  // cell の最小 index (subvolume がある
    private int	[]   mSpcIdxMax;  // 同最大 index:      ので0が最小ではない。)
    private int      mRayPropSkipMethod;// Skip 方法(Skip するしない)
    private int      mNetworkType;	 // ネットワークの形状
    
    // 空間中の物体の存在状況
    private Array3D  mObjInSpcMap; 

    // derived values
    private double   mCellHalf; // cell の長さの半分

    /**
     * local functions
     *------------------------------------------------------------
     * レイの進行方向を XPLUS, XMINUS,.. の形で得る(sub. calcDDA)
     * その方向に行けるかどうかは不明(定義空間内かどうかのテストは行わない。)
     */
    private static int getDirection(DDAInfo dInfo, int goingDirection)  
    {
	int dir;
	switch(goingDirection)
	{
	case Vector3.X:
	    if(dInfo.peekDDAVec().get(Vector3.X) < 0.0)
	    {
		dir = DIR_3D.XMINUS;
	    }else
	    {
		dir = DIR_3D.XPLUS;
	    }
	    break;
	    
	case Vector3.Y:	
	    if(dInfo.peekDDAVec().get(Vector3.Y) < 0.0)
	    {
		dir = DIR_3D.YMINUS;
	    }else
	    {
		dir = DIR_3D.YPLUS;
	    }
	    break;
	    
	case Vector3.Z:
	    if(dInfo.peekDDAVec().get(Vector3.Z) < 0.0)
	    {
		dir = DIR_3D.ZMINUS;
	    }else
	    {
		dir = DIR_3D.ZPLUS;
	    }
	    break;
	    
	default:
	    throw new RuntimeException("Error!: at getOnlyDirection illegal "+
				       "goingdir( = " + goingDirection + ")");
	    // commMpi2_exit(1);
	    // break;
	}
	return(dir);
    }

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

    /**
     * check initialize parameter
     */
    private static void exitIfIllegalParam(double cellLength, 
					   int []spcIdxMin,  int []spcIdxMax, 
					   int rayPropSkipMethod, 
					   int networkType)
    {
	String s = "Error! : SearchNextSpcDDA.exitIfIllegalParam : ";
	if(cellLength <= 0.0)
	{
	    throw new RuntimeException(s + "illegal cellLength (" + 
				       cellLength + ")");
	    // commMpi2_exit(1);
	}
	for(int i = 0; i < 3; i++)
	{
	    if((spcIdxMin[i] < 0) || (spcIdxMax[i] <= 0) ||
	       (spcIdxMin[i] >= spcIdxMax[i]))
	    {
		throw new RuntimeException(s + "illegal spc index. spcMin[" + 
					   i + "] = "  + spcIdxMin[i]  + 
					   ", spcMax[" + spcIdxMax[i] + "]");
		// commMpi2_exit(1);		
	    }
	}
	if((rayPropSkipMethod == NoSkip_rayprop)	||
	   (rayPropSkipMethod == SobokuSkip_rayprop) 	||
	   (rayPropSkipMethod == ProximityClouds_rayprop))
	{
	    // OK! : no skip と 素朴な skip はサポート
	}else
	{
	    throw new RuntimeException(s + "illegal skip method.(" + 
				       rayPropSkipMethod + ")");
	    // commMpi2_exit(1);		
	}
	
	if(networkType == Torus_network)
	{
	    throw new RuntimeException(s + "NIN : Not implemented for " + 
				       "Torus net.");
	    // commMpi2_exit(1);		
	}
	else if(networkType == MultiStage_network)
	{    
	    // OK! : multistage はサポート
	}else
	{
	    throw new RuntimeException(s + "illegal network type = " + 
				       networkType);
	    // commMpi2_exit(1);		
	}
    }

    /**
     * 次空間探索を行う
     *	次空間探索を行い、次空間の方向を返す
     *	しかし、次空間の方向がまだ定義空間内部かどうかは不明
     */
    private int getDirByDDA(DDAInfo dInfo)
    {
	// この関数をテストするために member へのアクセスはここだけにする
	// optimizer of compiler will be delete this code
	double CellLen	= this.mCellLength;
	double CellHalf = this.mCellHalf;
	
	// DDA info is already initialized
	Assert.out(dInfo.isDDAinited() == true, "Error!:dinfo already inited");
	
	int travAxis = dInfo.getTravAxis(); // トラバース方向軸    
	
	int OtherAxis1 = (travAxis + 1) % 3; // 進行軸以外軸 1
	int OtherAxis2 = (travAxis + 2) % 3; // 進行軸以外軸 2
	
	// 進行軸以外の軸方向にも進むか 相対位置を移動させてみる
	Vector3 relaPos = new Vector3(dInfo.peekRelativePos());
	Vector3 DDAVec  = new Vector3(dInfo.peekDDAVec());
	
	// 相対位置計算
	double t1 = relaPos.get(OtherAxis1) + DDAVec.get(OtherAxis1); 
	double t2 = relaPos.get(OtherAxis2) + DDAVec.get(OtherAxis2); 
	
	// 移動した後の相対位置の絶対値
	double absT1 = Math.abs(t1);
	double absT2 = Math.abs(t2);
	
	if((absT1 <= CellHalf) && (absT2 <= CellHalf)) // どちらにも進まない
	{
	    dInfo.changeRelativePos(t1, OtherAxis1, t2, OtherAxis2);
	    int dir = getDirection(dInfo, travAxis);
	    dInfo.setGoingDir(dir);
	    return(dir);		//----- 進行軸方向へ;
	}
	
	// 進行方向以外のベクトルの符号
	double axis1_sign = dSignD((dInfo.peekDDAVec()).get(OtherAxis1));
	double axis2_sign = dSignD((dInfo.peekDDAVec()).get(OtherAxis2));
	
	if((absT1 > CellHalf) && (absT2 <= CellHalf)) // 1の方向に進む
	{
	    double rpos = relaPos.get(OtherAxis1) - axis1_sign * CellLen;
	    dInfo.changeRelativePos(rpos,                    OtherAxis1, 
				    relaPos.get(OtherAxis2), OtherAxis2);
	    int dir = getDirection(dInfo, OtherAxis1);	
	    dInfo.setGoingDir(dir);
	    return(dir);		//----- 1軸方向へ;
	}
	
	if((absT1 <= CellHalf) && (absT2 > CellHalf)) // 2の方向に進む
	{
	    double rpos = relaPos.get(OtherAxis2) - axis2_sign * CellLen;
	    dInfo.changeRelativePos(relaPos.get(OtherAxis1), OtherAxis1,
				    rpos,                    OtherAxis2);
	    int dir = getDirection(dInfo, OtherAxis2);
	    dInfo.setGoingDir(dir);
	    return(dir);		//----- 2軸方向へ;
	}
	
	//--------------------------------------------------
	// それ以外(1,2方向しかし、1,2どちらかへ進む) 
	//--------------------------------------------------
	double param_s1
	    = (axis2_sign * (dInfo.peekDDAVec()).get(OtherAxis2))
            * (CellHalf - (axis1_sign * relaPos.get(OtherAxis1)));
	double param_s2
	    = (axis1_sign * (dInfo.peekDDAVec()).get(OtherAxis1))
            * (CellHalf - (axis2_sign * relaPos.get(OtherAxis2)));
	
	if(param_s1 < param_s2)
	{
	    double rpos = relaPos.get(OtherAxis1) - axis1_sign * CellLen;
	    dInfo.changeRelativePos(rpos,                    OtherAxis1,
				    relaPos.get(OtherAxis2), OtherAxis2);
	    int dir = getDirection(dInfo, OtherAxis1);
	    dInfo.setGoingDir(dir);
	    return(dir);		//----- 1軸方向へ;
	}else
	{
	    double rpos = relaPos.get(OtherAxis2) - axis2_sign * CellLen;
	    dInfo.changeRelativePos(relaPos.get(OtherAxis1), OtherAxis1,
				    rpos,                    OtherAxis2);
	    int dir = getDirection(dInfo, OtherAxis2);
	    dInfo.setGoingDir(dir);
	    return(dir);		//----- 2軸方向へ;
	}
	
    }

    /**
     * 進行軸とDDA用VectorをdInfoにセット(sub. of initializeDDA)
     */
    private void setTraverseAxisNVector(DDAInfo dInfo, Vector3 rayDir)
    {
	// access to member variable at here only
	double cellLen = this.mCellLength;
	
	// ベクトルの絶対値最大の要素を求める
	// 	ベクトルの大きさ(初期値x)
	double dAbsmax = Math.abs(rayDir.get(Vector3.X));
	double dy      = Math.abs(rayDir.get(Vector3.Y)); // ベクトルの大きさ
	double dz      = Math.abs(rayDir.get(Vector3.Z)); 
	int SinkouJiku = Vector3.X;	// トラバース方向軸
	
	if(dAbsmax < dy)
	{
	    dAbsmax    = dy;
	    SinkouJiku = Vector3.Y;
	}
	
	if(dAbsmax < dz)
	{
	    dAbsmax = dz;
	    SinkouJiku = Vector3.Z;
	}
	dInfo.setTravAxis(SinkouJiku); // 進行軸をセットし、
    
	//----- DDA用のベクトルをセットする
	dInfo.setDDAVec(rayDir.mulScalerNew(cellLen / dAbsmax));
    }

    /**
     * 次空間へ移動できるかをテストする。定義空間内なら yes 
     * 	IN : pSpcPos に元の位置を入れてが関数を呼ぶ。
     *	IN : dir 移動方向
     * テストのみ副作用なし
     */
    private boolean testCanGo(IntVec3 spcPos, int dir) 
    {
	switch(dir)
	{
	case DIR_3D.XPLUS:
	    if((spcPos.get(IntVec3.X) + 1) >= this.mSpcIdxMax[0])
	    {
		return(false);
	    }
	    break;
	case DIR_3D.XMINUS:
	    if((spcPos.get(IntVec3.X) - 1) <  this.mSpcIdxMin[0])
	    {
		return(false);
	    }
	    break;
	case DIR_3D.YPLUS:
	    if((spcPos.get(IntVec3.Y) + 1) >= this.mSpcIdxMax[1])
	    {
		return(false);
	    }
	    break;
	case DIR_3D.YMINUS:
	    if((spcPos.get(IntVec3.Y) - 1) <  this.mSpcIdxMin[1])
	    {
		return(false);
	    }
	    break;
	case DIR_3D.ZPLUS:
	    if((spcPos.get(IntVec3.Z) + 1) >= this.mSpcIdxMax[2])
	    {
		return(false);
	    }
	    break;
	case DIR_3D.ZMINUS:
	    if((spcPos.get(IntVec3.Z) - 1) <  this.mSpcIdxMin[2])
	    {
		return(false);
	    }
	    break;
	default:
	    throw new RuntimeException("Error! : at testCanGo Illegal dir.");
	    // commMpi2_exit(1);
	    // break;
	}
	return(true);
    }

    /**
     * 次空間へ移動する。
     * 	IN OUT: spcPos に元の位置を入れて関数を呼ぶ。
     *	IN    : dir 移動方向
     * 空間を移動する
     */
    private void goNextSpace(IntVec3 spcPos, int dir) 
    {
	// forceMove で利用する場合があるのでこの assert は無効
	// Assert.out(this.testCanGo(spcPos, dir) == true, "Error!:");
	
	int x = spcPos.get(IntVec3.X);
	int y = spcPos.get(IntVec3.Y);
	int z = spcPos.get(IntVec3.Z);
	switch(dir)
	{
	case DIR_3D.XPLUS:
	    x++;
	    break;
	case DIR_3D.XMINUS:
	    x--;
	    break;
	case DIR_3D.YPLUS:
	    y++;
	    break;
	case DIR_3D.YMINUS:
	    y--;
	    break;
	case DIR_3D.ZPLUS:
	    z++;
	    break;
	case DIR_3D.ZMINUS:
	    z--;
	    break;
	default:
	    throw new RuntimeException("Error!:goNextSpace() : Illegal dir.");
	    // commMpi2_exit(1);
	    // break;
	}
	spcPos.set(x, y, z);
    }
    
    /**
     * もし移動できるのならその方向に移動し、true を返す。
     * できない場合には移動せずに false を返す。
     */
    private boolean testAndGoIfItCan(IntVec3 spcPos, int dir) 
    {
	if(this.testCanGo(spcPos, dir) == true)
	{
	    this.goNextSpace(spcPos, dir);
	    return(true);
	}else
	{
	    return(false);
	}
    } 
    
    /**
     * 次空間を探索する
     * 	calculate next subspace and set the subspace to ray
     *	dInfo, spcPos は変更される
     *  skip 無し
     *
     * volume 部分と共有するための次空間探索 method
     * ddainfo を引数にすることで volume でも subspace でも OK!
     */
    private boolean moveRayToNextSubspaceNoSkip(DDAInfo dInfo, 
						Vector3 rayPos,// 視点
						Vector3 rayDir,//視線ベクトル
						IntVec3 spcPos)
    {
	/**
	 * 1.レイが次に行く次の空間を求める
	 * この関数は現在の空間位置と DDA の情報から新しい空間位置を決
	 * める
	 */
	if(dInfo.isDDAinited() == false)
	{
	    this.initializeDDA(dInfo, rayPos, rayDir, spcPos);
	}
	
	int nextDir = this.getDirByDDA(dInfo);
	
	// 2.その空間が定義空間内か調べる
	boolean isNextSpaceInDefspc = this.testAndGoIfItCan(spcPos, nextDir);
	return(isNextSpaceInDefspc);
    }

    /**-----------------------------------------------------------
     * public functions
     *------------------------------------------------------------
     * constructor
     */
    public SearchNextSpcDDA()
    {
	this.mDefSpcMin		= null;
	this.mCellLength	= -1.0;
	this.mSpcIdxMin		= null;
	this.mSpcIdxMax		= null;
	this.mRayPropSkipMethod	= ELSE_rayprop;
	this.mNetworkType	= ELSE_network;

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

    public Array3D peekObjInSpcMap()
    {
	return(this.mObjInSpcMap);
    }
    
    /**
     * initialize
     *	defSpcMin は DDA を適用する空間の最小位置であるが、これは 
     *	subspace の空間ではない。空間分割される前あるいは subvolume に
     *	分割される前の defspc min である。 
     *	ここを原点とし、minIdx, maxIdx の範囲で部分空間が存在する
     *	(誤って部分空間に分割した後の最小位置を入れるバグがなかなか直
     *	 らなかった。1998年4月 3日(金) 今日は多田研の花見であった。
     *	 3/30 に林原さんが結婚したらしい。)
     *
     * この有効範囲は [SpcIdxMin, SpcIdxMax) である．この interface
     * をどうするか
     * 	@param defSpcMin	DDA を適用する空間の最小位置，global な定義空間
     *  @param cellLength	DDAで traverse するセルの大きさ
     *  @param spcIdxMin	cell の最小index, 
     * 				subvolume のため 0 が最小ではない
     *  @param spcIdxMax, 	cell の最大index
     *  @param rayPropSkipMetod	skip 法
     *  @param networkType	ネットワーク形状
     */
    public void initWithoutObjInSpcMap(Vector3 	defSpcMin, 
				       double 	celllen, 
				       int [] 	spcIdxMin, 
				       int []	spcIdxMax,
				       int  	skipMethod,
				       int      systemNetworkType)
    {
	SearchNextSpcDDA.exitIfIllegalParam(celllen, spcIdxMin, spcIdxMax, 
					    skipMethod, systemNetworkType);
	this.mDefSpcMin	= defSpcMin; 	 
	this.mCellLength= celllen;
	this.mSpcIdxMin = new int[3];
	this.mSpcIdxMax	= new int[3];
	for(int i = 0; i < 3; i++)
	{
	    this.mSpcIdxMin[i] 	= spcIdxMin[i];
	    this.mSpcIdxMax[i]	= spcIdxMax[i];
	}
	this.mRayPropSkipMethod = skipMethod;
	this.mNetworkType 	= systemNetworkType;    

	// derived variables
	this.mCellHalf = this.mCellLength / 2.0;
    }

    /**
     * 空間中の物体の存在状況
     */
    public void initObjInSpcMap(Array3D objMap)
    {
	String s = "Error! : SearchNextSpcDDA.initObjInSpcMap : ";
	if((objMap == null) || (objMap.getTotalSize() <= 0))
	{
	    throw new RuntimeException(s + "illegal object in space map.");
	    // commMpi2_exit(1);		
	}
	this.mObjInSpcMap = objMap;
    }

    /**
     * 空間中の物体の存在状況の出力
     */
        /**
     * 空間情報の出力共通ルーチン
     */
    public static void printObjInSpcMapCommon(String head, Array3D spcmap)
    {
	int [] idx = new int[3];
	final int xmaxIdx = spcmap.getMaxIdx(0);
	final int ymaxIdx = spcmap.getMaxIdx(1);
	final int zmaxIdx = spcmap.getMaxIdx(2); 
	idx[0] = idx[1] = idx[2] = 0;
	
	ObjInASpc oias = (ObjInASpc)spcmap.get(idx);
	System.out.println(head + oias.getVisualOutDescription());
	for(idx[0] = 0; idx[0] <= xmaxIdx; idx[0]++)
	{
	    for(idx[1] = 0; idx[1] <= ymaxIdx; idx[1]++)
	    {
		System.out.print(head + " obj in subspace [" +
				 idx[0] + "][" + idx[1] + "]: ");
		for(idx[2] = 0; idx[2] <= zmaxIdx; idx[2]++)
		{
		    oias = (ObjInASpc)spcmap.get(idx);
		    System.out.print(oias.toVisualString());
		}
		System.out.println("");
	    }
	}
    }	

    /**
     * その空間位置に物体は存在するか
     */
    public boolean isObject(IntVec3 spcpos)
    {
	return(((ObjExistanceInASpc)(this.mObjInSpcMap.get(spcpos))).isObj());
    }
    
    /**
     * 3DDDAのための情報(DDAInfo)の初期化 (sub. of calcDDA)
     *	ray は define space 内にいることを確認してある
     *	rayDir はレイの進行方向ベクトル
     *	ray の持つ DDA info を更新する
     */
    public void initializeDDA(DDAInfo dInfo, 
			      Vector3 rayPos, Vector3 rayDir,
			      IntVec3 raySpcPos)
    {
	Assert.out(dInfo.isDDAinited() == false, 
		   "Error!: ddainfo is already initialized: initializeDDA");
	Assert.out(Math.abs((rayDir.getNorm()) - 1.0) < 
		   Mpi2Constants.D_ERROR_RES,
		   "Error!: rayDir is not normalized : initializeDDA");
    
	// access to member variable at here only
	double CellLen		= this.mCellLength;
	Assert.out(this.mCellLength > 0.0, "Error!: celllen must be > 0.0");
	Vector3 defSpcMin 	= this.mDefSpcMin;
    
	// 進行軸の決定と進行ベクトルのset
	this.setTraverseAxisNVector(dInfo, rayDir); 
	int shinkouJiku = dInfo.getTravAxis(); 
	
	int OtherAxis1 = (shinkouJiku + 1) % 3; // 進行軸以外軸 1
	int OtherAxis2 = (shinkouJiku + 2) % 3; // 進行軸以外軸 2
    
	// 進行軸方向の セルの最小値
	double DefSpcMinShinkouJiku =	defSpcMin.get(shinkouJiku);	
	// (0.5, 0.5, 0.5) セルの中心に位置するための偏差
	Vector3 center = new Vector3(0.5, 0.5, 0.5); 
	Vector3 spcpos = new Vector3(raySpcPos);
	
	// セルの３次元的中心
	// Vector3 centerOfCell = (defSpcMin + CellLen * (center + spcpos));
	center.plusHere(spcpos);
	center.mulScalerHere(CellLen);
	Vector3 centerOfCell = new Vector3(defSpcMin);
	centerOfCell.plusHere(center);
		
	/**-------------------------------------------------------------
	 * 相対位置 = レイの進行軸方向の逆のセル上の平面との交差点
	 * 交差点 = Define Space Min + Cell Length * spcpos
	 */
	double minAtCell = 0.0;
	if((rayDir.get(shinkouJiku)) > 0.0)	// 進行ベクトルは正
	{
	    minAtCell = 
		DefSpcMinShinkouJiku + spcpos.get(shinkouJiku) * CellLen;
	}else
	{				// 負の方向ではセルの大きい方から初める
	    minAtCell = DefSpcMinShinkouJiku 
		+ (spcpos.get(shinkouJiku) + 1.0) * CellLen;
	}
    
	/**
	 * 進行軸方向についての始点の位置から進行軸方向のベクトルに沿った 
	 * minAtCell までの距離 = param_t * 進行軸方向のベクトル成分
	 */
	double param_t = (minAtCell - rayPos.get(shinkouJiku)) 
	    / rayDir.get(shinkouJiku);

	/**
	 * この assertion は ray tracing の初期においては正しくない。とい
	 * うのは視点が定義空間の外にあることがあるからである。 
	 *     if(param_t > 0.0)
	 *     {
	 * 	System.out.println("initializeDDA param_t " + param_t);
	 * 	 // commMpi2_exit(1);
	 *     }
	 *     Assert.out(param_t < 1e-5, // (0 のことがあるので, 
	 * 	"Error!:")
	 */
	double relaPos1 
	    = (rayPos.get(OtherAxis1) + param_t * rayDir.get(OtherAxis1)) //rayDir
	    - centerOfCell.get(OtherAxis1);		    
    
	double relaPos2 
	    = (rayPos.get(OtherAxis2) + param_t * rayDir.get(OtherAxis2)) // rayDir
	    - centerOfCell.get(OtherAxis2);
	// 上で rayDir なのは rayDir が ray の進行方向の単位ベクトルから
	
	//----- 3DDDAの相対値初期値(進行軸方向は無意味)
	dInfo.changeRelativePos(relaPos1, OtherAxis1, relaPos2, OtherAxis2);
	
	/*----- 甘い assertionしかし、初期値は自空間内とは限らない(rayTr)
	 * その場合にはこの asserttion は誤りである。視点が空間の外にある
	 * 場合には相対位置は cell length の 1.5 倍以内にはおさまらない。
	 *----- 一番上部から45度ぎりぎりの場合には隣の端までなので、+-1.5倍
	 * Assert.out((relaPos1 <= (1.5 * CellLen)) && 
	 * ((-1.5 * CellLen) <= relaPos1), "Error!:", );
	 * Assert.out((relaPos2 <= (1.5 * CellLen)) && 
	 * ((-1.5 * CellLen) <= relaPos2), "Error!:");
	 */
	Assert.out((relaPos1 <= (1.5 * CellLen)) && 
		   ((-1.5 * CellLen) <= relaPos1),
		   "Error! : relapos1 (" + relaPos1 + "), \\pm 1.5x celllen = "
		   + (1.5 * CellLen));
	Assert.out((relaPos2 <= (1.5 * CellLen)) && 
		   ((-1.5 * CellLen) <= relaPos2),
		   "Error! : relapos2 (" + relaPos2 + "), \\pm 1.5x celllen = "
		   + (1.5 * CellLen));
	dInfo.setDDAset();		// 計算したことを記す
    }

    // 本来は subclass を作成するか，これを interface にするべきだが，
    // 今回は見送る
    // ray の位置を次空間に変更する : Skip 無し
    /**
     * 次空間を探索する
     * 	calculate next subspace and set the subspace to ray
     *	ray の 空間(volume でない方) の ddainfo, space pos は更新される
     * skip 無し
     */
    public boolean moveRayToNextSubspaceNoSkip(Ray ray)
    {
	DDAInfo ddainfo = ray.peekSpcDDAInfo();
	if(ddainfo == null)
	{
	    ddainfo = new DDAInfo();
	    ray.setSpcDDAInfo(ddainfo);
	}
	    
	IntVec3 spcpos  = ray.peekSpcPos();
	boolean isSendRay = 
	    this.moveRayToNextSubspaceNoSkip(ddainfo, 
					     ray.peekPos(), ray.peekDir(),
					     spcpos);
	ray.setSpcPos(spcpos);
	return(isSendRay);
    }    

    /**
     * 次空間を探索する
     * 	素朴な skip (もう ray.getSpcPos() に用はなく必ず隣へ移動して
     * 	みる)
     */
    public boolean moveRayWithSobokuSkip(Ray ray)
    {
	DDAInfo ddainfo = ray.peekSpcDDAInfo();
	IntVec3 spcpos  = ray.peekSpcPos();
	boolean isNextSpaceInDefspc = true;
	
	if(ddainfo == null)
	{
	    ddainfo = new DDAInfo();
	}
	if(ddainfo.isDDAinited() == false)
	{
	    this.initializeDDA(ddainfo, ray.peekPos(), ray.peekDir(), spcpos);
	}
	
	while(true)
	{
	    // 次の空間を探索する
	    int nextDir = this.getDirByDDA(ddainfo); 
	    
	    // 次の空間は定義空間内部か
	    isNextSpaceInDefspc = this.testAndGoIfItCan(spcpos, nextDir);
	    
	    if(isNextSpaceInDefspc == true)
	    {
		if(((ObjExistanceInASpc)(this.mObjInSpcMap.get(spcpos))).
		   isObj() == true)
		{
		    // 定義空間内 && 物体が存在する . skip できない
		    // isNextSpaceInDefspc == true で loop を抜ける
		    
		    ray.setSpcDDAInfo(ddainfo);
		    ray.setSpcPos(spcpos);
		    return(true);
		}
		else
		{
		    // 次空間は定義空間内 && 物体が存在しない . 素朴 Skip
		    // skip 回数を記録したい場合はここで記録する
		    //  		this.incSkipSpcNum();		
		    //  		this.incTravSpcNum();
		}
	    }else
	    {
		//何も存在しないまま定義空間を外れてしまった．行く場所はない
		// isNextSpaceInDefspc == false となって loop を抜ける
		break;
	    }
	}
	ray.setSpcDDAInfo(ddainfo);
	ray.setSpcPos(spcpos);
	return(isNextSpaceInDefspc);
    }
    
    /*
     *------------------------------------------------------------
     * volume 移動用
     *	隣接空間との整合をとりつつ移動するため、volume 中を移動する場
     *      合には境界において ray が正しい voxel にいない場合がある。
     *------------------------------------------------------------
     * ある部分空間がこの Direction Decision Unit で有効とされているか
     boolean SearchNextSpcDDA.
     isValidASpcpos(IntVec3 spcpos)
     {
     if((this.mSpcIdxMin[0]    <= spcpos.get(IntVec3.X)) && 
     (this.mSpcIdxMin[1]    <= spcpos.get(IntVec3.Y)) &&
     (this.mSpcIdxMin[2]    <= spcpos.get(IntVec3.Z)) &&
     (spcpos.get(IntVec3.X) < this.mSpcIdxMax[0]) && 
     (spcpos.get(IntVec3.Y) < this.mSpcIdxMax[1]) &&
     (spcpos.get(IntVec3.Z) < this.mSpcIdxMax[2]))
     {
     return(true);
     }
     else
     {
     return(false);	
     }
     }
     */

    /**
     * forceMoveRayToNextSpace
     * 	次空間を探索し必ず次の空間に移動してしまう。
     */
    void forceMoveRayToNextSpace(DDAInfo dInfo, 
				 Vector3 rayPos, Vector3 rayDir,
				 IntVec3 spcPos)
    {
	if(dInfo.isDDAinited() == false)
	{
	    this.initializeDDA(dInfo, rayPos, rayDir, spcPos);
	}
	int nextDir = this.getDirByDDA(dInfo);   
	this.goNextSpace(spcPos, nextDir);
    }


    /**
     * for test 
     */
    public static void main(String [] args)
    {
	SearchNextSpcDDA nssDDA = new SearchNextSpcDDA();
	
	int min[] = {  0,  0,  0, };
	int max[] = { 10, 10, 10, };
	nssDDA.initWithoutObjInSpcMap(new Vector3(0.0, 0.0, 0.0),
				      2.0, // celllen
				      min, max, 
				      NoSkip_rayprop,
				      MultiStage_network);
    }
}
