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

/**
 * Hyperboloid quadric GPrim.
 * 二次曲面　：　双曲面
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqHyperboloid extends CqQuadric {

	private CqVector3D	m_Point1 = new CqVector3D();		///< Start point of line to revolve.
	private CqVector3D	m_Point2 = new CqVector3D();		///< End point of line to revolve.
	private float	m_ThetaMin;		///< Min angle about z axis.
	private float	m_ThetaMax;		///< Max angle about z axis.


	//*************************************************
	//**  コンストラクタ定義
	//*************************************************
	
	/**
	 * コンストラクタ。<BR>初期値が与えられなかった場合。
	 */
	public CqHyperboloid( )
	{
		this( new CqVector3D( 0.0f, 0.0f, 0.0f ), new CqVector3D( 0.0f, 0.0f, 1.0f ), 0.0f, 1.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が一つ与えられた場合。
	 * 
	 * @param point1	曲面を生成する線分の点
	 */
	public CqHyperboloid( CqVector3D point1 )
	{
		this( point1, new CqVector3D( 0.0f, 0.0f, 1.0f ), 0.0f, 1.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が二つ与えられた場合。
	 * 
	 * @param point1	曲面を生成する線分の点
	 * @param point2	曲面を生成する線分の点
	 */
	public CqHyperboloid( CqVector3D point1, CqVector3D point2 )
	{
		this( point1, point2, 0.0f, 1.0f );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が三つ与えられた場合。
	 * 
	 * @param point1	曲面を生成する線分の点
	 * @param point2	曲面を生成する線分の点
	 * @param thetamin　　最小スイープ角度
	 */
	public CqHyperboloid( CqVector3D point1, CqVector3D point2, float thetamin )
	{
		this( point1, point2, thetamin, 1.0f  );
	}
	
	/**
	 * コンストラクタ。<BR>初期値が全て与えられた場合。
	 * 
	 * @param point1	曲面を生成する線分の点
	 * @param point2	曲面を生成する線分の点
	 * @param thetamin	最小スイープ角度
	 * @param thetamax	最大スイープ角度
	 */
	public CqHyperboloid( CqVector3D point1, CqVector3D point2, float thetamin, float thetamax )
	{
		m_Point1.assignment( point1 );
		m_Point2.assignment( point2 );
		m_ThetaMin = thetamin ;
		m_ThetaMax = thetamax ;
	}
	
	/**
	 * コピーコンストラクタ
	 * 
	 * @param From	コピー元
	 */
	public CqHyperboloid( final CqHyperboloid 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.IqSurkface#Bound()
	 */
	public	CqBound	Bound()
	{
	    STLVector<CqVector3D> curve = new STLVector<CqVector3D>( CqVector3D.class );
	    CqVector3D vA = new CqVector3D( 0, 0, 0 );
	    CqVector3D vB = new CqVector3D( 0, 0, 1 );
	    
	    curve.add( new CqVector3D( m_Point1 ));
	    curve.add( new CqVector3D( m_Point2 ));
	    CqMatrix matRot = new CqMatrix( (float)toRadians( m_ThetaMin ), vB );
	    
	    for ( CqVector3D i : curve )
	        i.assignment( matRot.multiply( i ));
	    
	    CqBound	B = new CqBound( RevolveForBound( curve, vA, vB, (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 vv = ( v ) / (float)m_vDiceSize ;
	
		CqVector3D p = new CqVector3D( m_Point1.mul( 1.0f - vv ).add( m_Point2.mul( vv )) );
		return new CqVector3D((float)( p.x * cos( theta )) - (float)( p.y * sin( theta )), (float)( p.x * sin( theta )) + (float)( p.y * cos( theta )), p.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 + ((float)u * ( m_ThetaMax - m_ThetaMin )) / (float)m_uDiceSize );
	    float sin_theta = (float) sin( theta );
	    float cos_theta = (float) cos( theta );
	    float vv = (float)v  / (float)m_vDiceSize ;
	
	    CqVector3D p = new CqVector3D( m_Point1.mul( 1.0f - vv ).add( m_Point2.mul( vv )) );

	    // Calculate the normal vector - this is a bit tortuous, and uses the general
	    //  formula for the normal to a surface that is specified by two parametric
	    //  parameters.

	    // Calculate a vector, a, of derivatives of coordinates w.r.t. u
	    float dxdu = -p.x * m_ThetaMax * sin_theta - p.y * m_ThetaMax * cos_theta;
	    float dydu =  p.x * m_ThetaMax * cos_theta - p.y * m_ThetaMax * sin_theta;
	    float dzdu =  0.0f;
	    CqVector3D a = new CqVector3D( dxdu, dydu, dzdu );

	    // Calculate a vector, b, of derivatives of coordinates w.r.t. v
	    CqVector3D p2p1 = m_Point2.sub( m_Point1 );
	    float dxdv = p2p1.x * cos_theta  -  p2p1.y * sin_theta;
	    float dydv = p2p1.x * sin_theta  +  p2p1.y * cos_theta;
	    float dzdv = p2p1.z;
	    CqVector3D b = new CqVector3D( dxdv, dydv, dzdv );

	    // The normal vector points in the direction of: a x b
	    Normal.assignment( a.mod( b ));
	    Normal.Unit();

	    // Return the point on the surface.
	    return ( new CqVector3D( p.x * cos_theta - p.y * sin_theta, p.x * sin_theta + p.y * cos_theta, p.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 arccent = (float)(( m_ThetaMin + m_ThetaMax ) * 0.5 );
	    CqVector3D midpoint = new CqVector3D(( m_Point1.add( m_Point2 )).div( 2.0f ));

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

	    if ( u )
	    {
	        pNew1.m_ThetaMax = arccent;
	        pNew2.m_ThetaMin = arccent;
	    }
	    else
	    {
	        pNew1.m_Point2.assignment( midpoint );
	        pNew2.m_Point1.assignment( midpoint );
	    }

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

	    return ( 2 );
	}

	public CqHyperboloid assignment( final CqHyperboloid From )
	{
		super.assignment( From );
		m_Point1.assignment( From.m_Point1 );
		m_Point2.assignment( From.m_Point2 );;
		m_ThetaMin = From.m_ThetaMin;
		m_ThetaMax = From.m_ThetaMax;
		
		return ( this );
	}

}
