// 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.STLArray;
import net.cellcomputing.himawari.accessory.STLVector;
import net.cellcomputing.himawari.accessory.primitive.p_int;
import net.cellcomputing.himawari.library.types.CqVector2D;

/**
 * 
 * トリミングループの作成。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqTrimLoop {
	
	/** default constructer */
//	public CqTrimLoop(){}
	public CqTrimLoop()
	{
		for(int i=0;i<Maxindex;i++)
		{
			m_aCurvePointsIndex[i] = new STLArray<p_int>(p_int.class); 
		}
	}
	
//	public CqTrimLoop(CqTrimLoop From)
//	{
//		
//		this.m_aCurvePoints.addAll(From.m_aCurvePoints);
//		this.m_aCurves.addAll(From.m_aCurves);
//	}
	
	public CqTrimLoop(CqTrimLoop From)
	{
		for(int i=0;i<Maxindex;i++)
		{
			m_aCurvePointsIndex[i] = new STLArray<p_int>(p_int.class); 
			this.m_aCurvePointsIndex[i].addAll(From.m_aCurvePointsIndex[i]);
		}
		this.m_aCurves.addAll(From.m_aCurves);
	}
	
	/** destructer */
	public void destract(){}
	
	
	/**
	 * ループを構成する点(の集合)を返す。
	 * 
	 * @return	m_aCurves	トリミングする面上の点
	 */
	public STLVector<CqTrimCurve> aCurves()
    {
        return ( m_aCurves );
    }

	
	/**
	 * トリミングする軌跡の点を決定する。
	 * 
	 * @param pSurface	トリミングする面
	 */
//	public void	Prepare( CqSurface pSurface )
//    {
//		int iPoint;
//    
//        for( CqTrimCurve iCurve : m_aCurves )
//        {
//        	int cPoints = pSurface.TrimDecimation( iCurve );
//   
//        	iCurve.Clamp();
//        	
//        	float range = iCurve.aKnots().get( iCurve.cKnots() - 1 ).value - iCurve.aKnots().get( 0 ).value;
//        	float u = iCurve.aKnots().get( 0 ).value;
//        	float du = range / cPoints;
//        	
//        	for ( iPoint = 0; iPoint < cPoints; iPoint++ )
//        	{
//        		m_aCurvePoints.add( iCurve.Evaluate( u ) );
//        		u += du;
//        	}
//        }
//    }
	public void	Prepare( CqSurface pSurface )
    {
		int iPoint;
    
        for( CqTrimCurve iCurve : m_aCurves )
        {
        	int cPoints = pSurface.TrimDecimation( iCurve );
   
        	iCurve.Clamp();
        	
        	float range = iCurve.aKnots().get( iCurve.cKnots() - 1 ).value - iCurve.aKnots().get( 0 ).value;
        	float u = iCurve.aKnots().get( 0 ).value;
        	float du = range / cPoints;
        	
        	for ( iPoint = 0; iPoint < cPoints; iPoint++ )
        	{
        		CqVector2D tmp = iCurve.Evaluate( u );
        		m_aCurvePoints.add( tmp );
        		int lastindex = m_aCurvePoints.size()-1;
        		int A;
        		int B;
        		if(lastindex!=0)
        		{
        			A = (int)(Math.floor((tmp.y*Maxindex)%((Maxindex))));
        			B = (int)(Math.floor((m_aCurvePoints.get(lastindex-1).y*Maxindex)%((Maxindex))));
        			
        			if(A == B )//通常バターン境界を超えない場合
        			{
        				m_aCurvePointsIndex[A].add( new p_int(lastindex) );
        			}
        			else if( A < B )//境界を越えてBのほうが大きい時
        			{
        				if(A !=0 )
        				{
        					for(int j = A ;j <= B; j++)//AとBの間の空間すべてにindexを追加する。
        						m_aCurvePointsIndex[j].add( new p_int(lastindex) );
        				}
        				else//Aが0のとき
        				{
        					for(int j = 0 ;j <= A; j++)//0からA間での追加
        						m_aCurvePointsIndex[j].add( new p_int(lastindex) );
        					for(int j = B ;j <= Maxindex-1; j++)//BからMaxindex間での追加
        						m_aCurvePointsIndex[j].add( new p_int(lastindex) );
        				}
        			}
        			else if ( B < A )
        			{
        				if( B !=0 )
        				{
	        				for(int j = B ;j <= A; j++)
	        					m_aCurvePointsIndex[j].add( new p_int(lastindex) );
        				}
        				else
        				{
        					for(int j = 0 ;j <= B; j++)//0からB間での追加
        						m_aCurvePointsIndex[j].add( new p_int(lastindex) );
        					for(int j = A ;j <= Maxindex-1; j++)//AからMaxindex間での追加
        						m_aCurvePointsIndex[j].add( new p_int(lastindex) );
	        			}
        			}
        			
            		
        		}
        		        		
        		u += du;
        	}
        }
        
    }
//	public void	Prepare( CqSurface pSurface )
//    {
//		int iPoint;
//    
//        for( CqTrimCurve iCurve : m_aCurves )
//        {
//        	int cPoints = pSurface.TrimDecimation( iCurve );
//   
//        	iCurve.Clamp();
//        	
//        	float range = iCurve.aKnots().get( iCurve.cKnots() - 1 ).value - iCurve.aKnots().get( 0 ).value;
//        	float u = iCurve.aKnots().get( 0 ).value;
//        	float du = range / cPoints;
//        	
//        	for ( iPoint = 0; iPoint < cPoints; iPoint++ )
//        	{
//        		CqVector2D tmp = iCurve.Evaluate( u );
//        		m_aCurvePoints.add( tmp );
//        		int lastindex = m_aCurvePoints.size()-1;
//        		int A = (int)(Math.floor((tmp.y*Maxindex)%((Maxindex))));
//        		m_aCurvePointsIndex[A].add( new p_int(lastindex) );
//        		
//        		int B;
//        		if(lastindex!=0)
//        		{
//        			B = (int)(Math.floor((m_aCurvePoints.get(lastindex-1).y*Maxindex)%((Maxindex))));
//        		        		
//	        		if(A != B )//境界値の場合の処理　一つ前が入っていたら追加する。
//	        		{
////	        			for(int j = A + 1 ;j <= B;j++)
//	        			m_aCurvePointsIndex[B].add( new p_int(lastindex) );
//	        		}
//        		}
//        		u += du;
//        	}
//        }
//        //最初と最後が境界値の場合の処理
//        int A = (int)(Math.floor((m_aCurvePoints.get(0).y*Maxindex)%((Maxindex))));
//        int B = (int)(Math.floor((m_aCurvePoints.get(m_aCurvePoints.size()-1).y*Maxindex)%((Maxindex))));
//        if(A != B)
//        {
//        	m_aCurvePointsIndex[B].add( new p_int(0) );
//        }
//        
//        
//    }
	
	/**
	 * テストポイントがトリミング図形の内側にあるか外側にあるかを判定
	 * 
	 * @param	v	位置を調べる点の座標
	 * @return	oddNodes	テストポイントの場所 ( 内側=true / 外側=false ) 	
	 */
//	public	boolean	TrimPoint( final CqVector2D v )
//    {
//        float x = v.x;
//        float y = v.y;
//        int i, j;
//    	boolean oddNodes = false;
//        int size = m_aCurvePoints.size();
//        for ( i = 0, j = size - 1; i < size; j = i++ )
//        {
//    		float ax = m_aCurvePoints.get( i ).x;
//    		float ay = m_aCurvePoints.get( i ).y;
//    		float bx = m_aCurvePoints.get( j ).x;
//    		float by = m_aCurvePoints.get( j ).y;
//    		
//    		// Does this line segment span the point in y?
//    		// y値より、トリミング図形のどの線分で判定を行うかを決定する。 
//    		if ( (( ay < y ) && ( by >= y )) || (( by < y ) && ( ay >= y )) )
//    		{
//    			// Does the horizontal intersection lie on the right side of the point?
//    			//　判定する線分の右側にあるかどうかを調べる。
//    			if( ax + ( y - ay ) / ( by - ay ) * ( bx - ax ) < x )
//    				// Then flip the state.
//    				oddNodes = ! oddNodes;
//    		}
//        }   
//       return( oddNodes );
//        
//        //** アルゴリズム説明 ***********************************************************************
//    	/* 
//    	 * This works by checking line segements in turn, and finding the ones 					*
//    	 * 	 that span the sample point in y. For those line segments, a test is 				*
//    	 *   made to see which side of the line segment the point lies, and on which side		*
//    	 *   of the sample point the intersection with the line segment is.						*
//    	 *   For each crossing on the same side of the sample point, a state flag is flipped.	*
//    	 *	 If the state is inside at the end, then the sample is inside the polygon.			*
//    	 *   See http://www.alienryderflex.com/polygon/											*
//    	 *   																					*
//    	 ****************************************************************************************/
//    }
	public	boolean	TrimPoint( final CqVector2D v )
    {
        float x = v.x;
        float y = v.y;
        int i, j;
    	boolean oddNodes = false;
    	int index = (int)(Math.floor((v.y*Maxindex)%((Maxindex))));
        int size = m_aCurvePointsIndex[index].size();
        int MaxSize = m_aCurvePoints.size()-1;
        for(i= 0; i < size ; i++)
        {
        	int front_index = m_aCurvePointsIndex[index].get(i).value;
        	int back_index;
        	if(front_index == 0)back_index = MaxSize;
        	else back_index = front_index - 1;
        	
        	float ax = m_aCurvePoints.get( front_index ).x;
    		float ay = m_aCurvePoints.get( front_index ).y;
    		float bx = m_aCurvePoints.get( back_index ).x;
    		float by = m_aCurvePoints.get( back_index ).y;
    		// Does this line segment span the point in y?
    		// y値より、トリミング図形のどの線分で判定を行うかを決定する。 
    		if ( (( ay < y ) && ( by >= y )) || (( by < y ) && ( ay >= y )) )
    		{
    			// Does the horizontal intersection lie on the right side of the point?
    			//　判定する線分の右側にあるかどうかを調べる。
    			if( ax + ( y - ay ) / ( by - ay ) * ( bx - ax ) < x )
    				// Then flip the state.
    				oddNodes = ! oddNodes;
    		}
        }   
//        for ( i = 0, j = size - 1; i < size; j = i++ )
//        {
//        	int pindex = m_aCurvePointsIndex[index].get(i).value;
//    		float ax = m_aCurvePoints.get( i ).x;
//    		float ay = m_aCurvePoints.get( i ).y;
//    		float bx = m_aCurvePoints.get( j ).x;
//    		float by = m_aCurvePoints.get( j ).y;
//    		
//    		// Does this line segment span the point in y?
//    		// y値より、トリミング図形のどの線分で判定を行うかを決定する。 
//    		if ( (( ay < y ) && ( by >= y )) || (( by < y ) && ( ay >= y )) )
//    		{
//    			// Does the horizontal intersection lie on the right side of the point?
//    			//　判定する線分の右側にあるかどうかを調べる。
//    			if( ax + ( y - ay ) / ( by - ay ) * ( bx - ax ) < x )
//    				// Then flip the state.
//    				oddNodes = ! oddNodes;
//    		}
//        }

       return( oddNodes );
        
        //** アルゴリズム説明 ***********************************************************************
    	/* 
    	 * This works by checking line segements in turn, and finding the ones 					*
    	 * 	 that span the sample point in y. For those line segments, a test is 				*
    	 *   made to see which side of the line segment the point lies, and on which side		*
    	 *   of the sample point the intersection with the line segment is.						*
    	 *   For each crossing on the same side of the sample point, a state flag is flipped.	*
    	 *	 If the state is inside at the end, then the sample is inside the polygon.			*
    	 *   See http://www.alienryderflex.com/polygon/											*
    	 *   																					*
    	 ****************************************************************************************/
    } 
	
	
	/**
	 * 
	 * 線分がトリミング図形と交点を持つかどうかの判定を行う。
	 * 
	 * @param v1	線分を構成する点座標
	 * @param v2	線分を構成する点座標
	 * @return	true(交点あり) / false(その他)
	 */
	public	boolean	LineIntersects(final CqVector2D v1, final CqVector2D v2)
	{
		float x1 = v1.x;
		float y1 = v1.y;
		float x2 = v2.x;
		float y2 = v2.y;

		int i, j;
	    int size = m_aCurvePoints.size();
	    for ( i = 0, j = size - 1; i < size; j = i++ )
	    {
			float x3 = m_aCurvePoints.get( i ).x;
			float y3 = m_aCurvePoints.get( i ).y;
			float x4 = m_aCurvePoints.get( j ).x;
			float y4 = m_aCurvePoints.get( j ).y;

			float d = (x2-x1)*(y4-y3) - (y2-y1)*(x4-x3);
			if( d == 0.0f ) 
				continue;	//0のときは2つの直線が平行の場合

			
			float r = ((y1-y3)*(x4-x3) - (x1-x3)*(y4-y3)) / d;
			float s = ((y1-y3)*(x2-x1) - (x1-x3)*(y2-y1)) / d;
			// 二つの線分が交差するかの判定
			if( (r >= 0.0f) && (s >= 0.0f) && (r <= 1.0f) && (s <= 1.0f))
				return(true);	
		}
		return(false);
	}

	
	private STLVector<CqTrimCurve>	m_aCurves = new STLVector<CqTrimCurve>( CqTrimCurve.class );
	private STLVector<CqVector2D>	m_aCurvePoints = new STLVector<CqVector2D>( CqVector2D.class );
	private int Maxindex = 1000;
	private STLArray<p_int>	m_aCurvePointsIndex[] = new STLArray[Maxindex];

}
