// 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.StrictMath.*;

/**
 * Torus quadric GPrim
 * 2次曲面　：　円環面(ドーナツ状)
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqTorus extends CqQuadric {
	
	
	private float	m_MajorRadius;	///< Major radius.
	private float	m_MinorRadius;	///< Minor radius.
	private float	m_PhiMin;		///< Min angle about ring.
	private float	m_PhiMax;		///< Max angle about ring.
	private float	m_ThetaMin;		///< Min andle about z axis.
	private float	m_ThetaMax;		///< Max angle about z axis.
	

	//*************************************************
	//**  コンストラクタ定義
	//*************************************************
	
	/**
	 * コンストラクタ。<BR>初期値が与えられなかった場合。
	 */
	public CqTorus()
	{
		this( 1.0f, 0.2f, 0.0f, 360.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param majorradius	トーラスの中心と回転させる円の中心との距離
	 */
	public CqTorus( float majorradius )
	{
		this( majorradius, 0.2f, 0.0f, 360.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が二つ与えられた場合。
	 * 
	 * @param majorradius	トーラスの中心と回転させる円の中心との距離
	 * @param minorradius	回転させる円の半径
	 */
	public CqTorus( float majorradius, float minorradius )
	{
		this( majorradius, minorradius, 0.0f, 360.0f, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が三つ与えられた場合。
	 * 
	 * @param majorradius	トーラスの中心と回転させる円の中心との距離
	 * @param minorradius	回転させる円の半径
	 * @param phimin		回転円の最小円弧角度
	 */
	public CqTorus( float majorradius, float minorradius, float phimin )
	{
		this( majorradius, minorradius, phimin, 360.0f, 0.0f, 360.0f );
	}

	/**
	 * コンストラクタ。<BR>初期値が四つ与えられた場合。
	 * 
	 * @param majorradius	トーラスの中心と回転させる円の中心との距離
	 * @param minorradius	回転させる円の半径
	 * @param phimin		回転円の最小円弧角度
	 * @param phimax		回転円の最大円弧角度
	 */
	public CqTorus( float majorradius, float minorradius, float phimin, float phimax )
	{
		this( majorradius, minorradius, phimin, phimax, 0.0f, 360.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が五つ与えられた場合。
	 * 
	 * @param majorradius	トーラスの中心と回転させる円の中心との距離
	 * @param minorradius	回転させる円の半径
	 * @param phimin		回転円の最小円弧角度
	 * @param phimax		回転円の最大円弧角度
	 * @param thetamin		最小スイープ角度
	 */
	public CqTorus( float majorradius, float minorradius, float phimin, float phimax, float thetamin)
	{
		this( majorradius, minorradius, phimin, phimax, thetamin, 360.0f );
	}

	/**
	 * コンストラクタ。<BR>初期値が全て与えられた場合。
	 * 
	 * @param majorradius	トーラスの中心と回転させる円の中心との距離
	 * @param minorradius	回転させる円の半径
	 * @param phimin		回転円の最小円弧角度
	 * @param phimax		回転円の最大円弧角度
	 * @param thetamin		最小スイープ角度
	 * @param thetamax		最大スイープ角度
	 */
	public CqTorus( float majorradius, float minorradius, float phimin, float phimax, float thetamin, float thetamax)
	{
		m_MajorRadius = majorradius;
		m_MinorRadius = minorradius;
		m_PhiMin = phimin;
		m_PhiMax = phimax;
		m_ThetaMin = thetamin;
		m_ThetaMax = thetamax;
	}
	
	/**
	 * コピーコンストラクタ。
	 * @param From	コピー元
	 */
	public CqTorus( final CqTorus From )
	{
		this.assginment( 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, 0 );
		CqVector3D vB = new CqVector3D( 1, 0, 0 );
		CqVector3D vC = new CqVector3D( 0, 0, 1 );
		CqVector3D vD = new CqVector3D( 0, 0, 0 );
		
		Circle( vA, vB, vC, m_MinorRadius, (float)toRadians( m_PhiMin ), (float)toRadians( m_PhiMax ), curve );
		CqMatrix matRot = new CqMatrix( (float)toRadians( m_ThetaMin ), vC );
		
		for( CqVector3D i : curve )
			i.assignment( matRot.multiply( i ));
		
		CqBound B = new CqBound( RevolveForBound( curve, vD, vC, (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) ) / m_uDiceSize );
		float phi = (float)toRadians( m_PhiMin + ( (float)v * (m_PhiMax - m_PhiMin) ) / m_vDiceSize );
		
		float r = (float)( m_MinorRadius * cos( phi ));
		float z = (float)( m_MinorRadius * sin( phi ));
		
		return ( new CqVector3D((float)((m_MajorRadius + r)*cos(theta)), (float)((m_MajorRadius + 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, net.cellcomputing.himawari.library.types.CqVector3D)
	 */
	public	CqVector3D	DicePoint( int u, int v, CqVector3D Normal )
	{
		float theta = (float)toRadians( m_ThetaMin + ((float)u * (m_ThetaMax - m_ThetaMin)) / m_uDiceSize );
		float phi = (float)toRadians( m_PhiMin + ((float)v * (m_PhiMax - m_PhiMin)) / m_vDiceSize );
		
		float r = (float)( m_MinorRadius * cos( phi ));
		float z = (float)( m_MinorRadius * sin( phi ));
		
		Normal.x( (float)(cos( phi ) * cos( theta )) );
		Normal.y( (float)(cos( phi ) * sin( theta )) );
		Normal.z( (float)(sin( phi )) );
		
		return ( new CqVector3D( (float)(( m_MajorRadius + r ) * cos( theta )), (float)(( m_MajorRadius + r ) * sin( theta )), z ) );
	}
	
	/**
	 *  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_PhiMax + m_PhiMin ) * 0.5);
		float arccent = (float)(( m_ThetaMin + m_ThetaMax ) * 0.5);
		
		CqTorus pNew1 = new CqTorus( this );
		CqTorus pNew2 = new CqTorus( this );
		
		if ( u )
		{
			pNew1.m_ThetaMax = arccent;
			pNew2.m_ThetaMin = arccent;
		}
		else
		{
			pNew1.m_PhiMax = zcent;
			pNew2.m_PhiMin = zcent;
		}
		
		aSplits.add( pNew1 );
		aSplits.add( pNew2 );
		
		return ( 2 );
	}
	
	/**
	 * 代入演算子のオーバーロード
	 * 
	 * @param From	代入するオブジェクト
	 * @return	代入されたオブジェクト
	 */
	public CqTorus assginment( final CqTorus From )
	{
		super.assignment( From );
	    m_MajorRadius = From.m_MajorRadius;
	    m_MinorRadius = From.m_MinorRadius;
	    m_PhiMax = From.m_PhiMax;
	    m_PhiMin = From.m_PhiMin;
	    m_ThetaMin = From.m_ThetaMin;
	    m_ThetaMax = From.m_ThetaMax;

	    return ( this );
	}
	
}
