// 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.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.sqrt;
import static net.cellcomputing.himawari.library.RiGlobal.QGetRenderContext;
import net.cellcomputing.himawari.library.types.CqVector2D;
import net.cellcomputing.himawari.library.types.CqVector3D;

/**
 * 
 * 静的なマイクロポリゴンの基礎となるラス。マイクロポリゴンの幾何学についての点の情報を保存する。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqMovingMicroPolygonKey {

	public CqVector3D	m_Point0	= new CqVector3D();
	public CqVector3D	m_Point1	= new CqVector3D();
	public CqVector3D	m_Point2	= new CqVector3D();
	public CqVector3D	m_Point3	= new CqVector3D();
	public CqVector3D	m_N			= new CqVector3D();		///< The normal to the micropoly.
	public float		m_D;								///< Distance of the plane from the origin, used for calculating sample depth.

	public CqBound 	m_Bound		= new CqBound();
	public boolean	m_BoundReady;

	
	public CqMovingMicroPolygonKey()
	{}
	
	public CqMovingMicroPolygonKey( final CqVector3D vA, final CqVector3D vB, final CqVector3D vC, final CqVector3D vD )
	{
		Initialise( vA, vB, vC, vD );
	}
	
	//【改】aqsis 1.01消去************************************************************
//	/** 
//	 * 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.
//	 */
//	@SuppressWarnings("unused")
//	public boolean fContains( final CqVector2D vecP, p_float Depth, float time )
//	{
//	    // Check against each line of the quad, if outside any then point is outside MPG, therefore early exit.
//	    float r1, r2, r3, r4;
//	    float x = vecP.x, y = vecP.y;
//	    float x0 = m_Point0.x, y0 = m_Point0.y, x1 = m_Point1.x, y1 = m_Point1.y;
//	    if ( ( r1 = ( y - y0 ) * ( x1 - x0 ) - ( x - x0 ) * ( y1 - y0 ) ) <= 0 ) return ( false );
//	    x0 = x1; y0 = y1; x1 = m_Point2.x; y1 = m_Point2.y;
//	    if ( ( r2 = ( y - y0 ) * ( x1 - x0 ) - ( x - x0 ) * ( y1 - y0 ) ) <= 0 ) return ( false );
//	    x0 = x1; y0 = y1; x1 = m_Point3.x; y1 = m_Point3.y;
//	    if ( ( r3 = ( y - y0 ) * ( x1 - x0 ) - ( x - x0 ) * ( y1 - y0 ) ) < 0 ) return ( false );
//	    x0 = x1; y0 = y1; x1 = m_Point0.x; y1 = m_Point0.y;
//
//	    // Check for degeneracy.
//	    if ( ! ( x0 == x1 && y0 == y1 ) )
//	        if ( ( r4 = ( y - y0 ) * ( x1 - x0 ) - ( x - x0 ) * ( y1 - y0 ) ) < 0 ) return ( false );
//
//	    Depth.value = ( m_D - ( m_N.x * vecP.x ) - ( m_N.y * vecP.y ) ) / m_N.z;
//
//	    return ( true );
//	}
	//************************************************************************************
	
	public CqBound GetTotalBound()
	{
		if(m_BoundReady)
			return m_Bound;

		// Calculate the boundary, and store the indexes in the cache.
	    m_Bound.vecMin().x( min( m_Point0.x, min( m_Point1.x, min( m_Point2.x, m_Point3.x ) ) ) );
	    m_Bound.vecMin().y( min( m_Point0.y, min( m_Point1.y, min( m_Point2.y, m_Point3.y ) ) ) );
	    m_Bound.vecMin().z( min( m_Point0.z, min( m_Point1.z, min( m_Point2.z, m_Point3.z ) ) ) );
	    m_Bound.vecMax().x( max( m_Point0.x, max( m_Point1.x, max( m_Point2.x, m_Point3.x ) ) ) );
	    m_Bound.vecMax().y( max( m_Point0.y, max( m_Point1.y, max( m_Point2.y, m_Point3.y ) ) ) );
	    m_Bound.vecMax().z( max( m_Point0.z, max( m_Point1.z, max( m_Point2.z, m_Point3.z ) ) ) );

		// Adjust for DOF
		if ( QGetRenderContext().UsingDepthOfField() )
		{
			final CqVector2D minZCoc = QGetRenderContext().GetCircleOfConfusion( m_Bound.vecMin().z );
			final 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 );
		}

		m_BoundReady = true;
	    return ( m_Bound );

	}
	
	/** 
	 * Store the vectors of the micropolygon.
	 * @param vA 3D Vector.
	 * @param vB 3D Vector.
	 * @param vC 3D Vector.
	 * @param vD 3D Vector.
	 */
	public void Initialise( final CqVector3D vA, final CqVector3D vB, final CqVector3D vC, final CqVector3D vD )
	{
	    // 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!
	    final CqVector3D vvB = ( vA.sub( vB ) ).Magnitude() < 1e-2 ? vC : vB;
	    final CqVector3D vvC = ( vvB.sub( vC ) ).Magnitude() < 1e-2 ? vD : vC;
	    final CqVector3D vvD = ( vvC.sub( vD ) ).Magnitude() < 1e-2 ? vvC : vD;

	    // Determine whether the MPG is CW or CCW, must be CCW for fContains to work.
	    boolean fFlip = ( ( vA.x - vvB.x ) * ( vvB.y - vvC.y ) ) >= ( ( vA.y - vvB.y ) * ( vvB.x - vvC.x ) );

	    if ( !fFlip )
	    {
	        m_Point0.assignment( vA );
	        m_Point1.assignment( vvD );
	        m_Point2.assignment( vvC );
	        m_Point3.assignment( vvB );
	    }
	    else
	    {
	        m_Point0.assignment( vA );
	        m_Point1.assignment( vvB );
	        m_Point2.assignment( vvC );
	        m_Point3.assignment( vvD );
	    }

	    m_N = ( vA.sub( vvB ) ) .mod( ( vvC.sub( vvB ) ) );
	    m_N.Unit();
	    m_D = m_N .mul( vA );

		m_BoundReady = false;

	}
	
	
	//【改】aqsis1.01追加********************
    final boolean IsDegenerate()
    {
        return ( m_Point2.equals( m_Point3 ));
    }
    //*****************************************
    
    
	public CqVector2D ReverseBilinear( final CqVector2D v )
	{
	    CqVector2D kA = new CqVector2D(), kB = new CqVector2D(), kC = new CqVector2D(), kD = new CqVector2D();

	    kA = new CqVector2D( m_Point0 );
	    kB = new CqVector2D( m_Point1 ).sub( kA );
	    kC = new CqVector2D( m_Point3 ).sub( kA );
	    kD = new CqVector2D( m_Point2 ).add( kA ).sub( new CqVector2D( m_Point1 ) ).sub( new CqVector2D( m_Point3 ) );

	    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;
	    CqVector2D kResult = new CqVector2D();

	    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 ) );

	    return ( kResult );

	}
	
	
	
}
