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

/**
 * Disk quadric primitive.
 * 二次曲面　：　円板
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqDisk extends CqQuadric {
	
	private float	m_Height;			///< Position on z axis.
	private float	m_MajorRadius;		///< Outer radius of disk.
	private float	m_MinorRadius;		///< Inner radius of disk.
	private float	m_ThetaMin;			///< Min angle about z axis.
	private float	m_ThetaMax;			///< Max angle about z axis.
	

	//*************************************************
	//**  コンストラクタ定義
	//*************************************************
	
	/**
	 * コンストラクタ。<BR>初期値が与えられなかった場合。
	 */
	public CqDisk()
	{
		this ( 0.0f, 0.0f, 1.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param height		Z軸上の位置
	 */
	public CqDisk( float height )
	{
		this ( height, 0.0f, 1.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が二つ与えられた場合。
	 * 
	 * @param height		Z軸上の位置
	 * @param minorradius	半径(短)
	 */
	public CqDisk( float height, float minorradius )
	{
		this ( height, minorradius, 1.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が三つ与えられた場合。
	 * 
	 * @param height		Z軸上の位置
	 * @param minorradius	半径(短)
	 * @param majorradius	半径(長)
	 */
	public CqDisk( float height, float minorradius, float majorradius )
	{
		this ( height, minorradius, majorradius, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が四つ与えられた場合。
	 * 
	 * @param height		Z軸上の位置
	 * @param minorradius	半径(短)
	 * @param majorradius	半径(長)
	 * @param thetamin		最小スイープ角度
	 */
	public CqDisk( float height, float minorradius, float majorradius, float thetamin )
	{
		this ( height, minorradius, majorradius, thetamin, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が全て与えられた場合。
	 * 
	 * @param height		Z軸上の位置
	 * @param minorradius	半径(短)
	 * @param majorradius	半径(長)
	 * @param thetamin		最小スイープ角度
	 * @param thetamax		最大スイープ角度
	 */
	public CqDisk( float height, float minorradius, float majorradius, float thetamin, float thetamax )
	{
		m_Height = height ;
		m_MajorRadius = majorradius ;
		m_MinorRadius = minorradius ;
		m_ThetaMin = thetamin ;
		m_ThetaMax = thetamax ;
	}
	
	/**
	 * コピーコンストラクタ
	 * @param From	コピー元
	 */
	public CqDisk( final CqDisk From )
	{
		this.assignment( From );
	}
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#destruct()
	 */
	public void	destruct()
	{}
	

	//*************************************************
	//**  メソッド定義
	//*************************************************
		
	/**
	 * Get the geometric bound of this GPrim.
	 * @see net.cellcomputing.himawari.library.IqSurface#Bound()
	 */
	public	CqBound	Bound()
	{
		STLVector<CqVector3D> curve = new STLVector<CqVector3D>( CqVector3D.class );
		CqVector3D vA = new CqVector3D( m_MajorRadius, 0, m_Height );
		CqVector3D vB = new CqVector3D( m_MinorRadius, 0, m_Height );
		CqVector3D vC = new CqVector3D( 0, 0, 0 );
		CqVector3D vD = new CqVector3D( 0, 0, 1 );
		
		curve.add( vA );
		curve.add( vB );
		CqMatrix matRot = new CqMatrix( (float)toRadians( m_ThetaMin ), vD );
		
		for ( CqVector3D i : curve )
			i.assignment( matRot.multiply( i ));
		
		CqBound	B = new CqBound( RevolveForBound( curve, vC, vD, (float)toRadians( m_ThetaMax - m_ThetaMin )));
		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 + ((float)u * ( m_ThetaMax - m_ThetaMin )) / (float)m_uDiceSize );
		float vv = m_MajorRadius - ( (float)v * ( m_MajorRadius - m_MinorRadius )) / (float)m_vDiceSize;
		
		return ( new CqVector3D( (float)( vv * cos( theta )), (float)( vv * sin( theta )), m_Height ) );
	}
	
	
	/**
	 * 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, net.cellcomputing.himawari.library.types.CqVector3D)
	 */
	public	CqVector3D	DicePoint( int u, int v, CqVector3D Normal )
	{
		Normal.assignment( new CqVector3D( 0.0f, 0.0f, m_ThetaMax > 0 ? 1.0f : -1.0f ) );
		return  DicePoint( u, v ) ;
	}
	
	public boolean	CanGenerateNormals()
	{
		return true;
	}
	
	
	/**
	 * Split this GPrim into a NURBS surface. Temp implementation, should split into smalled 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_MajorRadius + m_MinorRadius ) * 0.5 );
		float arccent = (float)(( m_ThetaMin + m_ThetaMax ) * 0.5 );
		
		CqDisk pNew1 = new CqDisk( this );
		CqDisk pNew2 = new CqDisk( this );
		
		if ( u )
		{
			pNew1.m_ThetaMax = arccent;
			pNew2.m_ThetaMin = arccent;
		}
		else
		{
			pNew1.m_MinorRadius = zcent;
			pNew2.m_MajorRadius = zcent;
		}
		
		aSplits.add( pNew1 );
		aSplits.add( pNew2 );
		
		return ( 2 );
	}
	
	/**
	 * 代入演算子のオーバーロード
	 * 
	 * 
	 * @param From	代入元
	 * @return	自身
	 */
	public CqDisk assignment( final CqDisk From )
	{
		super.assignment( From );
		m_Height = From.m_Height;
		m_MajorRadius = From.m_MajorRadius;
		m_MinorRadius = From.m_MinorRadius;
		m_ThetaMin = From.m_ThetaMin;
		m_ThetaMax = From.m_ThetaMax;
		
		return ( this );
	}
	
}
