/*
 * $Id: BoundTester.java,v 1.2.4.1 2000/06/18 20:43:54 yamauchi Exp $
 *     (M\pi)^2 : BoundTester
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *
 * 	空間の境界とのテスト
 *	位置ベクトル、ray とある box の範囲とのテスト。ここでの box は 
 *	x, y, z 軸に沿った箱のことを言う 
 *	
 * original source boundtest.cc
 *
 * 
 * 1999年8月 6日(金)
 */

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

import  jp.ac.tohoku.is.archi.mpi2.util.IntVec3;
import  jp.ac.tohoku.is.archi.mpi2.util.Vector3;
import  jp.ac.tohoku.is.archi.mpi2.util.Assert;
import  jp.ac.tohoku.is.archi.mpi2.dda.DIR_3D;

/**
 * 空間の境界と ray の交差テスト
 */ 
public final class BoundTester {
    private static final int [] mHitDirection = {
        DIR_3D.XPLUS,
        DIR_3D.XMINUS,
        DIR_3D.YPLUS,
        DIR_3D.YMINUS,
        DIR_3D.ZPLUS,
        DIR_3D.ZMINUS,
        DIR_3D.HERE,               
        DIR_3D.ELEMNUM,
        DIR_3D.ILLEGAL,
    };
    
    /**
     * 交差判定 bounding box 
     */
    public static int isHitBoundingBox(Vector3 minBox,    
				       Vector3 maxBox, 
				       Vector3 rayOrigin,
				       Vector3 rayDir,
				       Vector3 hitPointRef)
    {
	Vector3 boxCenter = (maxBox.plusNew(minBox));
	boxCenter.mulScalerHere(0.5);

	Vector3 boxHalfSize = (maxBox.minusNew(minBox));
	boxHalfSize.mulScalerHere(0.5);

	Vector3 vv = rayDir;
	vv.normalize();	
	double distance = Double.MAX_VALUE;
	int hitSide  = DIR_3D.ILLEGAL;
	for(int i = 0; i < 3; i++)
	{
	    /**
	     * check each faces
	     */
	    if(Math.abs(vv.get(i)) > 1e-5)
	    {			   
		Assert.out(boxHalfSize.get(i) > 0.0,"Error!: box must be > 0");
		
		double tmpDist = ((boxCenter.get(i) + boxHalfSize.get(i)) 
				  - rayOrigin.get(i)) / vv.get(i);
		    
		if((0.0 < tmpDist) && (tmpDist < distance))
		{
		    double [] hitPos = new double[3];
		    hitPos[i] = boxCenter.get(i) + boxHalfSize.get(i);
		    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 = 
			new Vector3(hitPos[0], hitPos[1], hitPos[2]);

		    relaPos.minusHere(boxCenter);
		    
		    /**
		     * x 方向のテストをしているとして、y, z が範囲内にある
		     * かを調べる。
		     */
		     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  = mHitDirection[i * 2];
			 distance = tmpDist;
			 hitPointRef.set(hitPos[0], hitPos[1], hitPos[2]);
		     }
		}	   
		
		/**
		 * 箱中心から Minus 方向の面
		 */
		tmpDist = ((boxCenter.get(i) - boxHalfSize.get(i)) - 
			   rayOrigin.get(i)) / vv.get(i);
		if((tmpDist > 0) && (tmpDist < distance))
		{
		    double [] hitPos = new double[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 = 
			new Vector3(hitPos[0], hitPos[1], hitPos[2]);
		    relaPos.minusHere(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  = mHitDirection[i * 2];
			distance = tmpDist;
			hitPointRef.set(hitPos[0], hitPos[1], hitPos[2]);
		    }
		}	   
	    }
	}	
	if(distance == Double.MAX_VALUE)
	{
	    return(DIR_3D.ILLEGAL);	
	}else
	{
	    return(hitSide);
	}
    }
    
    /**
     * pos が [min, max) の中にあるか 
     * changed from isViewPointInDefineSpace
     */
    public static boolean isPosInBox(Vector3 pos, 
				     Vector3 minpos, Vector3 maxpos)
    {
	for(int i = 0; i < 3; i++)
	{
	    if((pos.get(i) < minpos.get(i)) || (maxpos.get(i) < pos.get(i)))
	    {
		return(false);
	    }
	}
	return(true);
    }

    public static boolean isPosInBoxWithRes(Vector3 pos, 
					    Vector3 minpos, Vector3 maxpos,
					    double err)
    {
	Assert.out(err > 0.0, "Error!: isPosInBoxWithRes");
	
	for(int i = 0; i < 3; i++)
	{
	    if(((pos.get(i) + err) < minpos.get(i)) ||
	       (maxpos.get(i) < (pos.get(i) - err)))
	    {
		return(false);
	    }
	}
	return(true);
    }
}
