// 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.accessory.STLVector;
import net.cellcomputing.himawari.library.types.CqVector3D;
import static java.lang.Math.*;


/**
 * Hyperboloid quadric GPrim.
 * 二次曲面　：　方物面
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqParaboloid extends CqQuadric {
	
	private float	m_RMax;			///< Radius at zmax.
	private float	m_ZMin;			///< Min value on z axis.
	private float 	m_ZMax;			///< Max value on z axis.
	private float	m_ThetaMin;		///< Min angle about z axis.
	private float	m_ThetaMax;		///< Max angle about z axis.
	
	
	//*************************************************
	//**  コンストラクタ定義
	//*************************************************
	
	/**
	 *  コンストラクタ。<BR>初期値が与えられなかった場合。
	 */
	public CqParaboloid()
	{
		this( 1.0f, -1.0f, 1.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param rmax	最大半径
	 */
	public CqParaboloid( float rmax )
	{
		this( rmax, -1.0f, 1.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が二つ与えられた場合。
	 * 
	 * @param rmax	最大半径
	 * @param zmin	最小高さ
	 */
	public CqParaboloid( float rmax, float zmin )
	{
		this( rmax, zmin, 1.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param rmax	最大半径
	 * @param zmin	最小高さ
	 * @param zmax	最大高さ
	 */
	public CqParaboloid( float rmax, float zmin, float zmax )
	{
		this( rmax, zmin, zmax, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param rmax		最大半径
	 * @param zmin		最小高さ
	 * @param zmax		最大高さ
	 * @param thetamin	最小スイープ角度
	 */
	public CqParaboloid( float rmax, float zmin, float zmax, float thetamin )
	{
		this( rmax, zmin, zmax, thetamin, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param rmax		最大半径
	 * @param zmin		最小高さ
	 * @param zmax		最大高さ
	 * @param thetamin	最小スイープ角度
	 * @param thetamax	最大スイープ角度
	 */
	public CqParaboloid( float rmax, float zmin, float zmax, float thetamin, float thetamax )
	{
        m_RMax = rmax ;
        m_ZMin = zmin ;
        m_ZMax = zmax ;
        m_ThetaMin = thetamin ;
        m_ThetaMax = thetamax ;
	}
	
	/**
	 * コピーコンストラクタ
	 * 
	 * @param From	コピー元
	 */
	public CqParaboloid( final CqParaboloid From )
	{
		this.assignment( From );
	}
	
	public void	destruct()
	{}

	//*************************************************
	//**  メソッド定義
	//*************************************************
	
	
	/**
	 * Get the geometric bound of this GPrim.
	 * @see net.cellcomputing.himawari.library.IqSurface#Bound()
	 */
	public	CqBound	Bound()
	{
		/*	
		 * float xminang,yminang,xmaxang,ymaxang;
		 * xminang=yminang=MIN(m_ThetaMin,m_ThetaMax);
		 * xmaxang=ymaxang=MAX(m_ThetaMin,m_ThetaMax);
		 * 
		 * 
		 * // If start and end in same segement, just use the points.
		 * if(static_cast<int>(m_ThetaMin/90)!=static_cast<int>(m_ThetaMax/90))
		 * {
		 * if(yminang<90 && ymaxang>90)	yminang=90;
		 * if(yminang<270 && ymaxang>270)	ymaxang=270;
		 * if(xminang<180 && xmaxang>180)	xmaxang=180;
		 * }
		 */
		
		float x1 = ( float )( m_RMax * cos( toRadians( 0 )));
		float x2 = ( float )( m_RMax * cos( toRadians( 180 )));
		float y1 = ( float )( m_RMax * sin( toRadians( 90 )));
		float y2 = ( float )( m_RMax * sin( toRadians( 270 )));
		
		CqVector3D vecMin = new CqVector3D ( min( x1, x2 ), min( y1, y2 ), min( m_ZMin, m_ZMax ));
		CqVector3D vecMax = new CqVector3D ( max( x1, x2 ), max( y1, y2 ), max( m_ZMin, m_ZMax ) );
		
		CqBound	B = new CqBound( vecMin, vecMax );
		B.Transform( m_matTx );
		
		return ( AdjustBoundForTransformationMotion( B ) );
	}
	
	/**
	 * Get a point on the surface indexed by the surface paramters passed.
	 * @param u Float surface paramter in u.
	 * @param v Float surface paramter in v.
	 * @see net.cellcomputing.himawari.library.CqQuadric#DicePoint(int, int)
	 */
	public	CqVector3D	DicePoint( int u, int v )
	{
		     float theta = ( float )toRadians( m_ThetaMin + (( m_ThetaMax - m_ThetaMin ) * ( float )u ) / m_uDiceSize );
		     float z = m_ZMin + ( ( float ) v * ( m_ZMax - m_ZMin ) ) / m_vDiceSize;
		     float r = m_RMax * ( float )sqrt( z / m_ZMax );

		     return ( new CqVector3D( ( float )( r * cos( theta )), ( float )( r * sin( theta )), z ));
	}
	
	/**
	 * Get a point on the surface indexed by the surface paramters passed.
	 * @param u Float surface paramter in u.
	 * @param v Float surface paramter in v.
	 * @param Normal Storage for the surface normal at that point.
	 * @see net.cellcomputing.himawari.library.CqQuadric#DicePoint(int, int)
	 */
	public	CqVector3D	DicePoint( int u, int v, CqVector3D Normal )
	{
		float theta = ( float )toRadians( m_ThetaMin + (( m_ThetaMax - m_ThetaMin ) * ( float )u ) / m_uDiceSize );
		float sin_theta = ( float )sin( theta );
		float cos_theta = ( float )cos( theta );
		
		float z = m_ZMin + ((float) v * ( m_ZMax - m_ZMin )) / (float)m_vDiceSize;
		float r = m_RMax * ( float )sqrt( z / m_ZMax );
		
		float dzdr = r * 2.0f * m_ZMax / ( m_RMax * m_RMax );
		float normalAngle = ( float )( PI / 2.0 - atan( dzdr ));
		Normal.x(( float )( cos_theta * cos( normalAngle )));
		Normal.y(( float )( sin_theta * cos( normalAngle )));
		Normal.z(( float )( -sin( normalAngle )));
		
		return ( new CqVector3D( r * cos_theta, r * sin_theta, z ) );
	}

	/**
	 * Split this GPrim into smaller quadrics.
	 * @see net.cellcomputing.himawari.library.IqSurface#PreSubdivide(net.cellcomputing.himawari.accessory.STLVector, boolean)
	 */
	public	int PreSubdivide( STLVector<CqBasicSurface> aSplits, boolean u )
	{
		float zcent = ( float )(( m_ZMin + m_ZMax ) * 0.5 );
		float arccent = ( float )(( m_ThetaMin + m_ThetaMax ) * 0.5 );
		float rcent = ( float )( m_RMax * sqrt( zcent / m_ZMax ));
		
		CqParaboloid pNew1 = new CqParaboloid( this ) ;
		CqParaboloid pNew2 = new CqParaboloid( this ) ;
		
		if ( u )
		{
			pNew1.m_ThetaMax = arccent;
			pNew2.m_ThetaMin = arccent;
		}
		else
		{
			pNew1.m_ZMax = zcent;
			pNew1.m_RMax = rcent;
			pNew2.m_ZMin = zcent;
		}
		
		aSplits.add( pNew1 );
		aSplits.add( pNew2 );
		
		return ( 2 );
	}

	/**
	 * 代入演算子のオーバーロード
	 * 
	 * 
	 * @param From	代入元
	 * @return	自身
	 */
	public CqParaboloid	assignment( final CqParaboloid From )
	{
		super.assignment( From );
		m_RMax = From.m_RMax;
		m_ZMin = From.m_ZMin;
		m_ZMax = From.m_ZMax;
		m_ThetaMin = From.m_ThetaMin;
		m_ThetaMax = From.m_ThetaMax;
		
		return ( this );
	}
	
}
