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

/**
 * 	Sphere quadric GPrim.<br>
 * 	2次曲面　:　球
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqSphere extends CqQuadric {
	
	
	//*************************************************
	//**  コンストラクタ定義
	//*************************************************
	
	/**
	 * コンストラクタ。<BR>初期値が与えられなかった場合。
	 */
	public CqSphere()
	{
		this( 1.0f, -1.0f, 1.0f, 0.0f, 360.0f);
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param radius	球の半径
	 */
	public CqSphere( float radius )
	{
		this( radius, -1.0f, 1.0f, 0.0f, 360.0f);
	}

	/**
	 * コンストラクタ。<BR>初期値が2つ与えられた場合。
	 * 
	 * @param radius	球の半径
	 * @param zmin	Z方向のの最小値
	 */
	public CqSphere( float radius, float zmin)
	{
		this( radius, zmin, 1.0f, 0.0f, 360.0f);
	}
	
	/**
	 * コンストラクタ。<BR>初期値が3つ与えられた場合。
	 * 
	 * @param radius	球の半径
	 * @param zmin	Z方向の最小値
	 * @param zmax	Z方向の最大値
	 */
	public CqSphere( float radius, float zmin, float zmax)
	{
		this( radius, zmin, zmax, 0.0f, 360.0f);
	}
	
	/**
	 * コンストラクタ。<BR>初期値が4つ与えられた場合。
	 * 
	 * @param radius	球の半径
	 * @param zmin	Z方向の最小値
	 * @param zmax	Z方向の最大値
	 * @param thetamin	最小スイープ角度
	 */
	public CqSphere( float radius, float zmin, float zmax, float thetamin)
	{
		this( radius, zmin, zmax, thetamin, 360.0f);
	}
	
	/**
	 * コンストラクタ。<BR>初期値が与えられた場合。
	 * 
	 * @param radius	球の半径
	 * @param zmin	Z方向の最小値
	 * @param zmax	Z方向の最大値
	 * @param thetamin	最小スイープ角度
	 * @param thetamax	最大スイープ角度
	 */
	public CqSphere( float radius, float zmin, float zmax, float thetamin, float thetamax )
	{
		m_Radius = radius;
		m_ThetaMin = thetamin;
		m_ThetaMax = thetamax;
		
	    // Sanity check the values, while ensuring we keep the same signs.
		//　値が有効かどうかをチェック。
		float frad = abs(m_Radius); 
	
	    if( abs(zmin) > frad )	zmin = frad * ( (zmin < 0)? -1 : 1);
	    if( abs(zmin) > frad )	zmin = frad * ( (zmin < 0)? -1 : 1);
	    m_PhiMin = (float)asin( zmin / m_Radius );
	    m_PhiMax = (float)asin( zmax / m_Radius );
	}
	
	/**
	 * コピーコンストラクタ
	 * @param From	コピー元
	 */
	public CqSphere( final CqSphere From )
	{
		this.assginment( From );
	}
	
	/**
	 * デストラクタ。
	 * 実装なし。　
	 * @see net.cellcomputing.himawari.library.CqSurface#destruct()
	 */
	public void destruct(){
		super.destruct();
	};
	
	
	//***********************************************
	//* メソッド定義
	//***********************************************
	/**
	 * バウンディングボックスの取得
	 * Get the geometric bound of this GPrim.
	 * @return AdjustBoundForTransformationMotion( B )
	 * @see net.cellcomputing.himawari.library.IqSurface#Bound()
	 */
	@Override
	public 	CqBound	Bound()
	{
		STLVector<CqVector3D> curve = new STLVector<CqVector3D>( CqVector3D.class );
	    CqVector3D vA = new CqVector3D( 0, 0, 0 );
	    CqVector3D vB = new CqVector3D( 1, 0, 0 );
	    CqVector3D vC = new CqVector3D( 0, 0, 1 );
	    
	    //描画する円のデータを取得
	    Circle( vA, vB, vC, m_Radius, m_PhiMin, 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, vA, 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	u方向の表面座標	Float surface paramter in u.
	 * @param	v	v方向の表面座標	Float surface paramter in v.
	 * @see net.cellcomputing.himawari.library.CqQuadric#DicePoint(int, int)
	 */
	public 	CqVector3D	DicePoint( int u, int v )
	{
		float phi = m_PhiMin + ( ( float ) v * ( m_PhiMax - m_PhiMin ) ) / m_vDiceSize;

	    float cosphi = (float)cos( phi );
	    float theta = (float)toRadians( m_ThetaMin + ( ( float ) u * ( m_ThetaMax - m_ThetaMin ) ) / m_uDiceSize );

	    return ( new CqVector3D((float)( m_Radius * cos(theta) * cosphi), (float)(m_Radius * sin(theta) * cosphi), (float)(m_Radius * sin(phi) ) ) );

	}
	
	
	/**
	 * パラメータを通る面から表面上の点を取得。
	 * Get a point on the surface indexed by the surface paramters passed.
	 * @param	u	u方向の表面座標	Float surface paramter in u.
	 * @param	v	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 )
	{
		CqVector3D	p = DicePoint( u, v );
	    Normal.assignment( p );
	    Normal.Unit();
	    return ( p );
	}
	
	/**
	 * 法線が通るかどうかを返す。
	 * @see net.cellcomputing.himawari.library.CqSurface#CanGenerateNormals()
	 */
	public  boolean	CanGenerateNormals() 
	{
		return ( true );
	}
	
	
	/**
	 * GPrimをNURBS円の表面へ分割する。 臨時的に実装を行い、小さな二次曲面に分ける。
	 * Split this GPrim into a NURBS surface. Temp implementation, should split into smalled quadrics.
	 * @param	aSplits	分割する物体
	 * @param	u	
	 * @see net.cellcomputing.himawari.library.IqSurface#PreSubdivide(java.util.Vector, boolean)
	 */
	public 	int PreSubdivide( STLVector<CqBasicSurface> aSplits, boolean u )
	{
		float phicent = (float)(( m_PhiMin + m_PhiMax ) * 0.5);
	    float arccent = (float)(( m_ThetaMin + m_ThetaMax ) * 0.5);

	    CqSphere pNew1 = new CqSphere( this );
	    CqSphere pNew2 = new CqSphere( this );

	    if ( u )
	    {
	        pNew1.m_ThetaMax = arccent;
	        pNew2.m_ThetaMin = arccent;
	    }
	    else
	    {
	        pNew1.m_PhiMax = phicent;
	        pNew2.m_PhiMin = phicent;
	    }

	    aSplits.add( pNew1 );
	    aSplits.add( pNew2 );

	    return ( 2 );
	}
	
	
	/**
	 * 
	 * 代入演算子のオーバーロード
	 * 
	 * @param From	代入するオブジェクト
	 * @return	代入されたオブジェクト
	 */
	public CqSphere	assginment( final CqSphere From )
	{
		super.assignment( From );
	    m_Radius = From.m_Radius;
	    m_PhiMin = From.m_PhiMin;
	    m_PhiMax = From.m_PhiMax;
	    m_ThetaMin = From.m_ThetaMin;
	    m_ThetaMax = From.m_ThetaMax;

	    return ( this );
	}
	
	private float	m_Radius;		///< 球の半径	Radius.
	private float	m_PhiMin;		///< X方向の最小角度	Min angle about x axis.
	private float	m_PhiMax;		///< X方向の最大角度	Max angle about x axis.
	private float	m_ThetaMin;		///< 最小スイープ角度	Min angle about z axis.
	private float	m_ThetaMax;		///< 最大スイープ角度	Max angle about z axis.
	
	/*
	 float	m_ZMin;			///< Z軸上の最小値		Min value on z axis.
	 float	m_ZMax;			///< Z軸上の最大値		Max value on z axis.
	 */
}
