// 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 net.cellcomputing.himawari.library.RiGlobal.QGetRenderContext;
import static net.cellcomputing.himawari.library.EqMicroPolyFlags.*;
import static net.cellcomputing.himawari.library.EqIntIndex.*;
import static net.cellcomputing.himawari.library.EqEnvVars.*;
import static net.cellcomputing.himawari.library.CqStats.*;
import static java.lang.Math.max;
import static java.lang.Math.abs;
import static java.lang.Math.sqrt;
import net.cellcomputing.himawari.accessory.STLArray;
import net.cellcomputing.himawari.accessory.STLVector;
import net.cellcomputing.himawari.accessory.primitive.p_float;
import net.cellcomputing.himawari.library.types.CqColor;
import net.cellcomputing.himawari.library.types.CqRefCount;
import net.cellcomputing.himawari.library.types.CqVector2D;
import net.cellcomputing.himawari.library.types.CqVector3D;

/**
 * 
 * 静止画と動画のマイクロポリゴンを誘導する、基本的な抽象クラス。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqMicroPolygon extends CqRefCount {
	
	//nttdata 高速化
//	protected long	 m_IndexCode;
//	protected long m_BoundCode;
	protected int m_IndexCode;
	protected int m_BoundCode;
	
	protected CqBound m_Bound = new CqBound();	///< Stored bound.
	
	protected CqMicroPolyGridBase	m_pGrid;	///< Pointer to the donor grid.
	protected int	m_Index;					///< Index within the donor grid.
	
	protected short m_Flags;					///< Bitvector of general flags, using EqMicroPolyFlags as bitmasks.
	
	protected CqHitTestCache m_pHitTestCache; 	/// struct to hold cached values used in the point-in-poly test
	
	//************************************************************************//
	//*** コンストラクタの定義
	//************************************************************************//
	
	/**
	 * デフォルトコンストラクタ
	 */
	public CqMicroPolygon() {
		m_pGrid = null;
		m_Flags = 0;
		m_pHitTestCache = null;
		
		STATS_INC( MPG_allocated );
		STATS_INC( MPG_current );
		int cMPG = STATS_GETI( MPG_current );
		int cPeak = STATS_GETI( MPG_peak );
		STATS_SETI( MPG_peak, cMPG > cPeak ? cMPG : cPeak );
	}
	
	/**
	 * デストラクタ
	 */
	public void destruct(){
//		if ( m_pGrid ) RELEASEREF( m_pGrid );
		STATS_INC( MPG_deallocated );
		STATS_DEC( MPG_current );
		if ( !IsHit() )
			STATS_INC( MPG_missed );
	}
	
	//************************************************************************//
	//*** メソッドの定義
	//************************************************************************//
	
	/** 
	 * Set up the pointer to the grid this micropoly came from.
	 * @param pGrid CqMicroPolyGrid pointer.
	 */
	public void SetGrid( CqMicroPolyGridBase pGrid )
	{
		if ( m_pGrid != null ) m_pGrid.Release();
		m_pGrid = pGrid;
		m_pGrid.AddRef();
	}
	
	/** Get the pointer to the grid this micropoly came from.
	 * @return Pointer to the CqMicroPolyGrid.
	 */
	public CqMicroPolyGridBase pGrid()
	{
		return ( m_pGrid );
	}
	
	/** Get the index into the grid of this MPG
	 */
	public int GetIndex()
	{
		return( m_Index );
	}
	
	/** Set the index within the donor grid.
	 * @param Index Integer grid index.
	 */
	public void SetIndex( int Index )
	{
		assert( m_pGrid != null && m_pGrid.GridSize() > Index );
		m_Index = Index;
	}
	
	/** Release this micropolys reference to the donor grid.
	 */
	public void Detach()
	{
		if ( m_pGrid != null )
		{
//			RELEASEREF( m_pGrid );
			m_pGrid = null;
		}
	}
	
	/** Get the color of this micropoly.
	 * @return CqColor reference.
	 */
	public CqColor	colColor()
	{
		CqColor colRes = new CqColor();
		m_pGrid.pVar(EnvVars_Ci).GetColor( colRes, m_Index );
		return ( colRes );
	}
	
	/** Get the opacity of this micropoly.
	 * @return CqColor reference.
	 */
	public CqColor colOpacity()
	{
		CqColor colRes = new CqColor();
		m_pGrid.pVar(EnvVars_Oi).GetColor( colRes, m_Index );
		return ( colRes );
	}
	
	/** Calculate the bound of the micropoly.
	 */
	public void CalculateTotalBound()
	{
//		nttdata 高速化
//		STLVector<CqVector3D> pP = new STLVector<CqVector3D>( CqVector3D.class );
//		m_pGrid.pVar(EnvVars_P).GetPointPtr( pP );
		
		//STLVector<CqVector3D> pP;
		STLArray<CqVector3D> pP;
		pP = m_pGrid.pVar(EnvVars_P).GetPointPtr_2( );
		
		// Calculate the boundary, and store the indexes in the cache.
		CqVector3D B = pP.get( m_Index + 1 );
		int cu = m_pGrid.uGridRes();
		CqVector3D C = pP.get( m_Index + cu + 2 );
		CqVector3D D = pP.get( m_Index + cu + 1 );
		
		short BCMinX = 0;
		short BCMaxX = 0;
		short BCMinY = 0;
		short BCMaxY = 0;
		short BCMinZ = 0;
		short BCMaxZ = 0;
		m_BoundCode = 0xe4;
		int[] TempIndexTable = {	GetCodedIndex( m_BoundCode, 0 ),
				GetCodedIndex( m_BoundCode, 1 ),
				GetCodedIndex( m_BoundCode, 2 ),
				GetCodedIndex( m_BoundCode, 3 ) };
		if ( B.x < pP.get( TempIndexTable[ BCMinX ] ).x ) BCMinX = 1;
		if ( B.x > pP.get( TempIndexTable[ BCMaxX ] ).x ) BCMaxX = 1;
		if ( B.y < pP.get( TempIndexTable[ BCMinY ] ).y ) BCMinY = 1;
		if ( B.y > pP.get( TempIndexTable[ BCMaxY ] ).y ) BCMaxY = 1;
		if ( B.z < pP.get( TempIndexTable[ BCMinZ ] ).z ) BCMinZ = 1;
		if ( B.z > pP.get( TempIndexTable[ BCMaxZ ] ).z ) BCMaxZ = 1;
		
		if ( C.x < pP.get( TempIndexTable[ BCMinX ] ).x ) BCMinX = 2;
		if ( C.x > pP.get( TempIndexTable[ BCMaxX ] ).x ) BCMaxX = 2;
		if ( C.y < pP.get( TempIndexTable[ BCMinY ] ).y ) BCMinY = 2;
		if ( C.y > pP.get( TempIndexTable[ BCMaxY ] ).y ) BCMaxY = 2;
		if ( C.z < pP.get( TempIndexTable[ BCMinZ ] ).z ) BCMinZ = 2;
		if ( C.z > pP.get( TempIndexTable[ BCMaxZ ] ).z ) BCMaxZ = 2;
		
		if ( !IsDegenerate() )
		{
			if ( D.x < pP.get( TempIndexTable[ BCMinX ] ).x ) BCMinX = 3;
			if ( D.x > pP.get( TempIndexTable[ BCMaxX ] ).x ) BCMaxX = 3;
			if ( D.y < pP.get( TempIndexTable[ BCMinY ] ).y ) BCMinY = 3;
			if ( D.y > pP.get( TempIndexTable[ BCMaxY ] ).y ) BCMaxY = 3;
			if ( D.z < pP.get( TempIndexTable[ BCMinZ ] ).z ) BCMinZ = 3;
			if ( D.z > pP.get( TempIndexTable[ BCMaxZ ] ).z ) BCMaxZ = 3;
		}
		m_BoundCode = ( ( BCMinX & 0x3 ) |
				( ( BCMinY & 0x3 ) << 2 ) |
				( ( BCMinZ & 0x3 ) << 4 ) |
				( ( BCMaxX & 0x3 ) << 6 ) |
				( ( BCMaxY & 0x3 ) << 8 ) |
				( ( BCMaxZ & 0x3 ) << 10 ) );
		
		m_Bound = new CqBound( pP.get( GetCodedIndex( m_BoundCode, 0 ) ).x, pP.get( GetCodedIndex( m_BoundCode, 1 ) ).y, pP.get( GetCodedIndex( m_BoundCode, 2 ) ).z,
				pP.get( GetCodedIndex( m_BoundCode, 3 ) ).x, pP.get( GetCodedIndex( m_BoundCode, 4 ) ).y, pP.get( GetCodedIndex( m_BoundCode, 5 ) ).z );
		
		// Adjust for DOF
		if ( QGetRenderContext().UsingDepthOfField() )
		{
			CqVector2D minZCoc = QGetRenderContext().GetCircleOfConfusion( m_Bound.vecMin().z );
			CqVector2D maxZCoc = QGetRenderContext().GetCircleOfConfusion( m_Bound.vecMax().z );
			float cocX = max( minZCoc.x, maxZCoc.x );
			float cocY = max( minZCoc.y, maxZCoc.y );
			m_Bound.vecMin().x = m_Bound.vecMin().x - cocX ;
			m_Bound.vecMin().y = m_Bound.vecMin().y - cocY ;
			m_Bound.vecMax().x = m_Bound.vecMax().x + cocX ;
			m_Bound.vecMax().y = m_Bound.vecMax().y + cocY ;
		}
	}
	
	
	/** Get the bound of the micropoly.
	 * @return CqBound representing the conservative bound.
	 */
	public CqBound GetTotalBound()
	{
//		return ( m_Bound ); //2005/12/13 Hibiware に直接関係はなかった
		return ( new CqBound(m_Bound) );
	}
	
	public	int	cSubBounds()
	{
		return ( 1 );
	}
	
	public	CqBound SubBound( int iIndex,p_float time )
	{
		time.value = 0.0f;
		return ( GetTotalBound() );
	}
	
	/** 
	 * Sample the specified point against the MPG at the specified time.
	 * @param vecSample 2D vector to sample against.
	 * @param time Shutter time to sample at.
	 * @param D Storage for depth if valid hit.
	 * @return Boolean indicating smaple hit.
	 */
//【改】aqsis1.01への変更*****************************************************
//	public boolean Sample( final CqVector2D vecSample, p_float D, float time )
//	{
	public boolean Sample( final SqSampleData sample, p_float D, float time, boolean UsingDof )
	{
		final CqVector2D vecSample = sample.m_Position; 
		
		// If using DoF, we need to adjust the point positions, and the hit test cache,
		// \note: this invalidates the hit test cache, but we are using DoF, so all bets are off anyway.
		// still, would be good to find out if there is a better way of doing this.
		//CqHitTestCache hitTestCache = new CqHitTestCache();
		
		if(UsingDof)
		{
			CqVector3D[] points = { new CqVector3D( PointB() ), new CqVector3D( PointC() ), new CqVector3D( PointD() ), new CqVector3D( PointA() ) };
//nttdata 高速化
//			CqVector2D coc = QGetRenderContext().GetCircleOfConfusion(points[0].z);
//			points[0].x(points[0].x - ( coc.x * sample.m_DofOffset.x ));
//			points[0].y(points[0].y - ( coc.y * sample.m_DofOffset.y ));
//			
//			coc = QGetRenderContext().GetCircleOfConfusion(points[1].z);
//			points[1].x(points[1].x - ( coc.x * sample.m_DofOffset.x ));
//			points[1].y(points[1].y - ( coc.y * sample.m_DofOffset.y ));
//			
//			coc = QGetRenderContext().GetCircleOfConfusion(points[2].z);
//			points[2].x(points[2].x - ( coc.x * sample.m_DofOffset.x ));
//			points[2].y(points[2].y - ( coc.y * sample.m_DofOffset.y ));
//			
//			coc = QGetRenderContext().GetCircleOfConfusion(points[3].z);
//			points[3].x(points[3].x - ( coc.x * sample.m_DofOffset.x ));
//			points[3].y(points[3].y - ( coc.y * sample.m_DofOffset.y ));
			CqVector2D coc = new CqVector2D(); 
			QGetRenderContext().GetCircleOfConfusion(points[0].z,coc);
			points[0].x(points[0].x - ( coc.x * sample.m_DofOffset.x ));
			points[0].y(points[0].y - ( coc.y * sample.m_DofOffset.y ));
			
			QGetRenderContext().GetCircleOfConfusion(points[1].z,coc);
			points[1].x(points[1].x - ( coc.x * sample.m_DofOffset.x ));
			points[1].y(points[1].y - ( coc.y * sample.m_DofOffset.y ));
			
			QGetRenderContext().GetCircleOfConfusion(points[2].z,coc);
			points[2].x(points[2].x - ( coc.x * sample.m_DofOffset.x ));
			points[2].y(points[2].y - ( coc.y * sample.m_DofOffset.y ));
			
			QGetRenderContext().GetCircleOfConfusion(points[3].z,coc);
			points[3].x(points[3].x - ( coc.x * sample.m_DofOffset.x ));
			points[3].y(points[3].y - ( coc.y * sample.m_DofOffset.y ));
			
			CqHitTestCache hitTestCache = new CqHitTestCache();
			
			CacheHitTestValues( hitTestCache, points);
		}
		
		//ここまで*******************************************************
		
		if ( fContains( vecSample, D, time  ) )
		{
			// Now check if it is trimmed.
			if ( IsTrimmed() )
			{
				// Get the required trim curve sense, if specified, defaults to "inside".
				String[] pattrTrimSense = pGrid().pAttributes().GetStringAttribute( "trimcurve", "sense" );
				String strTrimSense = "inside";
				if ( pattrTrimSense != null ) strTrimSense = pattrTrimSense[ 0 ];
//				boolean bOutside = strTrimSense == "outside";//修正 2006/03/08
				boolean bOutside = strTrimSense.equals("outside");
				
				CqVector2D vecUV = ReverseBilinear( vecSample );
				
				p_float u = new p_float();
				p_float v = new p_float();
				
				pGrid().pVar(EnvVars_u).GetFloat( u, m_Index );
				pGrid().pVar(EnvVars_v).GetFloat( v, m_Index );
				CqVector2D uvA = new CqVector2D( u.value, v.value );
				
				pGrid().pVar(EnvVars_u).GetFloat( u, m_Index + 1 );
				pGrid().pVar(EnvVars_v).GetFloat( v, m_Index + 1 );
				CqVector2D uvB = new CqVector2D( u.value, v.value );
				
				pGrid().pVar(EnvVars_u).GetFloat( u, m_Index + pGrid().uGridRes() + 1 );
				pGrid().pVar(EnvVars_v).GetFloat( v, m_Index + pGrid().uGridRes() + 1 );
				CqVector2D uvC = new CqVector2D( u.value, v.value );
				
				pGrid().pVar(EnvVars_u).GetFloat( u, m_Index + pGrid().uGridRes() + 2 );
				pGrid().pVar(EnvVars_v).GetFloat( v, m_Index + pGrid().uGridRes() + 2 );
				CqVector2D uvD = new CqVector2D( u.value, v.value );
				
				CqVector2D vR = RiGlobal.BilinearEvaluate( uvA, uvB, uvC, uvD, vecUV.x, vecUV.y, CqVector2D.class );
				
				if ( pGrid().pSurface().bCanBeTrimmed() && pGrid().pSurface().bIsPointTrimmed( vR ) && !bOutside )
				{
					STATS_INC( MPG_trimmed );
					return ( false );
				}
			}
			
			if ( pGrid().fTriangular() )
			{
				CqVector3D vA = new CqVector3D();
				CqVector3D vB = new CqVector3D();
				pGrid().TriangleSplitPoints( vA, vB, time );
				float Ax = vA.x;
				float Ay = vA.y;
				float Bx = vB.x;
				float By = vB.y;
				
				float v = ( Ay - By ) * vecSample.x + ( Bx - Ax ) * vecSample.y + ( Ax * By - Bx * Ay );
				if ( v <= 0 )
					return ( false );
			}
			
			return ( true );
		}
		else
			return ( false );
	}
	
	/** Query if the micropolygon has been successfully hit by a pixel sample.
	 */
	public boolean IsHit()
	{
		return ( ( m_Flags & MicroPolyFlags_Hit ) != 0 );
	}
	
	/** Set the flag to state that the MPG has eben hit by a sample point.
	 */
	public void MarkHit()
	{
		m_Flags |= MicroPolyFlags_Hit;
	}
	
	/** Get the flag indicating if the micropoly has already beed pushed forward to the next bucket.
	 */
	public boolean IsPushedForward()
	{
		return ( ( m_Flags & MicroPolyFlags_PushedForward ) != 0 );
	}
	
	/** Set the flag indicating if the micropoly has already beed pushed forward to the next bucket.
	 */
	public void MarkPushedForward()
	{
		m_Flags |= MicroPolyFlags_PushedForward;
	}
	
	public void MarkTrimmed()
	{
		m_Flags |= MicroPolyFlags_Trimmed;
	}
	
	public boolean IsTrimmed()
	{
		return ( ( m_Flags & MicroPolyFlags_Trimmed ) != 0 );
	}
	
	public boolean IsMoving()
	{
		return false;
	}
	
	/** Determinde whether the 2D point specified lies within this micropolygon.
	 * @param vecP 2D vector to test for containment.
	 * @param Depth Place to put the depth if valid intersection.
	 * @param time The frame time at which to check containment.
	 * @return Boolean indicating sample hit.
	 */
	public boolean fContains( final CqVector2D vecP, p_float Depth, float time )
	{
		// AGG - optimised version of above.
		float x = vecP.x, y = vecP.y;
		
		// start with the edge that failed last time to get the most benefit
		// from an early exit.
		int e = m_pHitTestCache.m_LastFailedEdge;
		int prev = e - 1;
		if(prev < 0) prev = 3;
		for(int i=0; i<4; ++i)
		{
			// test which side of the edge the sample point lies.
			// the first two edges are tested with <= and the second two with <
			// this is so every sample point lies on exactly one side of the edge,
			// ie if it is exactly coincident with the edge it can't be on both
			// or neither sides.
			if( (e & 2) != 0 )
//**********
//				if( m_pHitTestCache.m_Y[e] > m_pHitTestCache.m_Y[prev] ||
//				( m_pHitTestCache.m_Y[e] == m_pHitTestCache.m_Y[prev] &&
//				m_pHitTestCache.m_X[e] > m_pHitTestCache.m_X[prev] ) )
			{
				if( (( y - m_pHitTestCache.m_Y[e]) * m_pHitTestCache.m_YMultiplier[e] ) -
						(( x - m_pHitTestCache.m_X[e]) * m_pHitTestCache.m_XMultiplier[e] ) < 0)
				{
					m_pHitTestCache.m_LastFailedEdge = e;
					return false;
				}
			}
			else
			{
				if( (( y - m_pHitTestCache.m_Y[e]) * m_pHitTestCache.m_YMultiplier[e] ) -
						(( x - m_pHitTestCache.m_X[e]) * m_pHitTestCache.m_XMultiplier[e] ) <= 0)
				{
					m_pHitTestCache.m_LastFailedEdge = e;
					return false;
				}
			}
			
			// move to next edge, wrapping to zero at four.
			prev = e;
			e = (e+1) & 3;
		}
		
		Depth.value = ( m_pHitTestCache.m_D - ( m_pHitTestCache.m_VecN.x * vecP.x ) -
				( m_pHitTestCache.m_VecN.y * vecP.y ) ) * m_pHitTestCache.m_OneOverVecNZ;
		
		return true;
	}
	
	/** Cache some values needed for the point in poly test.
	 * This must be called prior to calling fContains() on a mpg.
	 */
	public void CacheHitTestValues(CqHitTestCache cache, CqVector3D[] points)
	{
		m_pHitTestCache = cache;
//		
		int j = 3;
		for(int i=0; i<4; ++i)
		{
			cache.m_YMultiplier[i] = points[i].x - points[j].x;
			cache.m_XMultiplier[i] = points[i].y - points[j].y;
			cache.m_X[i] = points[j].x;
			cache.m_Y[i] = points[j].y;
			j = i;
		}
		
		// if the mpg is degenerate then we repeat edge c=>a so we still have four
		// edges (it makes the test in fContains() simpler).
		if(IsDegenerate())
		{
			for(int i=2; i<4; ++i)
			{
				cache.m_YMultiplier[i] = points[3].x - points[1].x;
				cache.m_XMultiplier[i] = points[3].y - points[1].y;
				cache.m_X[i] = points[1].x;
				cache.m_Y[i] = points[1].y;
			}
		}
		
//		cache.m_VecN = (points[3] - points[0]) % (points[1] - points[0]);
//		cache.m_VecN.Unit();
//		cache.m_D = cache.m_VecN * points[3];
//		cache.m_OneOverVecNZ = 1.0f / cache.m_VecN.z;
//		cache.m_VecN = ( points[3].sub(points[0]) ).mod( points[1].sub(points[0]) );
		//nttdata 高速化
		cache.m_VecN.exterior( points[3].sub(points[0]) , points[1].sub(points[0]) );
		cache.m_VecN.Unit();
		cache.m_D = cache.m_VecN.mul( points[3] );
		cache.m_OneOverVecNZ = 1.0f / cache.m_VecN.z;
		
		cache.m_LastFailedEdge = 0;
	}
	
	public void CacheHitTestValues(CqHitTestCache cache)
	{
		CqVector3D[] points = { PointB(), PointC(), PointD(), PointA() };
		CacheHitTestValues(cache, points);
	}
	
	
//	AGG - 19-6-04
//	this version moves the corners of the mpg for dof.
//	currently we don't use this as it's a fair bit slower than just offsetting
//	the sample position in the opposite direction (we need to call this for every
//	sample instead of just once). however, it is more correct than moving the
//	sample because the corners may move by different amounts. if I can work out
//	how to make it fast enough we should use this version.
	public void CacheHitTestValuesDof(CqHitTestCache cache, final CqVector2D DofOffset, CqVector2D[] coc)
	{
		CqVector3D[] points = new CqVector3D[4];
		
		points[0] = new CqVector3D( 
				PointB().x - (coc[1].x * DofOffset.x), 
				PointB().y - (coc[1].y * DofOffset.y),
				PointB().z
		);
		points[1] = new CqVector3D(
				PointC().x - (coc[2].x * DofOffset.x),
				PointC().y - (coc[2].y * DofOffset.y),
				PointC().z
		);
		points[2] = new CqVector3D(
				PointD().x - (coc[3].x * DofOffset.x),
				PointD().y - (coc[3].y * DofOffset.y),
				PointD().z
		);
		points[3] = new CqVector3D(
				PointA().x - (coc[0].x * DofOffset.x),
				PointA().y - (coc[0].y * DofOffset.y),
				PointA().z
		);
		
		CacheHitTestValues(cache, points);
	}
	
	/** Initialise the information within the micro polygon used during sampling.
	 */
	public void Initialise()
	{
		// Check for degenerate case, if any of the neighbouring points are the same, shuffle them down, and
		// duplicate the last point exactly. Exact duplication of the last two points is used as a marker in the
		// fContains function to indicate degeneracy. If more that two points are coincident, we are in real trouble!
		int cu = m_pGrid.uGridRes();
		int IndexA = m_Index;
		int IndexB = m_Index + 1;
		int IndexC = m_Index + cu + 2;
		int IndexD = m_Index + cu + 1;
		
		short CodeA = 0;
		short CodeB = 1;
		short CodeC = 2;
		short CodeD = 3;
		
		//高速化
//		STLVector<CqVector3D> pP = new STLVector<CqVector3D>(CqVector3D.class);
//		m_pGrid.pVar(EnvVars_P).GetPointPtr( pP );
		//STLVector<CqVector3D> pP ;
		STLArray<CqVector3D> pP ;
		pP = m_pGrid.pVar(EnvVars_P).GetPointPtr_2( );
		
		if ( ( pP.get( IndexA ).sub( pP.get( IndexB )) ).Magnitude2() < 1e-8 )
		{
			// A--B is degenerate
			IndexB = IndexC;
			CodeB = CodeC;
			IndexC = IndexD;
			CodeC = CodeD;
			IndexD = -1;
			CodeD = -1;
		}
		else if ( ( pP.get( IndexB ).sub( pP.get( IndexC ) ) ).Magnitude2() < 1e-8 )
		{
			// B--C is degenerate
			IndexB = IndexC;
			CodeB = CodeC;
			IndexC = IndexD;
			CodeC = CodeD;
			IndexD = -1;
			CodeD = -1;
		}
		else if ( ( pP.get( IndexC ).sub( pP.get( IndexD ) ) ).Magnitude2() < 1e-8 )
		{
			// C--D is degenerate
			IndexC = IndexD;
			CodeC = CodeD;
			IndexD = -1;
			CodeD = -1;
		}
		else if ( ( pP.get( IndexD ).sub( pP.get( IndexA ) ) ).Magnitude2() < 1e-8 )
		{
			// D--A is degenerate
			IndexD = IndexC;
			CodeD = CodeC;
			IndexD = -1;
			CodeD = -1;
		}
		
		CqVector3D vA2 = pP.get( IndexA );
		CqVector3D vB2 = pP.get( IndexB );
		CqVector3D vC2 = pP.get( IndexC );
		
		// Determine whether the MPG is CW or CCW, must be CCW for fContains to work.
		boolean fFlip = ( ( vA2.x - vB2.x ) * ( vB2.y - vC2.y ) ) >= ( ( vA2.y - vB2.y ) * ( vB2.x - vC2.x ) );
		
		m_IndexCode = 0;
		
		if ( !fFlip )
		{
			m_IndexCode = ( CodeD == -1 ) ?
					( ( CodeA & 0x3 ) | ( ( CodeC & 0x3 ) << 2 ) | ( ( CodeB & 0x3 ) << 4 ) | 0x8000000 ) :
						( ( CodeA & 0x3 ) | ( ( CodeD & 0x3 ) << 2 ) | ( ( CodeC & 0x3 ) << 4 ) | ( ( CodeB & 0x3 ) << 6 ) );
		}
		else
		{
			m_IndexCode = ( CodeD == -1 ) ?
					( ( CodeA & 0x3 ) | ( ( CodeB & 0x3 ) << 2 ) | ( ( CodeC & 0x3 ) << 4 ) | 0x8000000 ) :
						( ( CodeA & 0x3 ) | ( ( CodeB & 0x3 ) << 2 ) | ( ( CodeC & 0x3 ) << 4 ) | ( ( CodeD & 0x3 ) << 6 ) );
		}
	}
	
	public CqVector2D ReverseBilinear( final CqVector2D v )
	{
	    CqVector2D kA, kB, kC, kD;
	    CqVector2D kResult = new CqVector2D();
		boolean flip = false;

	    kA = new CqVector2D( PointA() );
	    kB = new CqVector2D( PointB() );
	    kC = new CqVector2D( PointD() );
	    kD = new CqVector2D( PointC() );

		if(abs(kB.x - kA.x) < abs(kC.x - kA.x) )
		{
			CqVector2D temp = new CqVector2D(); 
			temp.assignment( kC );
			kC.assignment( kB );
			kB.assignment( temp );
			//flip = true;
		}

//	    kD += kA - kB - kC;
//	    kB -= kA;
//	    kC -= kA;
	    kD.assignAdd( kA.sub( kB ).sub( kC ) );
	    kB.assignSub( kA );
	    kC.assignSub( kA );

	    float fBCdet = kB.x * kC.y - kB.y * kC.x;
	    float fCDdet = kC.y * kD.x - kC.x * kD.y;

	    CqVector2D kDiff = kA.sub( v );
	    float fABdet = kDiff.y * kB.x - kDiff.x * kB.y;
	    float fADdet = kDiff.y * kD.x - kDiff.x * kD.y;
	    float fA = fCDdet;
	    float fB = fADdet + fBCdet;
	    float fC = fABdet;

	    if ( abs( fA ) >= 1.0e-6 )
	    {
	        // t-equation is quadratic
	        float fDiscr = (float)sqrt( abs( fB * fB - 4.0f * fA * fC ) );
	        kResult.y( ( -fB + fDiscr ) / ( 2.0f * fA ) );
	        if ( kResult.y < 0.0f || kResult.y > 1.0f )
	        {
	            kResult.y( ( -fB - fDiscr ) / ( 2.0f * fA ) );
	            if ( kResult.y < 0.0f || kResult.y > 1.0f )
	            {
	                // point p not inside quadrilateral, return invalid result
	                return ( new CqVector2D( -1.0f, -1.0f ) );
	            }
	        }
	    }
	    else
	    {
	        // t-equation is linear
	        kResult.y( -fC / fB );
	    }
	    //kResult.x( -( kDiff.x + kResult.y * kC.x ) / ( kB.x + kResult.y * kD.x ) );
	    kResult.x = -( kDiff.x + kResult.y * kC.x ) / ( kB.x + kResult.y * kD.x ) ;
		if(flip)
		{
			float temp = kResult.x;
			kResult.x = kResult.y;
			kResult.y = temp;
		}

	    return ( kResult );
	}
	
	
    public CqVector3D PointA()	
    {
    	//nttdata 高速化
//    	STLVector<CqVector3D> pP = new STLVector<CqVector3D>(CqVector3D.class);
//        m_pGrid.pVar(EnvVars_P).GetPointPtr( pP );
    	
        //STLVector<CqVector3D> pP ;
        STLArray<CqVector3D> pP ;
        pP = m_pGrid.pVar(EnvVars_P).GetPointPtr_2( );
        return ( pP.get( GetCodedIndex( m_IndexCode, 0 ) ) );
    }
    
    
    public CqVector3D PointB()
    {
    	//nttdata 高速化
//    	STLVector<CqVector3D> pP = new STLVector<CqVector3D>(CqVector3D.class);
//        m_pGrid.pVar(EnvVars_P).GetPointPtr( pP );

        //STLVector<CqVector3D> pP ;
        STLArray<CqVector3D> pP ;
        pP = m_pGrid.pVar(EnvVars_P).GetPointPtr_2( );
        return ( pP.get( GetCodedIndex( m_IndexCode, 1 ) ) );
    }
    
    
    public CqVector3D PointC()
    {
    	//nttdata 高速化
//    	STLVector<CqVector3D> pP = new STLVector<CqVector3D>(CqVector3D.class);
//        m_pGrid.pVar(EnvVars_P).GetPointPtr( pP );

        //STLVector<CqVector3D> pP ;
        STLArray<CqVector3D> pP ;
        pP = m_pGrid.pVar(EnvVars_P).GetPointPtr_2( );
        return ( pP.get( GetCodedIndex( m_IndexCode, 2 ) ) );
    }
    public CqVector3D PointD()
    {
    	//nttdata 高速化
//    	STLVector<CqVector3D> pP = new STLVector<CqVector3D>(CqVector3D.class);
//        m_pGrid.pVar(EnvVars_P).GetPointPtr( pP );

        //STLVector<CqVector3D> pP ;
        STLArray<CqVector3D> pP ;
        pP = m_pGrid.pVar(EnvVars_P).GetPointPtr_2( );
        return ( pP.get( GetCodedIndex( m_IndexCode, 3 ) ) );
    }
	
    public boolean IsDegenerate()
    {
        return ( ( m_IndexCode & 0x8000000 ) != 0 );
    }
    protected int GetCodedIndex( int code, int shift )
  {
      switch ( ( ( code >> ( shift << 1 ) ) & 0x3 ) )
      {
      case 1:
          return ( m_Index + 1 );
      case 2:
          return ( m_Index + m_pGrid.uGridRes() + 2 );
      case 3:
          return ( m_Index + m_pGrid.uGridRes() + 1 );
      default:
          return ( m_Index );
      }
  }
//	protected int GetCodedIndex( short code, short shift )
//    {
//        switch ( ( ( code >> ( shift << 1 ) ) & 0x3 ) )
//        {
//        case 1:
//            return ( m_Index + 1 );
//        case 2:
//            return ( m_Index + m_pGrid.uGridRes() + 2 );
//        case 3:
//            return ( m_Index + m_pGrid.uGridRes() + 1 );
//        default:
//            return ( m_Index );
//        }
//    }
//	
//	protected final int GetCodedIndex( long code, long shift )
//	{
//		return GetCodedIndex( (short)code, (short)shift );
//	}
	
	//************************************************************************//
	//*** 演算子のオーバーロード
	//************************************************************************//
	
	
	/** Assigment operator, copies contents of donor micropoly while safely deleting old contents.
	 * @param From Donor micropoly.
	 */
	public CqMicroPolygon assignment( final CqMicroPolygon From )
	{
		if ( m_pGrid != null ) m_pGrid.Release();
		m_pGrid = From.m_pGrid;
		m_pGrid.AddRef();
		m_Index = From.m_Index;
		m_IndexCode = From.m_IndexCode;
		m_BoundCode = From.m_BoundCode;
		m_Flags = From.m_Flags;
		
		return ( this );
	}
	
	
	public static void Flush(){};
}
