// 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.accessory.primitive.p_float;
import net.cellcomputing.himawari.library.types.CqMatrix;
import net.cellcomputing.himawari.library.types.CqVector3D;
import static net.cellcomputing.himawari.library.EqIntIndex.*;
import static net.cellcomputing.himawari.library.CqStats.*;
import static net.cellcomputing.himawari.library.EqSplitDir.*;
import static net.cellcomputing.himawari.library.RiGlobal.*;
import static net.cellcomputing.himawari.library.types.PublicFunctions.*;
import static net.cellcomputing.himawari.library.Float_h.*;
import static net.cellcomputing.himawari.library.EqEnvVars.*;
import static java.lang.StrictMath.*;


/**
 * 
 * class CqQuadric
 *	Abstract base class from which all quadric primitives are defined.
 *	すべての二次曲面の基関数が定義される抽象的な基底クラス。
 * 
 * @author NTT DATA Corporation
 */
abstract public strictfp class CqQuadric extends CqSurface
{
	public static float	RI_PI = 3.14159265359f; //定義場所はここでよいか？
	public static int TOOLARGEQUADS = 10000;
	public static int ESTIMATEGRIDSIZE = 8;

	
	/**
	 * コンストラクタ
	 */
	public CqQuadric(){
		
		m_uDiceSize = 0;
		m_vDiceSize = 0;
	    STATS_INC( GPR_quad );
	}
	
	/**
	 * デストラクタ 
	 */
	public void destruct(){
		super.destruct();
	};
	
	
	
	/**
	 * 
	 * 定義・参照なし
	 * 
	 * @param B
	 * @return	null
	 */
	public CqBound MotionBound(CqBound	B){
		//定義なし
		return null;
	};
	
	
	/**
	 * Transform the quadric primitive by the specified matrix.
	 *	指定行列から元の二次曲面を変える。
	 * @see net.cellcomputing.himawari.library.IqSurface#Transform(net.cellcomputing.himawari.library.types.CqMatrix, net.cellcomputing.himawari.library.types.CqMatrix, net.cellcomputing.himawari.library.types.CqMatrix)
	 */
	public void	Transform( final CqMatrix matTx, final CqMatrix matITTx, final CqMatrix matRTx)
	{
		Transform( matTx, matITTx, matRTx, 0);
	}
	
	
	/**
	 * Transform the quadric primitive by the specified matrix.
	 *  指定行列から元の二次曲面を変える。
	 * @see net.cellcomputing.himawari.library.IqSurface#Transform(net.cellcomputing.himawari.library.types.CqMatrix, net.cellcomputing.himawari.library.types.CqMatrix, net.cellcomputing.himawari.library.types.CqMatrix, int)
	 */
	public void	Transform( final CqMatrix matTx, final CqMatrix matITTx, final CqMatrix matRTx, int iTime)
	{
		m_matTx = m_matTx.assignMul( matTx );
		m_matITTx.assignMul( matITTx );
	}
	
	
	/**
	 * Get the number of uniform values for this GPrim.
	 * 分割されたプリミティブと同値である数を取得。
	 * @see net.cellcomputing.himawari.library.IqSurface#cUniform()
	 */
	@Override //from IqSurface
	public int cUniform()
	{
		return ( 1 );
	}
	
	
	/**
	 * Get the number of varying values for this GPrim.
	 * 分割されたプリミティブと異なる値の数を取得
	 * @see net.cellcomputing.himawari.library.IqSurface#cVarying()
	 */
	@Override //from IqSurface
	public int	cVarying()
	{
		return ( 4 );
	}
	
	
	/**
	 * Get the number of vertex values for this GPrim.
	 * 分割されたプリミティブの頂点値の数の取得。
	 * @see net.cellcomputing.himawari.library.IqSurface#cVertex()
	 */
	@Override //from IqSurface
	public int	cVertex()
	{
		return ( 4 );
	}
	
	
	/**
	 * Get the number of facevarying values for this GPrim.
	 * 分割されたプリミティブと表面の異なる値の数を取得。
	 * @see net.cellcomputing.himawari.library.IqSurface#cFaceVarying()
	 */
	@Override //from IqSurface
	public int	cFaceVarying()
	{
		// TODO Must work out what this value should be.
		// この値は計算して求めなければならないもの・・・らしい。
		return ( 1 );
	}
	
	
	/**
	 * Determine whether the quadric is suitable for dicing. 
	 * 二次曲面がダイスに適合するかどうかを決定する。
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#Diceable()
	 */
	@Override //from IqSurface
	public boolean Diceable()
	{
		// If the cull check showed that the primitive cannot be diced due to crossing 
		// the e and hither planes, then we can return immediately.
		if ( !m_fDiceable )
			return ( false );
		
		int toomuch = (int)EstimateGridSize();  
		
		m_SplitDir = ( m_uDiceSize > m_vDiceSize ) ? new EqSplitDir(SplitDir_U) : new EqSplitDir(SplitDir_V);
		
		float gs = 16.0f;
		final float[] poptGridSize = QGetRenderContext().optCurrent().GetFloatOption( "System", "SqrtGridSize" );
		
		if( null != poptGridSize )
			gs = poptGridSize[0];
		
		if (toomuch > TOOLARGEQUADS) 
			return ( false );
		
		//マイクロポリゴン化するには、16未満の数値でなければならない。
		if ( m_uDiceSize > gs) 
			return ( false );
		if ( m_vDiceSize > gs) 
			return ( false );
		
		return ( true );

	}
	

	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#IsMotionBlurMatch(net.cellcomputing.himawari.library.CqBasicSurface)
	 */
	@Override //from IqSurface
	public boolean IsMotionBlurMatch( CqBasicSurface pSurf )
	{
		return false;
	}
	
	
	/**
	 * Estimate the size of the micropolygrid required to dice 
	 * this GPrim to a suitable shading rate.
	 * 適当な　shading rate　でプリミティブをダイシングするために、必要なマイクロポリゴンのサイズの見積もりをする
	 * 
	 * @return	m_uDiceSize * m_vDiceSize	マイクロポリゴンのサイズ
	 */
	public long	EstimateGridSize()
	{
		float maxusize, maxvsize;
		maxusize = maxvsize = 0;
		
		CqMatrix matTx = QGetRenderContext().matSpaceToSpace( "camera", "raster", new CqMatrix(), new CqMatrix(), QGetRenderContext().Time() ).multiply( m_matTx );
		
		//マイクロポリゴン化へ向け、まずグリッドのサイズで定義
		m_uDiceSize = ESTIMATEGRIDSIZE;
		m_vDiceSize = ESTIMATEGRIDSIZE;
		
		float udist, vdist;
		CqVector3D p = new CqVector3D();;
		CqVector3D pum1 = new CqVector3D();
		CqVector3D[] pvm1 = new CqVector3D[ ESTIMATEGRIDSIZE ];	
		for(int i = 0; i < ESTIMATEGRIDSIZE; i++){
			pvm1[ i ] = new CqVector3D();
		}
		
		int v, u;
		for ( v = 0; v <= ESTIMATEGRIDSIZE; v++ )
		{
			for ( u = 0; u <= ESTIMATEGRIDSIZE; u++ )
			{
				p = DicePoint( u, v );
				p.assignment( matTx.multiply( p ));
				
				// If we are on row two or above, calculate the mp size.
				//　列において2以上の場合、マイクロポリゴンのサイズを計算
				if ( v >= 1 && u >= 1 )
				{
					udist = ( p.x - pum1.x ) * ( p.x - pum1.x ) +
					( p.y - pum1.y ) * ( p.y - pum1.y );
					vdist = ( pvm1[ u - 1 ].x - pum1.x ) * ( pvm1[ u - 1 ].x - pum1.x ) +
					( pvm1[ u - 1 ].y - pum1.y ) * ( pvm1[ u - 1 ].y - pum1.y );
					
					maxusize = max( maxusize, udist );
					maxvsize = max( maxvsize, vdist );
				}
				if ( u >= 1 ) 
					pvm1[ u - 1 ] = pum1;
				pum1 = p;
			}
		}
		maxusize = (float)sqrt( maxusize );
		maxvsize = (float)sqrt( maxvsize );
		
		float ShadingRate = pAttributes().GetFloatAttribute( "System", "ShadingRateSqrt" ) [ 0 ];
		if( ShadingRate>0 ) {
			m_uDiceSize = (int)ceil( ESTIMATEGRIDSIZE * maxusize / ( ShadingRate ) );
			m_vDiceSize = (int)ceil( ESTIMATEGRIDSIZE * maxvsize / ( ShadingRate ) );
		}
		else{
			m_uDiceSize = 1;
			m_vDiceSize = 1;
		}
		
		// Ensure power of 2 to avoid cracking
		final int[] binary = pAttributes().GetIntegerAttribute( "dice", "binary" );
		
		if ( binary != null && binary[0] != 0 )
		{
			m_uDiceSize = (int)CEIL_POW2( m_uDiceSize );
			m_vDiceSize = (int)CEIL_POW2( m_vDiceSize );
		}
		
		return  (long) m_uDiceSize * m_vDiceSize;
	
	}
	
	
	/**
	 * Create the points which make up a NURBS circle control hull, for use during boundary
	 * 	generation.
	 * バウンディングされている場所のNURB円のコントロールポイントを作成
	 * 
	 * @param	O	円の原型	Origin of the circle.
	 * @param	X	円を形成する平面のX軸	X axis of the plane to generate the circle in.
	 * @param	Y	円を形成する平面のY軸	Y axis of the plane to generate the circle in.
	 * @param	r	円の半径	Radius of the circle.
	 * @param	as	円の開始角度	Start angle of the circle.
	 * @param	ae	円の終了角度	End angle of the circle.
	 * @param	points	円を形成する点を格納する		Storage for the points of the circle.
	 */
	public void	Circle( final CqVector3D O, final CqVector3D X, final CqVector3D Y, float r, float as, float ae, STLVector<CqVector3D> points )
	{
		float theta, angle, dtheta;
		int narcs; //Unsigned int
		
		while ( ae < as ){    //角度の値調整
			ae += 2 * RI_PI;
		}
		theta = ae - as;
		
//		if ( theta <= RI_PIO2 )
//			narcs = 1;
//		else
//		{
//			if ( theta <= RI_PI )
//				narcs = 2;
//			else
//			{
//				if ( theta <= 1.5 * RI_PI )
//					narcs = 3;
//				else	
		narcs = 4;		
//			}
//		}
		
		dtheta = theta / (float)( narcs );
		int n = 2 * narcs + 1;				// n control points ;
		//必ずn= 9
		CqVector3D PO = new CqVector3D();
		CqVector3D TO = new CqVector3D();
		CqVector3D P2 = new CqVector3D();
		CqVector3D T2 = new CqVector3D();
		CqVector3D P1 = new CqVector3D();
		
		PO.assignment( O.add( X.mul( (float)(r*cos(as)))).add( Y.mul( (float)(r*sin(as)))) ); 
		TO.assignment( X.mul( (float)-sin( as )).add( Y.mul( (float)cos( as ))) );
		
		points.resize( n );
		
		points.get( 0 ).assignment(PO);
		int index = 0;
		angle = as;
		
		int i;
		for ( i = 1; i <= narcs; i++ )
		{
			angle += dtheta;
			P2.assignment( O.add( X.mul( (float)(r*cos(angle)))).add( Y.mul( (float)(r*sin(angle)))) );
			
			points.get( index + 2).assignment(P2);
			T2.assignment( X.mul( (float)-sin(angle) ).add( Y.mul( (float)cos(angle) ) ) );
			
			IntersectLine( PO, TO, P2, T2, P1 );
			points.get( index + 1 ).assignment(P1);
			index += 2;
			if ( i < narcs )
			{
				PO.assignment( P2 );
				TO.assignment( T2 );
			}
		}
	}
	
	
	/**
	 * バウンディングボックスの作成
	 * 
	 * @param 	profile	物体の頂点データ
	 * @param 	S	
	 * @param 	Tvec
	 * @param 	theta	
	 * @return	bound	作成されたバウンディングボックスの情報
	 */
	public CqBound	RevolveForBound( final STLVector<CqVector3D> profile, final CqVector3D S, final CqVector3D Tvec, float theta )
	{		
		 CqBound bound = new CqBound( FLT_MAX, FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX );
		 
		 float angle, dtheta;
		 int narcs;
		 int i, j;
		 
		 if ( abs( theta ) > ( 2.0f * RI_PI ) )
		 {
			 if ( theta < 0 )
				 theta = -( 2.0f * RI_PI );
			 else
				 theta = 2.0f * RI_PI;
		 }
		 
		 /*	if ( fabs( theta ) <= RI_PIO2 )
		  narcs = 1;
		  else
		  {
		  if ( fabs( theta ) <= RI_PI )
		  narcs = 2;
		  else
		  {
		  if ( fabs( theta ) <= 1.5 * RI_PI )
		  narcs = 3;
		  else*/
		 narcs = 4;
		 /*		}
		  }*/
		 
		 dtheta = theta / (float)( narcs );
		 
		 STLVector<Float> cosines = new STLVector<Float>( Float.class, narcs + 1 );
		 STLVector<Float> sines = new STLVector<Float>( Float.class, narcs + 1 );
		 
		 angle = 0.0f;
		 for ( i = 1; i <= narcs; i++ )
		 {
			 angle = dtheta * (float)( i );
			 cosines.set( i, (float)cos( angle ));
			 sines.set( i, (float)sin( angle ));
		 }
		 
		 
		 CqVector3D P0 = new CqVector3D();
		 CqVector3D T0 = new CqVector3D();
		 CqVector3D P2 = new CqVector3D();
		 CqVector3D T2 = new CqVector3D();
		 CqVector3D P1 = new CqVector3D();
		 
		 for ( j = 0; j < profile.size(); j++ )
		 {
			 CqVector3D O = new CqVector3D();
			 CqVector3D pj = new CqVector3D( profile.get( j ) );
			 
			 ProjectToLine( S, Tvec, pj, O );
			 CqVector3D X = new CqVector3D();
			 CqVector3D Y = new CqVector3D();
			 X.assignment( pj.sub( O ) );
			 
			 float r = X.Magnitude();
			 
			 if ( r < 1e-7 )
			 {
				 bound.Encapsulate( O );
				 continue;
			 }
			 
			 X.Unit();
			 Y.assignment( Tvec.mod( X ) );
			 Y.Unit();
			 
			 P0.assignment( profile.get( j ) );
			 bound.Encapsulate( P0 );
				
			 T0.assignment( Y );
			 for ( i = 1; i <= narcs; ++i )
			 {
				 angle = dtheta * (float)( i );
				 P2.assignment( O.add( X.mul( r * cosines.get( i ) ) ).add( Y.mul( r * sines.get( i ) ) ) );
				 bound.Encapsulate( P2 );
						 
				 T2.assignment( X.mul( -sines.get( i ) ).add( Y.mul( cosines.get( i ))) );
				 
				 IntersectLine( P0, T0, P2, T2, P1 );
				 bound.Encapsulate( P1 );
						
				 if ( i < narcs )
				 {
					 P0.assignment( P2 );
					 T0.assignment( T2 );
				 }
			 }
		 }
		 return ( bound );
	}


	/**
	 * Dice the quadric filling in as much information on the grid as possible.
	 * できるだけ多くのグリッド情報で二次曲面のダイシングを行う。
	 * @see net.cellcomputing.himawari.library.IqSurface#DiceAll(net.cellcomputing.himawari.library.CqMicroPolyGrid)
	 */
	@Override 
	public int	DiceAll( CqMicroPolyGrid pGrid )
	{
		
		int lUses = Uses();
		int lDone = 0;
		
		CqVector3D	P = new CqVector3D();
		CqVector3D	N = new CqVector3D();
		int v, u;
		
		CqParameterTyped<p_float, p_float> ps = s();
		CqParameterTyped<p_float, p_float> pt = t();
		CqParameterTyped<p_float, p_float> pu = this.u();
		CqParameterTyped<p_float, p_float> pv = this.v();
		CqParameterTyped<p_float, p_float> pst = ( CqParameterTyped<p_float, p_float> )FindUserParam( "st" );
		
		p_float s0 = new p_float(), s1 = new p_float(), s2 = new p_float(), s3 = new p_float();
		if( USES( lUses, EnvVars_s ) && null != pGrid.pVar(EnvVars_s) && bHasVar(EnvVars_s) )
		{
			if( pst != null )
			{
				s0.value = ((p_float)pst.pValue_get( 0 ,0)).value;
				s1.value = ((p_float)pst.pValue_get( 1 ,0)).value;
				s2.value = ((p_float)pst.pValue_get( 2 ,0)).value;
				s3.value = ((p_float)pst.pValue_get( 3 ,0)).value;
			}
			else if( ps != null )
			{
				s0.value = ((p_float)ps.pValue_get( 0 ,0)).value;
				s1.value = ((p_float)ps.pValue_get( 1 ,0)).value;
				s2.value = ((p_float)ps.pValue_get( 2 ,0)).value;
				s3.value = ((p_float)ps.pValue_get( 3 ,0)).value;
			}
			
//			DONE( lDone, EnvVars_s );
			lDone = lDone | (0x00000001 << EnvVars_s);
		}
		
		p_float t0 = new p_float(), t1 = new p_float(), t2 = new p_float(), t3 = new p_float();
		if( USES( lUses, EnvVars_t ) && null != pGrid.pVar(EnvVars_t) && bHasVar(EnvVars_t) )
		{
			if( pst != null )
			{
				t0.value = ((p_float)pst.pValue_get( 0 ,1)).value;
				t1.value = ((p_float)pst.pValue_get( 1 ,1)).value;
				t2.value = ((p_float)pst.pValue_get( 2 ,1)).value;
				t3.value = ((p_float)pst.pValue_get( 3 ,1)).value;
			}
			else if( pt != null )
			{
				t0.value = ((p_float)pt.pValue_get( 0 ,0)).value;
				t1.value = ((p_float)pt.pValue_get( 1 ,0)).value;
				t2.value = ((p_float)pt.pValue_get( 2 ,0)).value;
				t3.value = ((p_float)pt.pValue_get( 3 ,0)).value;
			}
			
//			DONE( lDone, EnvVars_t );
			lDone = lDone | (0x00000001 << EnvVars_t);
		}
		
		p_float u0 = new p_float(), u1 = new p_float(), u2 = new p_float(), u3 = new p_float();
		if( USES( lUses, EnvVars_u ) && null != pGrid.pVar(EnvVars_u) && bHasVar(EnvVars_u) )
		{
			u0.value = ((p_float)pu.pValue_get( 0 ,0)).value;
			u1.value = ((p_float)pu.pValue_get( 1 ,0)).value;
			u2.value = ((p_float)pu.pValue_get( 2 ,0)).value;
			u3.value = ((p_float)pu.pValue_get( 3 ,0)).value;
			
//			DONE( lDone, EnvVars_u );
			lDone = lDone | (0x00000001 << EnvVars_u);
		}
		
		p_float v0 = new p_float(), v1 = new p_float(), v2 = new p_float(), v3 = new p_float();
		if( USES( lUses, EnvVars_v ) && null != pGrid.pVar(EnvVars_v) && bHasVar(EnvVars_v) )
		{
			v0.value = ((p_float)pv.pValue_get( 0 ,0)).value;
			v1.value = ((p_float)pv.pValue_get( 1 ,0)).value;
			v2.value = ((p_float)pv.pValue_get( 2 ,0)).value;
			v3.value = ((p_float)pv.pValue_get( 3 ,0)).value;
			
//			DONE( lDone, EnvVars_v );
			lDone = lDone | (0x00000001 << EnvVars_v);
		}
		
		/*    
			 if( USES( lUses, EnvVars_P ) && null != pGrid->pVar(EnvVars_P) )
			 DONE( lDone, EnvVars_P );
			 if( USES( lUses, EnvVars_Ng ) && null != pGrid->pVar(EnvVars_Ng) )
			 {
			 DONE( lDone, EnvVars_Ng );
			 pGrid->SetbGeometricNormals( TqTrue );
			 }
		 */
		
		float du = (float)( 1.0 / uDiceSize() );
		float dv = (float)( 1.0 / vDiceSize() );
		
		for ( v = 0; v <= vDiceSize(); v++ )
		{
			float vf = v * dv;
			for ( u = 0; u <= uDiceSize(); u++ )
			{
				float uf = u * du;
				int igrid = ( v * ( uDiceSize() + 1 ) ) + u;
				if( USES( lUses, EnvVars_P ) && null != pGrid.pVar(EnvVars_P) )
				{
					if( USES( lUses, EnvVars_Ng ) && null != pGrid.pVar(EnvVars_Ng) )
					{
						P = DicePoint( u, v, N );
						pGrid.pVar(EnvVars_P).SetPoint( m_matTx.multiply( P ), igrid );
						pGrid.pVar(EnvVars_Ng).SetNormal( m_matITTx.multiply( N ), igrid );
						
					}
					else
					{
						P = DicePoint( u, v );
						pGrid.pVar(EnvVars_P).SetPoint( m_matTx.multiply( P ), igrid );
					}
				}
				if( USES( lUses, EnvVars_s ) && null != pGrid.pVar(EnvVars_s) && bHasVar(EnvVars_s) )
				{
					p_float _s = BilinearEvaluate( s0, s1, s2, s3, uf, vf, p_float.class);
					pGrid.pVar(EnvVars_s).SetFloat( _s.value, igrid );
//					pGrid.pVar(EnvVars_s).SetFloat( _s, igrid );
				}
				if( USES( lUses, EnvVars_t ) && null != pGrid.pVar(EnvVars_t) && bHasVar(EnvVars_t) )
				{
					p_float _t = BilinearEvaluate( t0, t1, t2, t3, uf, vf, p_float.class );
					pGrid.pVar(EnvVars_t).SetFloat( _t.value, igrid );
				}
				if( USES( lUses, EnvVars_u ) && null != pGrid.pVar(EnvVars_u) && bHasVar(EnvVars_u) )
				{
					p_float _u = BilinearEvaluate( u0, u1, u2, u3, uf, vf, p_float.class );
					pGrid.pVar(EnvVars_u).SetFloat( _u.value, igrid );
				}
				if( USES( lUses, EnvVars_v ) && null != pGrid.pVar(EnvVars_v) && bHasVar(EnvVars_v) )
				{
					p_float _v = BilinearEvaluate( v0, v1, v2, v3, uf, vf, p_float.class );
					pGrid.pVar(EnvVars_v).SetFloat( _v.value, igrid );
				}
			}
		}
		return( lDone );
	}

	
	/**
	 * Pure virtual, get a surface point.
	 * 純仮想関数。<br>
	 * 表面の点を取得。
	 * 
	 * @param	u	表面u座標	Surface u coordinate.
	 * @param	v	表面v座標	Surface v coordinate.
	 * @return	3D	指定されたu,v座標に表面への点を表すベクトル<br>	vector representing the surface point at the specified u,v coordniates.
	 */
	public abstract CqVector3D DicePoint( int u, int v );
	
	
	/** 
	 * Pure virtual, get a surface point and normal.
	 * 純仮想関数。<br>
	 * 表面の点と垂線を取得。
	 * 
	 * @param	u	表面u座標	Surface u coordinate.
	 * @param	v	表面v座標	Surface v coordinate.
	 * @param	Normal	表面に対する垂線	Storage for the surface normal.
	 * @return	3D	指定されたu,v座標に表面への点を表すベクトル<br>	vector representing the surface point at the specified u,v coordniates.
	 */
	public abstract CqVector3D DicePoint( int u, int v, CqVector3D Normal );
	
	/** 
	 * 代入演算子。演算子のオーバーロード。
	 * 
	 * @param From	代入するオブジェクト
	 * @return	代入されたオブジェクト
	 */
	public CqQuadric assignment( final CqQuadric From )
	{
		super.assignment( From );
		m_matTx.assignment( From.m_matTx );
		m_matITTx.assignment( From.m_matITTx );
		m_uDiceSize = From.m_uDiceSize;
		m_vDiceSize = From.m_vDiceSize;

		return ( this );
	}
	
	// Derived from CqSurface
	/**
	 * Dice the quadric into a grid of MPGs for rendering.
	 * レンダリングのために、二次曲面のマイクロポリゴン化を行う　
	 *	@see net.cellcomputing.himawari.library.IqSurface#NaturalDice(net.cellcomputing.himawari.library.CqParameter, int, int, net.cellcomputing.himawari.library.IqShaderData)
	 */
	public void	NaturalDice( CqParameter pParameter, int uDiceSize, int vDiceSize, IqShaderData pData )
	{
		// Special case for "P", else normal bilinear dice for all others.
		int hash = pData.strName().hashCode();
		if ( hash == RIH_P )
		{
			CqVector3D	P = new CqVector3D();
			int v, u;
			for ( v = 0; v <= vDiceSize; v++ )
			{
				for ( u = 0; u <= uDiceSize; u++ )
				{
					int igrid = ( v * ( uDiceSize + 1 ) ) + u;
					P.assignment( DicePoint( u, v ) );
					pData.SetPoint( m_matTx.multiply( P ), igrid );
				}
			}
		}
		else
			NaturalDice( pParameter, uDiceSize, vDiceSize, pData );
	}
	
	
	/**
	 * Generate and store the geometric normals for this quadric.
	 * 二次曲面のための法線を作成し格納
	 * @see net.cellcomputing.himawari.library.CqSurface#GenerateGeometricNormals(int, int, net.cellcomputing.himawari.library.IqShaderData)
	 */
	@Override
	public void	GenerateGeometricNormals( int uDiceSize, int vDiceSize, IqShaderData pNormals )
	{
		int v, u;
		CqVector3D	N = new CqVector3D();
		for ( v = 0; v <= vDiceSize; v++ )
		{
			for ( u = 0; u <= uDiceSize; u++ )
			{
				int igrid = ( v * ( uDiceSize + 1 ) ) + u;
				DicePoint( u, v, N );
				
				boolean CSO = pTransform().GetHandedness(pTransform().Time(0));
				boolean O = pAttributes().GetIntegerAttribute( "System", "Orientation" ) [ 0 ] != 0;
				
				N .assignment( ( O == CSO ) ? N : N.negative() );
				pNormals.SetNormal( m_matITTx.multiply( N ), igrid );
			
			}
		}
	}


	/**
	 * 無限の2つの線が交差する点を見つける。<br>
	 * アルゴリズムから、線の1つから平面を発生させ、交差点がこの平面ともう片方の線の間の点であることが分かる。
	 * 
	 * @return	true/false	false(平行), true(交差)
	 */
	static boolean IntersectLine( CqVector3D P1, CqVector3D T1, CqVector3D P2, CqVector3D T2, CqVector3D P )
	{
		CqVector3D	v = new CqVector3D();
		CqVector3D  px = new CqVector3D();
		
		px.assignment( T1.mod( P1.sub(T2) ) );
		v.assignment( px.mod( T1 ) );
		
		float	t = ( P1.sub( P2 ) ).mul( v );
		float	vw = v.mul( T2 );
		
		if ( ( vw * vw ) < 1.0e-07 )
			return ( false );
		
		t /= vw;
		P.assignment( P2.add( T2.mul(((( P1.sub( P2 )).mul( v )) / vw ))));
		return ( true );
	}
	
	
	/**
	 * Project a point onto a line, returns the projection point in p.
	 *  線上の点を投影し、点Pの投影先を返す。
	 * 
	 * @param	S	
	 * @param 	Trj	
	 * @param 	pnt	
	 * @param 	p	
	 */	
	static void ProjectToLine( final CqVector3D S, final CqVector3D Trj, final CqVector3D pnt, CqVector3D p )
	{
		CqVector3D a = pnt.sub( S );
		float fraction, denom;
		
		denom = Trj.Magnitude2();
		fraction = ( denom == 0.0f ) ? 0.0f : ( Trj.mul( a ) / denom);
		p.assignment( Trj.mul( fraction ) );
		p.assignAdd( S );
	}
	
	
	/** オブジェクトからカメラへの変換行列。 
	 */
	protected CqMatrix	m_matTx = new CqMatrix();		///< Transformation matrix from object to camera.
	
	/** 変換行列を逆転移させたもの。法線を変更。
	 */
	protected CqMatrix	m_matITTx = new CqMatrix();		///< Inverse transpose transformation matrix, for transforming normals.
	
}

