/*
 * $Id: RayTrFRGUnit.java,v 1.5.4.1 2000/06/15 20:41:23 yamauchi Exp $
 *     (M\pi)^2 : RayTrFRGUnit (RayTracing : First Rays Generation Unit)
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *
 *	calculation of First Ray Generation unit for ray tracing 
 *	ray-tracing 法において ray を一気に発生させるのではなく、
 *	いくつか発生させてから計算・通信を行うためどこまで ray を発生
 *	したかなどを保持する必要がある。そのためのクラスである。 
 *	
 * original source rayTrFRGUnit.h 
 *
 *	初期レイ発生 : FRGUnit for RayTracing の実装
 * 
 * 1999年8月 6日(金)
 */

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


import  java.lang.Math;

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

/**
 * First Ray Generation Unit : for raytracing method
 */
public final class RayTrFRGUnit implements Mpi2Enum, Mpi2Constants {
    private int      mScanMethodOnScreen; // first ray generation method
    
    private IntVec3  mPEid;
    private Vector3  mViewPoint;		
    
    private PixelPointer mPixelPointer;  // screen 上の pixel 位置発生器

    private Vector3 mScreenLeftUpFromVp; // 画面左上方を指す vector
    private Vector3 mDeltaXscr;		 // screenにおける1 pixel分のベクトル X
    private Vector3 mDeltaYscr;		 // 			              Y
    private int     mMaxRaysInAPixel;	 // 1 pixel に発っする ray の本数
    private int     mGenRayParPixel;	 // 現在のpixelに対し何本rayを発したか
    private boolean mCanGenerateNextRay; // 次の ray は発生可能か
 
    // [空間情報]
    //     private Vector3 mDefSpcMin;		// [空間情報] 定義空間
    //     private Vector3 mDefSpcMax;
    //     private Vector3 mPartVolMinPos;	// Partition volume の最小値
    private double  	mSubDivNum;			// 分割空間数
    private SpaceParam 	mSpcParamRef;
    private double     	mPartVolOneSize;	// 副空間の一辺の長さ
      
    // 
    private IntVec3 mVpSpcPos;		// 視点の存在空間位置(視点が定義空間内)
    private boolean mIsVpInDefSpc;	// 視点が定義空間内部にあるか
    private int     mNumOfTotalGenRays;	// 現在までの総 ray 発生数
    private double  mDistRaysFactor;   	// 各rayの1つの画素への寄与の割合 分散 
    // 				 	// raytrace が 4本/pixel ならば 1/4 

    // screen 位置
    private int	    mScrX;
    private int	    mScrY;

    private static double getDRandom()
    {
	return(Math.random());
    }
    
    private static void initRandom()
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN : initRandom");
    }

    /**
     * screen pointer(分散レイなどをどのように発生するかの Strategy) 
     * の初期化とそれに伴う member の初期化．たとえば，mVpSpcPos など
     * は視点が定義空間の内部か外部かを示すがこれは初期光分散を行うか
     * どうかに深くかかわっているのでここで初期化する．
     */
    private void initForScreenPointer(int scrSizeX, int scrSizeY,  
				      int myRank,   int totalPENum)
    {
	// 視点の位置が定義空間の内部か外部かを判断する
	if(BoundTester.isPosInBox(this.mViewPoint, 
				  this.mSpcParamRef.peekDefSpaceMin(), 
				  this.mSpcParamRef.peekDefSpaceMax()))
	{
	    // 視点は定義空間内部に存在する: 視点の存在する副空間位置を知る
	    this.mIsVpInDefSpc	= true;
	    this.mVpSpcPos 	= this.getNewSpcposOfaPoint(this.mViewPoint);
	}else
	{
	    Assert.out(this.mIsVpInDefSpc == false, 
		       "Error!: initForScreenPointer");
	}
	
	// どの初期光発生 method を使うかにより screen pointer を初期化
	switch(this.mScanMethodOnScreen)
	{
	case AllScan_frgm:		// 全体のスキャン (初期光分散はない)
	    this.initForAllScanMethod(scrSizeX, scrSizeY);
	    break;
	    
	case ScanlineScan_frgm:	// スキャンラインごとに分散する
	    this.initForScanlineScanMethod(scrSizeX, scrSizeY, 
					   myRank, totalPENum);
	    break;
	case DistributedScan_frgm:	// distributed 型の分散 
	    this.initForDistributedScanMethod(scrSizeX, scrSizeY,
					      myRank, totalPENum);
	    break;
	default:
	    throw new RuntimeException("Internal Error! : illegal ray " + 
				       "generation method in ray-tracing.");
	    // commMpi2_exit(1);
	}
    }

    /**
     * AllScan にて初期化する
     */
    private void initForAllScanMethod(int scrSizeX, int scrSizeY)
    {
	Assert.out(this.mScanMethodOnScreen == AllScan_frgm, 
		   "Error!: initForAllScanMethod");
	this.mPixelPointer = new PixelPtrAllScan(scrSizeX, scrSizeY);
	
	// AllScan の場合には視点位置により発生する ray を担当する PE が変
	// 化する． 
	if(this.mIsVpInDefSpc == true)
	{
	    Assert.out(BoundTester.
		       isPosInBox(this.mViewPoint, 
				  this.mSpcParamRef.peekDefSpaceMin(),
				  this.mSpcParamRef.peekDefSpaceMax()) == true,
		       "Error!: initForAllScanMethod this.mIsVpInDefSpc");
	    // この PE は視点の存在する副空間を担当しているか? 
	    if(PE.getInstance().isDealSpace(this.mVpSpcPos) == false)
	    {
		// 視点が定義空間内にあるが、視点の存在する部分空間をこの 
		// PE が担当していないので ray を発生させる必要がない 
		this.mCanGenerateNextRay = false; // ray 発生を終了とする
		if(Mpi2Option.getInstance().getDebugMode() == true)
		{
		    String s = Mpi2Option.toHead(DEBUGHEAD, this.mPEid) +
			"generate 0 rays\n" +
			"Because view point is in a define space and this PE "+
			"does not deal subspace within a view point";
		    System.out.println(s);
		}	    
	    }else
	    {
		if(Mpi2Option.getInstance().getDebugMode() == true)
		{
		    String s = Mpi2Option.toHead(DEBUGHEAD, this.mPEid) +
			"AllScan : view point is in defined space " + 
			"and this PE generate rays.";
		    System.out.println(s);			
		}
	    }
	}else
	{
	    Assert.out(BoundTester.
		       isPosInBox(this.mViewPoint, 
				  this.mSpcParamRef.peekDefSpaceMin(),
				  this.mSpcParamRef.peekDefSpaceMax())
		       == false,
		       "Error! : initForAllScanMethod");
	}
    }

    /**
     * ScanlineScan にて初期化する
     */
    private void initForScanlineScanMethod(int scrSizeX, int scrSizeY,  
					   int myRank,   int totalPEnum)
    {
        // myRank は PE 0 から PE n-1 となっているはず
	Assert.out(myRank < totalPEnum, "Error! : initForScanlineScanMethod");
	Assert.out(myRank >= 0, 	"Error! : initForScanlineScanMethod");
	this.mPixelPointer = new PixelPtrScanlineScan(scrSizeX, scrSizeY, 
						      totalPEnum, myRank); 
	System.out.println("DEBUG: GenScreen ScanLine Pointer : myRank is " +
			   myRank + ", skip = " + totalPEnum);
	
    }

    private void initForDistributedScanMethod(int scrSizeX, int scrSizeY,
					      int myRank,   int totalPEnum)
    {
	// NIN
	Mpi2Option.getInstance().verboseOut("NIN");
	System.exit(1);
    }
    
    /** 
     * スクリーン上の pixel 単位の移動．次の pixel へ移動する
     * pixel 内での指定数の ray を発生したら次の pixel へ移動する
     * まだ ray が発生できるのならばそのまま
     */
    private void moveOnScreen()
    {
	this.mGenRayParPixel++;
	// 1 pixel における発生数を越えたか? loop の基準はこれ
	if(this.mGenRayParPixel >= this.mMaxRaysInAPixel)
	{
	    this.mGenRayParPixel = 0;
	    // this.mPixelPointer.next によって移動する．
	    // これ以上移動できない場合にはそれを記す
	    this.mCanGenerateNextRay = this.mPixelPointer.next();
	    this.mScrX = this.mPixelPointer.getPixelPosX();
	    this.mScrY = this.mPixelPointer.getPixelPosY();
	}else
	{
	    // まだこの pixel で ray が発生可能
	}	
    }
    
    /**
     * ある点, point の存在する分割空間の指標を点の位置から得る
     *
     * cp = cell periad, p = point, min, max = define space min, max
     * k が求める sub space pos とすると
     * min + k \frac{max - min}{cp} <= p < min + (k + 1)\frac{max - min}{cp}
     *   より
     * \frac{cp (p - min)}{max - min} - 1 < k <= \frac{cp (p - min)}{max - min}
     *  となる
     * \frac{cp}{max - min} = partVolOneSize
     *  (partVolOneSize * (p - min)) - 1 
     */
    private IntVec3 getNewSpcposOfaPoint(Vector3 pos) 
    {
	Vector3 relativePos = 
	    pos.minusNew(this.mSpcParamRef.peekPartVolumeMin());
	int [] spos = new int[3]; // space position
	for(int i = 0; i < 3; i++)
	{
	    // 位置を空間の大きさで割り、小さい方向へ丸める
	    spos[i] = 
		(int)(Math.floor(relativePos.get(i) / this.mPartVolOneSize));
	    Assert.out(spos[i] >= 0, "Error!: spos must be >= 0");
	    
	    /*
	     * 通常は floor だけで良いが、最初のレイが define space の壁の
	     * 大きな部分の方に当たった場合だけは例外である。
	     * なぜなら、各部分空間内は [min, max) の範囲であるが一番外だ
	     * けは外から交差してくるので、その時だけば [min, max] として
	     * 計算する必要があるのである。未来の私へ、わかるかね? 山内君 
	     * なんてね (いかんタッキーの口癖がうつってしまった。)
	     */
	    if(spos[i] == this.mSpcParamRef.getSubDivNum())
	    {
		if((double)((int)(relativePos.get(i) / this.mPartVolOneSize))
		   == (relativePos.get(i) / this.mPartVolOneSize))
		{
		    // 空間の大きい方の端に正に丁度 point がある 
		    spos[i]--;
		}else
		{
		    System.out.println("Internal Error! : Is this pos in " + 
				       "define space?");
		}
	    }
	}
	return(new IntVec3(spos[0], spos[1], spos[2]));
    }

    /**
     * view vector generator
     */
    private Vector3 genNewViewVec()
    {
	/*
	 * AllScan の場合には格子状のエリアシング模様が発生するが，これは
	 * 並列動作時のみであり，1PE 時には正しく動作する．
	 * この模様を AllScan mode の並列動作時に取り除くには隣接 pixel を担
	 * 当する PE 間で共有するある pixel 上には同一のサンプリングを行わ
	 * なくてはならない．共有する場合には 1 つの PE がサンプリングして，
	 * 担当する PE へと ray を送信するという手などがあるが， AllScan 
	 * mode だけ固有の通信のコードを入れたくないので AllScan の並列動
	 * 作時の不都合はそのまま残しておく． 
	 */
	double scrX = (double)(this.mScrX);
	double scrY = (double)(this.mScrY);

	// this.mScrX + drand() 
	Vector3 deltaXdir = this.mDeltaXscr.mulScalerNew(scrX + getDRandom());
	// this.mScrY + drand() 
	Vector3 deltaYdir = this.mDeltaYscr.mulScalerNew(scrY + getDRandom());
	
	// viewvec = screen left up + deltaXdir + deltaYdir
	Vector3 viewVec = this.mScreenLeftUpFromVp.plusNew(deltaXdir);
	viewVec.plusHere(deltaYdir);

	viewVec.normalize();
	return(viewVec);	
    }
    
    /** 
     * ray generator
     *------------------------------
     * 視点が定義空間内にある場合の ray 発生 
     * 1 本 ray を発生させて index を進めておく。
     * 全て終了したらそのフラグを立てる。
     *      内部で保持している発生レイの位置は移動しない
     */
    private Ray newOneRayVpInDefineSpc() // 視点が定義空間内部にある
    {
	Assert.out(this.mCanGenerateNextRay == true, 
		   "Error!: newOneRayVpInDefineSpc : no more gen ray.");
	Assert.out(this.mIsVpInDefSpc == true, 
		   "Error!: mIsVpInDefSpc == false");

	// 直接 genNewViewVec() を書いても良いと思うがちょっと不安がある．
	Vector3 distPixelVec = this.genNewViewVec();

	Assert.out(Math.abs((distPixelVec.getNorm()) - 1.0) < D_ERROR_RES, 
		   "Error!: not normalized.");    

	// 視点が定義空間内部にあるので必ず ray は定義空間内部にある．
	Ray ray = Ray.newForRayTrace(this.mScrX, this.mScrY,
				     this.mViewPoint, 
				     distPixelVec, // 直接 genNewViewVec()?
				     this.mVpSpcPos,
				     this.mDistRaysFactor); //, PEid;
	return(ray);
    }

    /**
     * 視点が定義空間の外にある場合の ray 発生
     */
    private Ray newOneRayVpOutOfDefSpc() // 視点が定義空間外部にある
    {
	Assert.out(this.mCanGenerateNextRay == true, 
		   "Error!: newOneRayVpOutOfDefSpc : no more rays");
	Assert.out(this.mIsVpInDefSpc == false, 
		   "Error!: newOneRayVpOutOfDefSpc : in defspc");

	Vector3 viewVec = this.genNewViewVec();

	Assert.out(Math.abs((viewVec.getNorm()) - 1.0) < D_ERROR_RES,
		   "Error!: not normalzied");    

	/*
	 * 定義空間との交差判定 
	 * 当たった場合にはどの部分空間にあたったか計算
	 */
	Vector3 hitpos = new Vector3();
	int hitSide = 
	    BoundTester.isHitBoundingBox(this.mSpcParamRef.peekDefSpaceMin(),
					 this.mSpcParamRef.peekDefSpaceMax(),
					 this.mViewPoint, viewVec,
					 hitpos);
	if(hitSide != DIR_3D.ILLEGAL)
	{
	    // ray が定義空間に hit した場合
	    IntVec3 hitSubSpcPos = this.getNewSpcposOfaPoint(hitpos);

	    switch(this.getScanMethodOnScreen())
	    {
	    case AllScan_frgm:
		// AllScan 初期光の分散を行わない
		// ray がこの PE の担当空間にいない場合には却下する
		// 親参照:ちょっとdirty
		if(PE.getInstance().isDealSpace(hitSubSpcPos) == true) 
		{
		    Ray ray = Ray.newForRayTrace(this.mScrX, this.mScrY, 
						 this.mViewPoint, viewVec,
						 hitSubSpcPos, 
						 this.mDistRaysFactor); 
		    return(ray);
		}else 
		{ 
		    return(null); //定義空間にはhitしたが担当空間でないから却下
		}
		
	    case ScanlineScan_frgm:
	    case DistributedScan_frgm:
		{
		    /* 
		     * 初期光を分散させる
		     * スクリーンが分割され，各スクリーンがノードに分配される
		     * ので，必ず各ノードは ray を発生する
		     * この ray はこの空間にいるとは限らない．
		     */
		    Ray ray = Ray.newForRayTrace(this.mScrX, this.mScrY, 
						 this.mViewPoint, viewVec,
						 hitSubSpcPos, 
						 this.mDistRaysFactor); //,PEid
		    return(ray);		
		}
	    default:
		throw new RuntimeException("Internal Error! : No such screen "
					   + "scan method.");
		// commMpi2_exit(1);
		// return(null);
	    }
	}else
	{
	    // 定義空間に hit しないので発生する必要はない．
	    return(null);
	}
	//cout + "never reach. rayTrFRGUnitClass.newOneRayVpOutOfDefSpc()" 
	//commMpi2_exit(1);
	//return(null);
    }

    public RayTrFRGUnit()
    {
	this.mScanMethodOnScreen   = ELSE_frgm;
    
	this.mPEid 		= null;
	this.mViewPoint		= null;
	this.mPixelPointer	= null;

	this.mScreenLeftUpFromVp= null;
	this.mDeltaXscr		= null;
	this.mDeltaYscr		= null;

	this.mMaxRaysInAPixel 	= -1;
	this.mGenRayParPixel	= -1;

	this.mCanGenerateNextRay= false; // 初期化までは next rays はなし


	// this.mSpcParamRef.peekDefSpaceMin()	= null;		
	// this.mDefSpcMax	= null;
	// this.mPartVolMinPos	= null;	
	// this.mSubDivNum	= 0.0;	
	// this.mPartVolOneSize	= -1.0;	
	this.mSpcParamRef	= null;

	this.mVpSpcPos   	= null;		
	this.mIsVpInDefSpc	= false;
	this.mNumOfTotalGenRays	= -1;
	this.mDistRaysFactor	= 0.0;

	this.mScrX = -10;
	this.mScrY = -11;
    }
    /**
     * init 
     *	@param rayGenerationMethod : 分散 ray 手法(Allscan や ScanlineScan)
     *  @param viewDat	: 視点の位置，視線方向などの視点情報
     *  @param spcDat	: 空間情報
     *  @param PEid	: この初期光発生ユニットの属する PE の ID
     *  @param totalPEnum : システムの全 PE 数 (初期光分散時に必要)
     *  @param myRank 	: 自分の rank (0 からはじまる)
     */
    public void init(int	 rayGenerationMethod,
		     ViewData    viewDat, 
		     SpaceParam  spcDat, 
		     IntVec3 	 PEid,
		     int   	 totalPEnum,
		     int 	 myRank)
    {
	// 視点に関するパラメータを得る
	final Vector3 vp  = viewDat.peekViewPoint();   //視点ベクトル
	final Vector3 vv  = viewDat.peekViewVector();  //視線ベクトル
	final Vector3 vup = viewDat.peekViewUpVector();//スクリーン上方ベクトル
	final double vFovRad = viewDat.getV_FOV();
	final double vHovRad = viewDat.getH_FOV();

	final int screenSizeX = viewDat.getScreenSizeX();
	final int screenSizeY = viewDat.getScreenSizeY();
	
	// スクリーン垂直方向単位ベクトル (既に単位ベクトルのはずだが念のため)
	vup.normalize();			     
	vv.normalize();			     
    
	// スクリーン水平方向単位ベクトル(vSh)は $vSh = vv  \times vup$
	Vector3 vScreenHoriz = vv.outPrdNew(vup); 
	if(vScreenHoriz.getNorm() <= 1e-4)
	{
	    System.out.println(NORMALHEAD + " Warning!:maybe view up vector is"
			       +" illegal.Generated image will not be right.");
	}

	vScreenHoriz.normalize(); // $vSh = \frac{vSh}{|vSh|}$
	double  halfX  = Math.tan(vHovRad);
	double  halfY  = Math.tan(vFovRad);
	// = vp + .... - vp が以下の式に加えられている,すると視点からの相対 vec
	// screenLeftUpFromVp = vv - (halfX * vScreenHoriz) + (halfY * vup);
	Vector3 t1 = vScreenHoriz.mulScalerNew(halfX);
	Vector3 t2 = vup.mulScalerNew(halfY);
	Vector3 screenLeftUpFromVp = vv.minusNew(t1);
	screenLeftUpFromVp.plusHere(t2);

	// deltaX = ((2.0 * halfX) / double(screenSizeX)) * vScreenHoriz;
	Vector3 deltaX = 
	    vScreenHoriz.mulScalerNew((2.0 * halfX) / (double)(screenSizeX));
	    
	// deltaY = ((-2.0 * halfY) / double(screenSizeY)) * vup;	
	Vector3 deltaY = 
	    vup.mulScalerNew((-2.0 * halfY) / (double)(screenSizeY));
	
	initRandom();		// distributed ray-tracing のための乱数初期化

	// initialize member
	this.mScanMethodOnScreen = rayGenerationMethod;
	this.mPEid	 	 = PEid;
	this.mViewPoint		 = vp;
	this.mPixelPointer	 = null; // 初期化は initForScreenPointer にて
	this.mScreenLeftUpFromVp = screenLeftUpFromVp;
	this.mDeltaXscr		 = deltaX;
	this.mDeltaYscr		 = deltaY;
	this.mMaxRaysInAPixel	 = viewDat.getDistributedRayNum();
	Assert.out(this.mMaxRaysInAPixel > 0, 
		   "Error!: mMaxRaysInAPixel must be > 0");
	this.mGenRayParPixel	 = 0;
	
	this.mCanGenerateNextRay = true; // まだ ray は発生していない

	// [空間情報] ある点の副空間位置を求めるための準備 
	// 空間情報の参照初期化
	this.mSpcParamRef = spcDat;

	// this.mDefSpcMin = spcDat.getDefSpaceMin(); // 定義空間をとりだす
	// 	this.mDefSpcMax       = spcDat.getDefSpaceMax();
	// 	this.mPartVolMinPos   = spcDat.getPartVolumeMin();
	final Vector3 partVolMaxPos = this.mSpcParamRef.peekPartVolumeMax();
	
	// partition volume の初期化．
	// partition volume は立方体であるので一辺がわかれば良い 
	this.mSubDivNum	  = (double)(this.mSpcParamRef.getSubDivNum());

	this.mPartVolOneSize = 
	    (partVolMaxPos.get(Vector3.X)
	     - (this.mSpcParamRef.peekPartVolumeMin()).get(Vector3.X))
	    / this.mSubDivNum;
	
	// initialize parameter for distributed ray-trace 
	this.mNumOfTotalGenRays	= 0;	// 総 ray 発生数
	
	/**
	 * mDistRaysFactor : 各 ray の輝度は distributed のレイの数分の 1  
	 * の重みを持つ。そのため coefficient value にその値を掛けておく
	 * 4 本の distributed ならば各 ray の 1 つの pixel への寄与は 1/4 
	 */
	this.mDistRaysFactor = 1.0 / (double)(this.mMaxRaysInAPixel);
	
	this.mScrX = 0;
	this.mScrY = 0;
	
	/**
	 * screen pointer の初期化
	 * 	this.mIsVpInDefSpc, this.mVpSpcPos の初期化
	 */
	this.initForScreenPointer(screenSizeX, screenSizeY, 
				  myRank, totalPEnum);
    }

    /**
     * 
     */
    public boolean canGenerateNextRay()  
    {
	return(this.mCanGenerateNextRay); 
    }

    /**
     * ray を一本発生させる
     * 	初期光を分散法の適用時には，発生した ray の部分空間番号が，発
     * 	生させた PE の担当でない場合がある．
     */
    public Ray newNextRay()
    {
	// この関数を呼ぶ前に一本は発生可能であることを調べてあるはず
	Assert.out(this.canGenerateNextRay() == true, 
		   "Error!: no more rays can generate : newNextRay.");
	Ray ray = null;
	if(this.mIsVpInDefSpc == true)
	{
	    // 視点が空間のどこにあるか関係するのは初期光分散なしの All
	    // scan の時のみ
	    Assert.out(this.getScanMethodOnScreen() == AllScan_frgm, 
		       "Error!: Here mustbe AllScan_frgm");

	    /**
	     * 視点が定義空間内部に存在する
	     * この場合にはある pixel 位置の ray を発生できないということ
	     * はないので、初期光分散にかかわらず必ず1本は ray を発生する
	     */
	    ray = this.newOneRayVpInDefineSpc();
	    this.moveOnScreen();	// 次の発生位置へ移動
	    Assert.out(ray != null, "Error!: ray must be new");
	    this.mNumOfTotalGenRays++; // 発生 ray 数をカウントする
	}else
	{				
	    /**
	     * 視点は定義空間内部にはない
	     * 発生可能状態であっても現在の screen 位置でこの PE が ray 発
	     * 生を担当するかどうか不明なので、1 本発生させるまで screen 
	     * 上の pixel を移動させてみる。
	     * つまりまだスクリーンを走査可能(canGenerateNextRay() == true) 
	     * でかつ現在の pixel がこの PE 担当でない場合には次々とスクリー
	     * ンを走査させてみる 
	     */
	    while(this.canGenerateNextRay() == true)
	    {
		// この pixel は発生可能か? 
		ray = this.newOneRayVpOutOfDefSpc();
		this.moveOnScreen();	// 次の発生位置へ移動
		if(ray != null)
		{   
		    this.mNumOfTotalGenRays++; // 発生 ray 数をカウントする
		    break;		// ray 発生
		}
	    }
	}
	/**
	 * 初期光を分散させた場合には ray の spcpos を実際に担当している 
	 * PE の所に ray は存在しない可能性がある．
	 */
	return(ray);
    }

    /**
     * get total number of generated rays
     */
    public int getTotalGenRays()
    {
	return(this.mNumOfTotalGenRays); 
    }

    /**
     * get screen scan method on screen
     */
    public int getScanMethodOnScreen() 
    { 
	return(this.mScanMethodOnScreen);
    }

    /**
     * test 
     public static void main(String [] args)
     {
     
     }
     */
}
