// Aqsis
// Copyright (c) 1997 - 2001, Paul C. Gregory
//
// Contact: pgregory@aqsis.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/**
 * Copyright (C) 2006-2007  NTT DATA CORPORATION
 * 
 * Version: 1.0.0 2007/04/01
 *  
 */
package net.cellcomputing.himawari.library;

import net.cellcomputing.himawari.library.types.CqMatrix;
import net.cellcomputing.himawari.library.types.CqVector2D;
import net.cellcomputing.himawari.library.types.CqVector3D;

import static net.cellcomputing.himawari.library.Float_h.*;
/**
 * CqBoundはバウンディングボックスの処理を簡略化、高速化するために使用される。
 * 
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqBound {
	
	/**
	 * コンストラクタ：引数によって初期化される。
	 * 
	 * @param pBounds　float[6]の配列
	 */
	public CqBound( float[] pBounds )
	{
		if ( pBounds != null)
		{
			m_vecMin.x = pBounds[ 0 ] ;	
			m_vecMin.y = pBounds[ 2 ] ;
			m_vecMin.z = pBounds[ 4 ] ;
			
			m_vecMax.x = pBounds[ 1 ] ;
			m_vecMax.y = pBounds[ 3 ] ;	
			m_vecMax.z = pBounds[ 5 ] ;
		}
	}
	
	/**
	 * コンストラクタ：何も引数がないときは以下のように初期化される。<br>
	 * 見ためMinに-FLT_MAXが入りそうだが、比較後に小さい方を入れるためこれでよい。
	 */
	public CqBound()
	{
		m_vecMin.x = FLT_MAX ;
		m_vecMin.y = FLT_MAX ;
		m_vecMin.z = FLT_MAX ;
		m_vecMax.x = -FLT_MAX ;	
		m_vecMax.y = -FLT_MAX ;
		m_vecMax.z = -FLT_MAX ;
	}
	
	/**
	 * コンストラクタ：引数によって初期化される。
	 * @param XMin 最小値の値のｘ座標
	 * @param YMin 最小値の値のｙ座標
	 * @param ZMin 最小値の値のｚ座標
	 * @param XMax 最大値の値のｘ座標
	 * @param YMax 最大値の値のｙ座標
	 * @param ZMax 最大値の値のｚ座標
	 */
	public    CqBound( float XMin , float YMin , float ZMin  , float XMax , float YMax , float ZMax )
	{
		m_vecMin.x = XMin ;
		m_vecMin.y = YMin ;
		m_vecMin.z = ZMin ;
		m_vecMax.x = XMax ;
		m_vecMax.y = YMax ;
		m_vecMax.z = ZMax ;
	}
	
	/**
	 * コンストラクタ：引数によって初期化される。
	 * @param vecMin　最小値のVector3Dの値
	 * @param vecMax　最大値のVector3Dの値
	 */
	public CqBound( final CqVector3D vecMin, final CqVector3D vecMax )
	{
		m_vecMin.assignment( vecMin );
		m_vecMax.assignment( vecMax );
	}
	
//	---------------------------------------------------------------------
	
	/**
	 * コピーコンストラクタ
	 * @param From　コピーされる値
	 */
	public CqBound( final CqBound From )
	{
		this.m_vecMax.assignment( From.m_vecMax );
		this.m_vecMin.assignment( From.m_vecMin );
	}
	
	/**
	 * デストラクタ<br>
	 * 何もしない
	 * 
	 * 
	 */
	public void destract()
	{}
	
//	public final CqVector3D	vecMin()
//	{
//	return ( m_vecMin );
//	}
	
	/**
	 * メンバ変数 m_vecMinのゲッター
	 * @return　メンバ変数 m_vecMin
	 */
	public    CqVector3D	vecMin()
	{
		return ( m_vecMin );
	}
	
//	public    const	CqVector3D&	vecMax() const
//	{
//	return ( m_vecMax );
//	}
	/**
	 * メンバ変数 m_vecMaxのゲッター
	 * @return　メンバ変数 m_vecMax
	 */
	public    CqVector3D	vecMax()
	{
		return ( m_vecMax );
	}
	
	/**
	 * m_vecMinからm_vecMaxでのベクトルを求める。
	 *  
	 * @return　CqVector3Dで四角を交差するベクトル
	 */
	public	CqVector3D vecCross()
	{
		return( m_vecMax.sub( m_vecMin ) );
	}
	
	/**
	 * バウンディングボックスの大きさを求める。
	 * @return　float型でバウンディングボックスの大きさ
	 */
	public	float	Volume()
	{
		return (float)Math.sqrt( Volume2() );
	}
	
	/**
	 * バウンディングボックスの大きさを求めるために使われる。
	 * @return　float型でバウンディングボックスの大きさの2乗
	 */
	public	float Volume2()
	{
		return( vecCross().Magnitude2() );
	}
	
//	---------------------------------------------------------------------
	
	/**
	 *　Bound同士を代入する。 
	 * 
	 * @param From　代入するもの
	 * @return　自分自身を返す。
	 */
	public    CqBound	assignment( final CqBound From )
	{
		m_vecMin.assignment( From.m_vecMin );
		m_vecMax.assignment( From.m_vecMax );
		
		return ( this );
	}
	
	
	/**
	 * バウンドの値を指定されたマトリックスによって変換する。
	 * 
	 * @param matTransform　変換させるためのマトリックスを参照するCqMatrix
	 */
	public    void		Transform( final CqMatrix matTransform )
	{
		// Transform the cuboid points.
		CqVector3D[]	avecCuboid = new CqVector3D[ 8 ];
		avecCuboid[ 0 ] = m_vecMin;
		avecCuboid[ 1 ] = new CqVector3D( m_vecMax.x, m_vecMin.y, m_vecMin.z );
		avecCuboid[ 2 ] = new CqVector3D( m_vecMin.x, m_vecMax.y, m_vecMin.z );
		avecCuboid[ 3 ] = new CqVector3D( m_vecMin.x, m_vecMin.y, m_vecMax.z );
		avecCuboid[ 4 ] = new CqVector3D( m_vecMax.x, m_vecMax.y, m_vecMin.z );
		avecCuboid[ 5 ] = new CqVector3D( m_vecMin.x, m_vecMax.y, m_vecMax.z );
		avecCuboid[ 6 ] = new CqVector3D( m_vecMax.x, m_vecMin.y, m_vecMax.z );
		avecCuboid[ 7 ] = m_vecMax;
		
		m_vecMin = new CqVector3D( FLT_MAX, FLT_MAX, FLT_MAX );
		m_vecMax = new CqVector3D( -FLT_MAX, -FLT_MAX, -FLT_MAX );
		
		int i;
		for ( i = 0; i < 8; i++ )
		{
			avecCuboid[ i ].assignment( matTransform.multiply( avecCuboid[ i ] ) );
			if ( avecCuboid[ i ].x < m_vecMin.x ) m_vecMin.x = avecCuboid[ i ].x ;
			if ( avecCuboid[ i ].y < m_vecMin.y ) m_vecMin.y = avecCuboid[ i ].y ;
			if ( avecCuboid[ i ].z < m_vecMin.z ) m_vecMin.z = avecCuboid[ i ].z ;
			
			if ( avecCuboid[ i ].x > m_vecMax.x ) m_vecMax.x = avecCuboid[ i ].x ;
			if ( avecCuboid[ i ].y > m_vecMax.y ) m_vecMax.y = avecCuboid[ i ].y ;
			if ( avecCuboid[ i ].z > m_vecMax.z ) m_vecMax.z = avecCuboid[ i ].z ;
		}
	}
	
	/**
	 * 指定されたバウンドと現在のバウンドを結合する。両方の領域を含む新たなバウンドを作成しリターンする。
	 * 
	 * @param bound　このBoundの追加するCqBound
	 * @return　結合後のCqBound
	 */
	public    CqBound		Combine( final CqBound bound )
	{
		CqBound Result = new CqBound();
		
		Result.m_vecMax.x = Math.max( m_vecMax.x, bound.m_vecMax.x );
		Result.m_vecMax.y = Math.max( m_vecMax.y, bound.m_vecMax.y );
		Result.m_vecMax.z = Math.max( m_vecMax.z, bound.m_vecMax.z );
		
		Result.m_vecMin.x = Math.min( m_vecMin.x, bound.m_vecMin.x );
		Result.m_vecMin.y = Math.min( m_vecMin.y, bound.m_vecMin.y );
		Result.m_vecMin.z = Math.min( m_vecMin.z, bound.m_vecMin.z );
		
		return ( Result );
	}
	
	
	/**
	 * 現在のバウンドを引数のバウンドと比較して、拡張する。
	 * 
	 * @param bound　このBoundの追加するCqBound
	 * @return　拡張したあとの自分自身を返す
	 */
	public    CqBound	Encapsulate( final CqBound bound )
	{
		m_vecMax.x = Math.max( m_vecMax.x, bound.m_vecMax.x );
		m_vecMax.y = Math.max( m_vecMax.y, bound.m_vecMax.y );
		m_vecMax.z = Math.max( m_vecMax.z, bound.m_vecMax.z );
		
		m_vecMin.x = Math.min( m_vecMin.x, bound.m_vecMin.x );
		m_vecMin.y = Math.min( m_vecMin.y, bound.m_vecMin.y );
		m_vecMin.z = Math.min( m_vecMin.z, bound.m_vecMin.z );
		
		return( this );
	}
	
	/**
	 * 現在のバウンドを引数のCqVector3Dと比較して、拡張する。
	 * 
	 * @param v 拡張するための点のデータ
	 * @return　拡張したあとの自分自身を返す
	 */
	public    CqBound	Encapsulate( final CqVector3D v )
	{
		m_vecMax.x = Math.max( m_vecMax.x, v.x );
		m_vecMax.y = Math.max( m_vecMax.y, v.y );
		m_vecMax.z = Math.max( m_vecMax.z, v.z );
		
		m_vecMin.x = Math.min( m_vecMin.x, v.x );
		m_vecMin.y = Math.min( m_vecMin.y, v.y );
		m_vecMin.z = Math.min( m_vecMin.z, v.z );
		
		return( this );
	}
	
	/**
	 * 現在のバウンドを引数のCqVecto2Dと比較して、拡張する。
	 * @param v　拡張するための点のデータ
	 * @return　拡張したあとの自分自身を返す
	 */
	public    CqBound	Encapsulate( final CqVector2D v )
	{
		m_vecMax.x = Math.max( m_vecMax.x, v.x );
		m_vecMax.y = Math.max( m_vecMax.y, v.y );
		
		m_vecMin.x = Math.min( m_vecMin.x, v.x );
		m_vecMin.y = Math.min( m_vecMin.y, v.y );
		
		return( this );
	}
	
	/**
	 * 引数で指定されたCqVector3Dの点がバウンドの中にあるかを判定する。
	 * @param v　対象となるCqVector3Dの点のデータ
	 * @return　中にあるかどうかを返す。
	 */
	public    boolean	Contains3D( final CqVector3D v )
	{
		if ( ( v.x >= m_vecMin.x && v.x <= m_vecMax.x ) &&
				( v.y >= m_vecMin.y && v.y <= m_vecMax.y ) &&
				( v.z >= m_vecMin.z && v.z <= m_vecMax.z ) )
			return ( true );
		else
			return ( false );
	}
	/**
	 * 引数で指定されたCqVector2Dの点がバウンドの中にあるかを判定する。
	 * @param v　対象となるCqVector2Dの点のデータ
	 * @return　中にあるかどうかを返す。
	 */
	public    boolean	Contains2D( final CqVector2D v ) 
	{
		if ( ( v.x < m_vecMin.x || v.x > m_vecMax.x ) ||
				( v.y < m_vecMin.y || v.y > m_vecMax.y ) )
			return ( false );
		else
			return ( true );
	}
	
//	----------------------------------------------------------------------
//	/** Outputs a bound to an output stream.
//	* 出力ストリームにバウンドを出力します。
//	* param Stream Stream to output the matrix to.
//	* \param Bound The bound to output.
//	* \return The new state of Stream.
//	*/
//	public    friend std::ostream &operator<<( std::ostream &Stream, const CqBound &Bound );
//	{
//	CqVector3D min = Bound.vecMin();
//	CqVector3D max = Bound.vecMax();
//	CqVector3D cross = Bound.vecCross();
//	
//	Stream << min << "-->" << max << "  |  Cross: " << cross << std::ends;
//	return ( Stream );
//	}
	
	/**
	 * 出力ストリームにバウンドを出力します。
	 * 
	 * 
	 * @return　新しいストリームを返す。
	 */
	public String toString()
	{
		CqVector3D min = this.vecMin();
		CqVector3D max = this.vecMax();
		CqVector3D cross = this.vecCross();
		String ret = new String(min.toString() + "-->" + max.toString() + "  |  Cross: " + cross.toString() );
		
		return ret;
	}
	
	
	
	
	private    CqVector3D	m_vecMin = new CqVector3D();
	private    CqVector3D	m_vecMax = new CqVector3D();
}
