/*
 * $Id: Ray.java,v 1.12.4.1 2000/06/15 20:41:23 yamauchi Exp $
 *     (M\pi)^2 : Ray
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source ray.h, ray.cc 
 * 1999年7月30日(金)
 */

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

import  java.io.Serializable;
import  java.lang.String;
import  jp.ac.tohoku.is.archi.mpi2.dda.DDAInfo;
import  jp.ac.tohoku.is.archi.mpi2.obj.ObjectPrimitive;
import  jp.ac.tohoku.is.archi.mpi2.sysparam.Mpi2Enum; 
import  jp.ac.tohoku.is.archi.mpi2.sysparam.Mpi2Option;
import  jp.ac.tohoku.is.archi.mpi2.util.*;

public final class Ray 
implements Encodable		// Serializable
{
    /**
     * Ray ID のソース
     *   この ID は並列計算する場合には PE の数だけ同じ ID がある
     *   システム唯一とはならない
     */
    // TRACE_RAY
    private static int mRAYIDSRC = 0;
    // TRACE_RAY
    

    /**
     * Ray ID : DEBUG 用．通常は消しておく
     */
    // TRACE_RAY
    private int     mRAYID;
    // TRACE_RAY
    
    /**
     * ray そのものの情報
     */
    private Vector3 mStartPos;	// ray 出発点
    private Vector3 mDir;	// ray 方向ベクトル
    
    private ObjectPrimitive mRadObjRef;	// ray 発生物体 (他空間では NULL)
    private Vector3 mHitPos;	// 物体との交差点
    private IntVec3 mSpcPos;	// 現在存在する空間 index

    private DDAInfo mSpcDInfo;	// 空間 traverse のための DDA 情報を保持する

    // private volTravInfo mVTinfo;// volume traverse のための情報を保持する

    // ray の輝度 (radiosity stage   : delta-radiosity,
    //		   ray-tracing stage : color)
    private Vector3 mColor;	

    // ray tracing ray
    private Vector3 mRefCoeff;	// ray の減衰係数 
    private int     mScreenX, mScreenY; // Display Screen 座標
    private int	    mReflectedNum; 	// ray 反射回数
    private boolean mIsInSideObject;	// 透過物体の内部にいるか外部にいるか

    // 反射 delta-radiosity ray 作成用
    void initForReflectionRadiosity(Ray pOldRay, 
				    Vector3 reflectedRayDir,
				    Vector3 newDeltaRadiosity,
				    ObjectPrimitive pHitObj)
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN: initForReflectionRadiosity");
    }
    
    // 反射 ray-tracing ray 作成用
    void initForReflectionRayTracing(Ray pOldRay, 
				     Vector3 reflectedRayDir,
				     Vector3 reflectedRayCoef,
				     ObjectPrimitive pHitObj)
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN: initForReflectionRadiosity");
    }

    /**
     * default constructor
     */
    private Ray()
    {
	// TRACE_RAY
	this.mRAYID = mRAYIDSRC;	
	mRAYIDSRC++;
	// TRACE_RAY

	this.mStartPos	= null;
	this.mDir	= null;
    
	this.mRadObjRef	= null;
	this.mHitPos	= null;
	this.mSpcPos	= null;

	this.mSpcDInfo	= null;

	this.mColor	= null;

	this.mRefCoeff	= null;
	this.mScreenX	= -1;
	this.mScreenY	= -2;
	this.mReflectedNum	= 0;

	this.mIsInSideObject	= false;
	// throw new RuntimeException("Ray can not construct ray()");
    }
    
    /**
     * constructor for ray tracing ray
     */ 
    public static Ray newForRayTrace(int screenX, int screenY,
				     Vector3 viewPoint, 
				     Vector3 destVecFromVp, 
				     IntVec3 spcpos,    
				     double   distRaysFactor)
    {
	Assert.out(((screenX >= 0) && (screenY >= 0)),
		   "Error! :newForRayTrace: illegal screen X, Y");

	Ray r = new Ray();

	r.mStartPos	= new Vector3(viewPoint);
	r.mDir		= new Vector3(destVecFromVp);

	// r.mDir must be already normalized
	Assert.out(Math.abs(r.mDir.getNorm() - 1.0) < 1e-5, 
		   "ray.mDir must be normalized : newForRayTrace");

	r.mRadObjRef	= null;
	r.mHitPos	= new Vector3(Vector3.PLUS_INFTY);
	r.mSpcPos	= new IntVec3(spcpos);

	r.mSpcDInfo	= null;

	r.mColor	= new Vector3(Vector3.ZERO);

	// ray-tracing では減衰率は 1 に初期化されるが、 distributed
	// ray-tracing のため、各 ray の寄与は 1 より小さくなる。その 
	// distributed のための重みをかけて初期化する
	Assert.out(((0.0 < distRaysFactor) && (distRaysFactor <= 1.0)),
		   "Error! : newForRayTrace distRaysFactor " + 
		   "must be (0.0, 1.0]");
	
	r.mRefCoeff	= 
	    new Vector3(distRaysFactor, distRaysFactor, distRaysFactor);;
	r.mScreenX	= screenX;
	r.mScreenY	= screenY;
	r.mReflectedNum	= 0;	// 作成されたばかり

	// this.mVTinfo.invalidateInit();

	r.mIsInSideObject = false; // 透過物体の外部にいるとして初期化
	
	return(r);
    }


    /**
     * init fot radiosity rays
     */
    public static Ray newForRadiosity(Vector3 viewPoint,  
				      Vector3 destVecFromVp, 
				      Vector3 initRadioVal,   
				      ObjectPrimitive rayGenObjectRef, 
				      IntVec3 spcpos)
    {
	Ray r = new Ray();

	r.mStartPos	= viewPoint;
	r.mDir		= destVecFromVp;
	// r.mDir must be already normalized
	Assert.out(Math.abs(r.mDir.getNorm() - 1.0) < 1e-5,
		   "ray.mDir must be normalized: newForRadiosity");

	r.mRadObjRef	= rayGenObjectRef;
	r.mHitPos	= new Vector3(Vector3.PLUS_INFTY);
	r.mSpcPos	= spcpos;

	r.mSpcDInfo	= null;

	r.mColor	= initRadioVal;

	// 反射する可能性あり:減衰率は1に初期化
	r.mRefCoeff	= new Vector3(1.0, 1.0, 1.0);
	r.mScreenX	= -2;	// screen は存在しない
	r.mScreenY	= -2;
	r.mReflectedNum	= 0;	// 作成されたばかり

	// this.mVTinfo.invalidateInit();

	r.mIsInSideObject = false; // 透過物体の外部にいるとして初期化

	return(r);
    }

    /**
     * 反射レイ作成用 Ray Tracing 法
     *   @param oldRay 		  新しい ray を作成する物体と反射した元の ray 
     *   @param reflectedRayDir   反射方向
     *   @param reflectedRayCoef  減衰係数の累積(累積乗算)
     *   @param hitObj 	  	  交差した物体への参照(同じ object 
     *   			  に 2 重の交差判定を行わないように)
     */
    public static Ray newForReflectionRayTracing(Ray 	 oldRay, 
						 Vector3 reflectedRayDir,
						 Vector3 reflectedRayCoef,
						 ObjectPrimitive hitObj)
    {
	Ray r = new Ray();
	
	r.mStartPos = new Vector3(oldRay.peekHitPos()); // 反射レイ視点は交差点
	r.mDir	    = reflectedRayDir;	// 方向ベクトルは反射・透過方向

	r.mRadObjRef	= hitObj;

	// 反射したので交差物体はあったはず : ray 発生物体は旧交差物体
	Assert.out(hitObj != null, "Error! : newForReflectionRayTracing : "
		   + "hitObj must be != null");


	// 物体との交差点はまだない
	r.mHitPos	= new Vector3(Vector3.PLUS_INFTY);

	// 現在存在する空間indexは同じ
	r.mSpcPos	= new IntVec3(oldRay.peekSpcPos()); 

	// 3DDDA のための情報は全て計算しなおす必要あり
	r.mSpcDInfo = null;
	// r.mVTinfo.invalidateInit();

	r.mColor   = new Vector3(0.0, 0.0, 0.0); // IC で輝度計算,セットは後
	r.mRefCoeff = reflectedRayCoef;	// ray の減衰係数は更新

 	// Display Screen座標も旧と同じ
	r.mScreenX	= oldRay.getScreenX();
	r.mScreenY	= oldRay.getScreenY();

	// ray 反射回数を増加
	r.mReflectedNum	= (oldRay.getReflectedNum() + 1); 
	
	// r.setNextID();
	// r.mOrgPEID	= oldRay.mOrgPEID;  // 発信 PE も旧と同じ
	// cout << "#R n4 " << r.mID << endl;
	// #ifdef COUNT_SKIP_SPACE
	//     r.mNumOfSkipSpaces 	= 0;
	//     r.mNumOfTraverseSpaces 	= 0;
	// #endif

	r.mIsInSideObject = oldRay.isInSideObject();

	return(r);
    }			

    /**
     * 2 次レイの初期化関数 : Raldiosity 法
     * 
     *   @param oldRay 		  新しい ray を作成する物体と反射した元の ray 
     *   @param reflectedRayDir   反射方向
     *   @param newDeltaRadiosity 反射して減衰した radiosity 値
     *   @param hitobj 	  	  交差した物体への参照(同じ object 
     *   			  に 2 重の交差判定を行わないように)
     */
    public static Ray newForReflectionRadiosity(Ray oldRay, 
						Vector3 reflectedRayDir,
						Vector3 newDeltaRadiosity,
						ObjectPrimitive  hitObj)
    {
	Ray r = new Ray();

	// 反射 ray の視点は交差点
	r.mStartPos	= new Vector3(oldRay.peekHitPos()); 

	// 方向ベクトルは反射・透過方向
	r.mDir 		= new Vector3(reflectedRayDir);

	// これは 2 次レイなので交差物体が存在する
	Assert.out((hitObj != null), "Error! : newForReflectionRadiosity, " +
		   "hitObj must be != null");
	r.mRadObjRef	= hitObj;
	r.mHitPos 	= new Vector3(Vector3.PLUS_INFTY); 
	// 物体との交差点はまだない

	// 現在存在する空間 index は同じ
	r.mSpcPos	= new IntVec3(oldRay.mSpcPos); 

	// 3DDDA のための情報は全て計算しなおす必要あり
	r.mSpcDInfo	= null;
	// r.mVTinfo.invalidateInit();

	// 新 delta-radiosity 値
	r.mColor        = new Vector3(newDeltaRadiosity);

	r.mRefCoeff	= new Vector3(); // 減衰係数はradiosityでは無関係
	r.mScreenX  	= -3;	// Display Screen座標は無関係
	r.mScreenY	= -3;

	// ray 反射回数は PEClass::calcNsetIntensityRadiosity にて既に増加した
	// FIXHERE newForReflectionRayTracing と対称性がない
	r.mReflectedNum	= oldRay.mReflectedNum;	

	// r.setNextID();
	// r.mOrgPEID	= oldRay.mOrgPEID;  // 発信 PE も旧と同じ
	// cout << "#R n4 " << r.mID << endl;
	// #ifdef COUNT_SKIP_SPACE
	//     r.mNumOfSkipSpaces 	= 0;
	//     r.mNumOfTraverseSpaces 	= 0;
	// #endif
	
	r.mIsInSideObject = oldRay.isInSideObject();

	return(r);
    }
    
    /**
     * copy contructor
     */
    public Ray(Ray rhs)
    {
	// TRACE_RAY
	this.mRAYID = rhs.mRAYID;	
	// TRACE_RAY

	this.mStartPos	= new Vector3();
	this.mDir	= new Vector3();
    
	this.mRadObjRef	= rhs.mRadObjRef;
	this.mHitPos	= new Vector3(rhs.mHitPos);
	this.mSpcPos	= new IntVec3(rhs.mSpcPos);	

	this.mSpcDInfo	= new DDAInfo(rhs.mSpcDInfo);
	// private volTravInfo mVTinfo;// volume traverse のための情報を保持

	this.mColor	= new Vector3(rhs.mColor);	
	
	this.mRefCoeff	= new Vector3(rhs.mRefCoeff);
	this.mScreenX	= rhs.mScreenX;
	this.mScreenY 	= rhs.mScreenY;

	this.mReflectedNum	= rhs.mReflectedNum;
	this.mIsInSideObject	= rhs.mIsInSideObject;
    }

    /**
     * private deserialize constructor
     */
    private Ray(Vector3 startpos, Vector3 dir, Vector3 hitpos, IntVec3 spcpos,
		DDAInfo spcdinfo, Vector3 color, Vector3 rayrefcoeff,
		int screenX, int screenY, int reflectnum, boolean isinsideobj)
    {
	// TRACE_RAY
	this.mRAYID = mRAYIDSRC;	
	mRAYIDSRC++;
	// TRACE_RAY

	this.mStartPos	= startpos;
	this.mDir	= dir;
    
	// this.mRadObjRef = 
	this.mHitPos	= hitpos;
	this.mSpcPos	= spcpos;

	this.mSpcDInfo	= spcdinfo;
	// private volTravInfo mVTinfo;// volume traverse のための情報を保持

	this.mColor	= color;
	
	this.mRefCoeff	= rayrefcoeff;
	this.mScreenX	= screenX;
	this.mScreenY 	= screenY;

	this.mReflectedNum	= reflectnum;
	this.mIsInSideObject	= isinsideobj;
    }

    public void setPos(Vector3 aStartPos) 
    {
	this.mStartPos = aStartPos; 
    }
    public Vector3 peekPos() 		  
    {
	return(this.mStartPos);	
    }

    public void setDir(Vector3 aDir)   
    {
	this.mDir = aDir;		
    }
    public Vector3 peekDir() 	   	  
    {
	return(this.mDir);		
    }

    public void setColor(Vector3 aCol) 
    {
	this.mColor = aCol;		
    }
    public Vector3 peekColor() 	   	  
    {
	return(this.mColor);	
    }

    public void setRefCoeff(Vector3 aRef)
    {
	this.mRefCoeff = aRef;	
    }
    public Vector3 peekRefCoeff() 	  
    {
	return(this.mRefCoeff);	
    }

    public void setRadObjRef(ObjectPrimitive obj)
    {
	this.mRadObjRef = obj;
    }
    public ObjectPrimitive peekRadObjRef()
    {
	return(this.mRadObjRef);	
    }

    public void setHitPos(Vector3 aHPos)
    {
	this.mHitPos = aHPos;	
    }
    public Vector3 peekHitPos() 	  
    {
	return(this.mHitPos);	
    }

    public void setSpcPos(IntVec3 aSpc)
    {
	this.mSpcPos  = aSpc;	
    }    
    public IntVec3 peekSpcPos()    	  
    {
	return(this.mSpcPos);    	
    }    

    public int getScreenX() 	  
    { 
	return(this.mScreenX); 
    }
    public int getScreenY() 	  
    {
	return(this.mScreenY); 	
    }

    public int getReflectedNum() 	  
    {
	return(this.mReflectedNum); 
    }

    // public void    incReflectedNum() 	  
    // { 
    //     this.mReflectedNum++;	
    // }
    
    // DDA 関係 
    //----- 空間 traverse 用 DDA
    public void    setSpcDDAInfo(DDAInfo dinfo) {this.mSpcDInfo = dinfo; }
    public DDAInfo peekSpcDDAInfo()  { return(this.mSpcDInfo);	 }
//     public void   setSpcDinfoIllegalDir()	{ 
// 	this.mSpcDInfo.setGoingDir(DIR_ILLEGAL); 
//     }

    //-----  voxel traverse 用 DDA
//     public void    invalidateVolData() { this.mVTinfo.invalidateInit();	 }
//     // volume DDA
//     public YorN   isVolDDAinited() 	
//     {
// 	// improve いちいちコンストラクトするので効率が悪い
// 	return((this.mVTinfo.getVolDDA()).isDDAinited());
//     }
//     public void   setVolDDAInfo(DDAInfoClass& dinfo) 
//     {
// 	this.mVTinfo.setVolDDA(dinfo);
//     }
//     public DDAInfo getVolDDAInfo() {return(this.mVTinfo.getVolDDA());}
//     //    volume ID
//     public void   setVolID(int vid) 		{ this.mVTinfo.setVolID(vid);	}
//     public int    getVolID() 		{ return(this.mVTinfo.getVolID()); }
//     //    volume index
//     public void    setVoxelIdx(IntVec3& voxelIdx) 
//     {
// 	this.mVTinfo.setVolIdx(voxelIdx);
//     }
//     public IntVec3 getVoxelIdx() { return(this.mVTinfo.getVolIdx()); }
//     public void    setAccumOpacity(double accumOpacity)
//     {
// 	this.mVTinfo.setAccumOpacity(accumOpacity);
//     }
//     public double getAccumOpacity(){return(this.mVTinfo.getAccumOpacity());}
    
    //----- その他
    public String toString() 
    {
	return(this.toStringWithHead(""));
    }
    public String toStringWithHead(String head) 
    {
	String s = 
	    head + "----- Ray -----\n" +
	    head + " startpos " + this.mStartPos + ",  dir " + this.mDir+ "\n"+
	    head + " rayorg obj: " + 
	    ((this.mRadObjRef != null) ? "There is, " : "NO, ") + 
	    head + " hitpos " + this.mHitPos + 
	    head + " ray in spcpos " + this.mSpcPos + "\n" +
	    head + " color " + this.mColor + "\n"   +
	    head + " reflectance coefficient " + this.mRefCoeff + "\n" +
	    head + " screen pos " + this.mScreenX + ", " + this.mScreenY +"\n"+
	    head + " refrected num " + this.mReflectedNum + " " + 
	    head + ((this.mIsInSideObject == true) ? "in":"out") + " of obj";
	// TRACE_RAY
	s += head + " ray ID = " + this.getID();
	// TRACE_RAY
	return(s);
    }

    public boolean isOK() 
    {
	return((this.mSpcPos.get(IntVec3.X) >= 0) ? true : false);
    }

    /**
     * 物体内か外かの変更 Flip-Flop
     */
    public void changeInOut() 
    {	
	this.mIsInSideObject = (this.mIsInSideObject == true) ? false : true;
    }

    /**
     * 物体内か
     */
    public boolean isInSideObject()
    {
	return(this.mIsInSideObject);
    }

    /*
     * 通信用のタグを返す
     */
    public static int getTAG() 
    { 
	return(Mpi2Enum.TAG_RAY);	
    }

    /**
     * equals
     */
    public boolean equals(Object obj)
    {
	if(!(obj instanceof Ray))
	{
	    return(false);
	}
	Ray rhs = (Ray)obj;
	
	if((this.mStartPos.equals(rhs.mStartPos)) 	&&
	   (this.mDir.equals(rhs.mDir))			&&
	   //private ObjectPrimitive mRadObjRef
	   (this.mHitPos.equals(rhs.mHitPos))		&&
	   (this.mSpcPos.equals(rhs.mSpcPos))		&&
	   (this.mSpcDInfo.equals(rhs.mSpcDInfo))	&&
	   (this.mColor.equals(rhs.mColor))		&&
	   (this.mRefCoeff.equals(rhs.mRefCoeff))	&&
	   (this.mScreenX        == rhs.mScreenX)	&&
	   (this.mScreenY        == rhs.mScreenY)	&&
	   (this.mReflectedNum   == rhs.mReflectedNum)	&&
	   (this.mIsInSideObject == rhs.mIsInSideObject))
	{
	    return(true);
	}else
	{
	    return(false);
	}
    }

    /**------------------------------------------------------------
     * Encodable
     *------------------------------------------------------------*/
    private final static int mSizeInDouble = 
        + Vector3.ZERO.sizeInDouble() 	// mStartPos
	+ Vector3.ZERO.sizeInDouble() 	// mDir
	// this.mRadObjRef	// ray 発生物体 (他空間では NULL)
	+ Vector3.ZERO.sizeInDouble()	// mHitPos
	+ IntVec3.ZERO.sizeInDouble() 	// mSpcPos
	+ (new DDAInfo()).sizeInDouble() // DDAInfo
	+ Vector3.ZERO.sizeInDouble() 	// mColor
	+ Vector3.ZERO.sizeInDouble()	// mRefCoeff
	+ 1				// mScreenX
    	+ 1 				// mScreenY
    	+ 1				// mReflectedNum
    	+ 1;				// mIsInSideObject

    public int sizeInDouble()
    {
	return(mSizeInDouble);
    }
    
    public void encodeInDouble(double[] array, int offset)
    {
	int idx = 0;
	Assert.out(this.mStartPos != null, "Error : encode null mStartPos");
	this.mStartPos.encodeInDouble(array, offset + idx);
	idx += this.mStartPos.sizeInDouble();

	Assert.out(this.mDir != null, "Error : encode null mDir");
	this.mDir.encodeInDouble(array, offset + idx);
	idx += this.mDir.sizeInDouble();

	// this.mRadObjRef;	// ray 発生物体 (他空間では NULL)

	Assert.out(this.mHitPos != null, "Error : encode null mHitPos");
	this.mHitPos.encodeInDouble(array, offset + idx);
	idx += this.mHitPos.sizeInDouble();

	Assert.out(this.mSpcPos != null, "Error : encode null mSpcPos");
	this.mSpcPos.encodeInDouble(array, offset + idx);
	idx += this.mSpcPos.sizeInDouble();

	Assert.out(this.mSpcDInfo != null, "Error : encode null mSpcDInfo");
	this.mSpcDInfo.encodeInDouble(array, offset + idx);
	idx += this.mSpcDInfo.sizeInDouble();

	Assert.out(this.mColor != null, "Error : encode null mColor");
	this.mColor.encodeInDouble(array, offset + idx);
	idx += this.mColor.sizeInDouble();

	Assert.out(this.mRefCoeff != null, "Error : encode null mRefCoeff");
	this.mRefCoeff.encodeInDouble(array, offset + idx);
	idx += this.mRefCoeff.sizeInDouble();

	array[offset + idx] = (double)(this.mScreenX);
	idx += 1; 

	array[offset + idx] = (double)(this.mScreenY);
	idx += 1; 

	array[offset + idx] = (double)(this.mReflectedNum);
	idx += 1; 
	array[offset + idx] = (double)((this.mIsInSideObject == true) ? 1 : 0);
	idx += 1;
    }

    
    /*
     * Implements ObjectDecoder
     */
    private static class RayObjectDecoder implements ObjectDecoder 
    {
    	public int sizeInDouble() 
	{
	    return(mSizeInDouble);
	}
    	public Object decodeFromDouble(double[] array, int offset)
	{
	    int idx = 0;
	    ObjectDecoder vec3  = Vector3.getObjectDecoder();
	    ObjectDecoder ivec3 = IntVec3.getObjectDecoder();

	    Vector3 startpos = 
		(Vector3)(vec3.decodeFromDouble(array, offset + idx));
	    idx  += startpos.sizeInDouble();
	    
	    Vector3 dir	     = 
		(Vector3)(vec3.decodeFromDouble(array, offset + idx));
	    idx  += dir.sizeInDouble();

	    Vector3 hitpos   = 
		(Vector3)(vec3.decodeFromDouble(array, offset + idx));
	    idx  += hitpos.sizeInDouble();
				
	    IntVec3 spcpos   = 
		(IntVec3)(ivec3.decodeFromDouble(array, offset + idx));
	    idx  += spcpos.sizeInDouble();
				
	    DDAInfo spcdinfo = (DDAInfo)
		(DDAInfo.getObjectDecoder().decodeFromDouble(array, 
							     offset + idx));
	    idx  += spcdinfo.sizeInDouble();

	    Vector3 color    = 
		(Vector3)(vec3.decodeFromDouble(array, offset + idx));
	    idx  += color.sizeInDouble();

	    Vector3 rayrefcoeff = 
		(Vector3)(vec3.decodeFromDouble(array, offset + idx));
	    idx  += rayrefcoeff.sizeInDouble();	    

	    int screenX      = (int)(array[offset + idx]);
	    idx += 1;

	    int screenY      = (int)(array[offset + idx]);
	    idx += 1;
	    
	    int reflectnum   = (int)(array[offset + idx]);
	    idx += 1;

	    boolean isinsideobj = (array[offset + idx] == 1.0) ? true : false;
	    // idx += 1;	    

	    return(new Ray(startpos, dir, hitpos, spcpos,
			   spcdinfo, color, rayrefcoeff,
			   screenX,  screenY, reflectnum, isinsideobj));
	}
    }
    private final static ObjectDecoder mODInstance = new RayObjectDecoder();
    public  static ObjectDecoder getObjectDecoder() 
    {
	return(mODInstance);
    }


    /**
     * serialize test
     */
    public static void main(String [] args)
    {
	Ray [] r = new Ray[16];
	for(int i = 0; i < r.length; i++)
	{
	    r[i] = Ray.newForRayTrace(1, 2, 
				      new Vector3(0.0, 1.0, 2.0),
				      new Vector3(1.0, 0.0, 0.0),
				      new IntVec3(2, 3, 5),
				      0.2);
	    DDAInfo da = new DDAInfo();
	    r[i].setSpcDDAInfo(da);
	}

	EncodedData encodedbuf = Marshal.createEncodedData(r);
	Encoder encoder = encodedbuf.getEncoder();

	EncodedData recvbuf = 
	    Marshal.createEncodedData(Ray.getObjectDecoder(), 
				      r.length);

	double [] src   = (double [])encodedbuf.getArray();
	double [] dest  = (double [])recvbuf.getArray();
	for(int i = 0; i < dest.length; i++)
	{
	    dest[i] = src[i];
	}
	
	Decoder dec = recvbuf.getDecoder();

	for(int i = 0; i < r.length; i++)
	{
	    Ray recvRay = (Ray)dec.decode(Ray.getObjectDecoder());
	    if(recvRay.equals(r[i]))
	    {
		System.out.println("[" + i + "] is OK.");
	    }
	}
    }
    
    // TRACE_RAY
    public int getID()
    {
	return(this.mRAYID);
    }
    // TRACE_RAY
}
