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


/**
 * 
 * 3次元ベクトル<br>
 * 2次元/4次元ベクトルからの変換や、単位ベクトル化するメソッドを持つ。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqVector3D {
	
	private static final long serialVersionUID = 4782309621216697640L;
	public float	x;	///< X component.
	public float	y;	///< Y component.
	public float	z;
	//************************************************************************//
	//*** コンストラクタの定義
	//************************************************************************//
	
	/**
	 * デフォルトコンストラクタは何もしない
	 */
	public CqVector3D() {}
	
	/**
	 * コピーコンストラクタ
	 * @param From	コピー元
	 */
	public CqVector3D( CqVector3D From ){
//		this.assignment( From );
		x = From.x;
		y = From.y;
		z = From.z;
	}
	
	/**
	 * ２次元ベクトルをもとに初期化。各要素をコピーし、第３要素には０を入れる。
	 * @param From	２次元ベクトル
	 */
	public CqVector3D( final CqVector2D From ){
		this.x = From.x;
		this.y = From.y;
		this.z = 0.0f;
	}
	
	/**
	 * RGBカラー情報をもとに初期化。各要素をコピーする。<br>
	 * 内部的には、第１要素からRGBの順番でコピーを行う。
	 * @param From	
	 */
	public CqVector3D( final CqColor From ){
//		this.assignment(From);
		x = From.m_fRed;
		y = From.m_fGreen;
		z = From.m_fBlue;
		
	}
	
	/**
	 * floatの引数値をもとに３次元ベクトルを作成。
	 * @param x	行列の第１要素
	 * @param y	行列の第２要素
	 * @param z	行列の第３要素
	 */
	public CqVector3D( float x, float y, float z ){
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	/**
	 * floatの引数値を全要素として３次元ベクトルを作成。
	 * @param f	行列の全要素の値
	 */
	public CqVector3D( float f ){
		this.x = f;
		this.y = f;
		this.z = f;
	}
	
	/**
	 * ４次元ベクトルの第１〜３要素をそれぞれ第４要素で割り、このオブジェクトの各要素に代入する。
	 * @param From	CqVector4D
	 */
	public CqVector3D( final CqVector4D From ){
//		this.assignment(From);
		if( From.w != 1.0 )
		{
			x = From.x / From.w;
			y = From.y / From.w;
			z = From.z / From.w;
		}
		else
		{
			x = From.x;
			y = From.y;
			z = From.z;
		}
	}
	
	/**
	 * ３次元配列をもとに初期化。各要素をコピーする
	 * @param array	float型３次元配列
	 */
	public CqVector3D( final float[] array ){
		this.x = array[0];
		this.y = array[1];
		this.z = array[2];
	}
	
	//************************************************************************//
	//*** メソッドの定義
	//************************************************************************//
	
	/**
	 * 第１要素のセッタ
	 * @param x	第１要素
	 */
	public void x( float x ){
		this.x = x;
	}
	
	/**
	 * 第２要素のセッタ
	 * @param y	第２要素
	 */
	public void y( float y ){
		this.y = y;
	}
	
	/**
	 * 第３要素のセッタ
	 * @param z	第３要素
	 */
	public void z( float z ){
		this.z = z;
	}
	
	/**
	 * ベクトル長の２乗を返す
	 * @return	ベクトル長の２乗
	 */
	public float Magnitude2()
	{
//		return this.lengthSquared();
        return ( ( x * x ) + ( y * y ) + ( z * z ) );

	}
	
	/**
	 * ベクトルの長さを返す
	 * @return	ベクトルの長さ
	 */
	public float Magnitude()
	{
//		return (this.Magnitude2() <= 0) ? 0.0f: this.length();
		float mag2 = Magnitude2();
        return ( mag2<=0.0f? 0.0f : (float)(Math.sqrt( mag2 )) );

	}
	
	/**
	 * 単位化する 
	 * @return	単位化された自分自身
	 */
	public CqVector3D Unit()
	{
		float Mag = Magnitude();
		if( Mag > 0.0f )
		{
			x /= Mag;
			y /= Mag;
			z /= Mag;
		}
		return this;
	}
	
	//************************************************************************//
	//*** 演算子のオーバーロード
	//************************************************************************//
	
	/**
	 * 配列参照演算子。
	 * 0,1,2がそれぞれx,y,zに相当する。それ以外の値の場合はzを返す。
	 * 
	 * @param i	0,1,2の整数
	 * @return	相当するx,y,z
	 */
	public float valueAt( int i ){
		switch ( i )
		{
		case 0:		return ( x );
		case 1:		return ( y );
		case 2:		return ( z );
		default:	return ( z );
		}
	}
	
	//nttdata 高速化
	/**
	 * 代入演算子。
	 * CqVector3Dから全要素をこのベクトルの各要素へコピー。 
	 * 
	 * @param From	CqVector3Dオブジェクト
	 * @return	計算結果（htis）
	 */
	public CqVector3D assignment( final float a_x,final float a_y ){
		
		x = a_x;
		y = a_y;
		z = 0.0f;
		
		return this;
	}
	/**
	 * 代入演算子。
	 * CqVector3Dから全要素をこのベクトルの各要素へコピー。 
	 * 
	 * @param From	CqVector3Dオブジェクト
	 * @return	計算結果（htis）
	 */
	public CqVector3D assignment( final float a_x,final float a_y, final float a_z ){
		
		x = a_x;
		y = a_y;
		z = a_z;
		
		return this;
	}
	// ここまで
	/**
	 * 代入演算子。
	 * CqVector3Dから全要素をこのベクトルの各要素へコピー。 
	 * 
	 * @param From	CqVector3Dオブジェクト
	 * @return	計算結果（htis）
	 */
	public CqVector3D assignment( final CqVector2D From ){
		
		x = From.x;
		y = From.y;
		z = 0.0f;
		
		return this;
	}
	/**
	 * 代入演算子。
	 * CqVector3Dから全要素をこのベクトルの各要素へコピー。 
	 * 
	 * @param From	CqVector3Dオブジェクト
	 * @return	計算結果（htis）
	 */
	public CqVector3D assignment( final CqVector3D From ){
		
		x = From.x;
		y = From.y;
		z = From.z;
		
		return this;
	}
	
	/**
	 * 代入演算子。
	 * ４次元ベクトルの代入。 <br>
	 * ４次元ベクトルの第１〜３要素をそれぞれ第４要素で割り、このオブジェクトの各要素に代入する。
	 * 
	 * @param From	CqVector4Dオブジェクト
	 * @return	代入結果（this）
	 */
	public CqVector3D assignment( final CqVector4D From ){
		
		if( From.w != 1.0 )
		{
			x = From.x / From.w;
			y = From.y / From.w;
			z = From.z / From.w;
		}
		else
		{
			x = From.x;
			y = From.y;
			z = From.z;
		}
		
		return this;
	}
	
	/**
	 * 代入演算子。
	 * CqColorからRBGをこのベクトルの各要素へコピー。 
	 * 
	 * @param From	CqColorオブジェクト
	 * @return	計算結果（htis）
	 */
	public CqVector3D assignment( final CqColor From ){
		
//		x = From.fRed();
//		y = From.fGreen();
//		z = From.fBlue();
		x = From.m_fRed;
		y = From.m_fGreen;
		z = From.m_fBlue;
		return this;
	}
	
	/**
	 * 加算代入。
	 * CqVector3D同士の各要素それぞれに加算代入を行う。
	 * 
	 * @param From	加算するCqVector3D
	 * @return	計算結果（this）
	 */
	public CqVector3D assignAdd( final CqVector3D From ){
		
		//this.add( this, From );
		this.x += From.x;
		this.y += From.y;
		this.z += From.z;
		
		return this;
	}
	
	
	/**
	 * 加算代入。
	 * このベクトルの全要素に対してfを加算代入する。
	 * 
	 * @param f	加算する値
	 * @return	計算結果（this）
	 */
	public CqVector3D assignAdd( final float f ){
		
		this.x += f;
		this.y += f;
		this.z += f;
		return this;
	}
	
	/**
	 * 減算代入。
	 * CqVector3D同士の各要素それぞれに減算代入を行う。
	 * 
	 * @param From	減算するCqVector3D
	 * @return	計算結果（this）
	 */
	public CqVector3D assignSub( final CqVector3D From ){
		
//		this.sub( this, From );
		this.x -= From.x;
		this.y -= From.y;
		this.z -= From.z;
		
		return this;
	}
	
	/**
	 * 減算代入。
	 * このベクトルの全要素に対してfを減算代入する。
	 * 
	 * @param f	減算する値
	 * @return	計算結果（this）
	 */
	public CqVector3D assignSub( final float f ){
		
		this.x -= f;
		this.y -= f;
		this.z -= f;
		return this;
	}
	
	/**
	 * 剰余算代入。
	 * ただし、CqVector3Dでは剰余算(%)は「外積」演算として扱われる。
	 * 
	 * @param From	外積対象のCqVector3D
	 * @return	計算結果（this）
	 */
	public CqVector3D assignMod( final CqVector3D From ){
		
//		this.cross( this, From );
		CqVector3D vecTemp  = new CqVector3D(this);
	    x = ( vecTemp.y * From.z ) - ( vecTemp.z * From.y );
	    y = ( vecTemp.z * From.x ) - ( vecTemp.x * From.z );
	    z = ( vecTemp.x * From.y ) - ( vecTemp.y * From.x );
		return this;
	}
	
	/**
	 * 乗算代入。
	 * ベクタのスカラ倍を計算する。
	 * 
	 * @param f	スカラ値
	 * @return	計算結果（this）
	 */
	public CqVector3D assignMul( float f ){
//		this.scale(f);
		this.x *= f;
		this.y *= f;
		this.z *= f;
		
		return this;
	}
	
	/**
	 * 乗算代入。
	 * ベクタ同士の各要素を掛け合わせる。
	 * 
	 * @param Scale	乗算するCqVector3D
	 * @return	計算結果（this）
	 */
	public CqVector3D assignMul(final CqVector3D Scale ){
		
		this.x *= Scale.x;
		this.y *= Scale.y;
		this.z *= Scale.z;
		return this;
	}
	
	/**
	 * 除算代入。
	 * ベクタ同士の各要素を割る。
	 * 
	 * @param Scale	除算するCqVector3D
	 * @return	計算結果（this）
	 */
	public CqVector3D assignDiv( final CqVector3D Scale ){
		
		this.x /= Scale.x;
		this.y /= Scale.y;
		this.z /= Scale.z;
		return this;
	}
	
	/**
	 * 除算代入。
	 * ベクタの各要素を指定したスカラ値で割る。
	 * 	
	 * @param Scale	除算する値
	 * @return	計算結果（this）
	 */
	public CqVector3D assignDiv( float Scale ){
		
		this.x /= Scale;
		this.y /= Scale;
		this.z /= Scale;
		return this;
	}
	
	/**
	* 比較演算子。
	* 全ての要素が等しいときにtrueを返す。
	* 
	* @param Cmp	比較対象のCqVector3D
	* @return	true:  全ての要素が等しい<br>
	* 			false: いずれかの要素が等しくない
	*/
	public boolean equals( final CqVector3D Cmp ){
		return ( ( x == Cmp.x ) && ( y == Cmp.y ) && ( z == Cmp.z ) );
//		return this.equals(Cmp);
	}
	
	/**
	 * 右比較演算子。　<br>
	 * 全要素を比較し、全てが引数のそれより大きいか等しければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector3D
	 * @return	true：	全要素が大きいか等しい<br>
	 * 			false：	いずれかの要素が小さい
	 */
	public boolean ge( final CqVector3D Cmp )
	{
		return ( ( x >= Cmp.x ) && ( y >= Cmp.y ) && ( z >= Cmp.z ) );
	}
	
	/**
	 * 左比較演算子。　<br>
	 * 全要素を比較し、全てが引数のそれより小さいか等しければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector3D
	 * @return	true：	全要素が小さいか等しい<br>
	 * 			false：	いずれかの要素が大きい
	 */
	public boolean le( final CqVector3D Cmp )
	{
		return ( ( x <= Cmp.x ) && ( y <= Cmp.y ) && ( z <= Cmp.z ) );
	}
	
	/**
	 * 右比較演算子。　<br>
	 * 全要素を比較し、全てが引数のそれより大きければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector3D
	 * @return	true：	全要素が大きい<br>
	 * 			false：	いずれかの要素が小さいか等しい
	 */
	public boolean gt( final CqVector3D Cmp ) 
	{
		return ( ( x > Cmp.x ) && ( y > Cmp.y ) && ( z > Cmp.z ) );
	}
	
	/**
	 * 左比較演算子。　<br>
	 * 全要素を比較し、全てが引数のそれより小さければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector3D
	 * @return	true：	全要素が小さい<br>
	 * 			false：	いずれかの要素が大きいか等しい
	 */
	public boolean lt( final CqVector3D Cmp )
	{
		return ( ( x < Cmp.x ) && ( y < Cmp.y ) && ( z < Cmp.z ) );
	}
	
	/**
	 * 加算演算子。
	 * 全要素それぞれにfを加算する。
	 * 
	 * @param f	加算する値
	 * @return	計算結果
	 */
	public CqVector3D add( float f )
	{
		CqVector3D r = new CqVector3D( x,y,z );
		return r.assignAdd(f);	
	}
	
	/**
	 * 減算演算子。
	 * 全要素それぞれからfを減算する。
	 * 
	 * @param f	減算する値
	 * @return	計算結果
	 */
	public CqVector3D sub( float f )
	{
		CqVector3D r = new CqVector3D( x,y,z );
		return r.assignSub(f);	
	}
	
	/**
	 * 逆減算演算子。
	 * 全要素それぞれをfから減算する。
	 * 
	 * @param f	減算する値
	 * @return	計算結果
	 */
	public CqVector3D subInv( float f )
	{
		return new CqVector3D( f - this.x, f - this.y, f - this.z );	
	}
	
	/**
	 * 乗算演算子。
	 * 全要素それぞれにfを乗算する。
	 * 
	 * @param f	加算する値
	 * @return	計算結果
	 */
	public CqVector3D mul( float f )
	{
		CqVector3D r = new CqVector3D( x,y,z );
		return r.assignMul(f);	
	}
	
	/**
	 * 除算演算子。
	 * 全要素それぞれからfを除算する。(v/f)
	 * 
	 * @param f	除算する値
	 * @return	計算結果
	 */
	public CqVector3D div( float f )
	{
		CqVector3D r = new CqVector3D( x,y,z );
		return r.assignDiv(f);	
	}
	
	/**
	 * 逆除算演算子。
	 * 全要素それぞれをfから除算する。(f/v)
	 * 
	 * @param f	除算する値
	 * @return	計算結果
	 */
	public CqVector3D divInv( float f )
	{
		return new CqVector3D( f / this.x, f / this.y, f / this.z );	
	}
	
	/**
	 * 加算演算子。
	 * 各要素それぞれを加算する。
	 * 
	 * @param b	加算するCqVector3D
	 * @return	計算結果
	 */
	public CqVector3D add( final CqVector3D b ){
		CqVector3D r = new CqVector3D( x,y,z );
		return r.assignAdd(b);
	}
	
	/**
	 * 減算演算子。
	 * 各要素それぞれを減算する。
	 * 
	 * @param b	減算するCqVector3D
	 * @return	計算結果
	 */
	public CqVector3D sub( final CqVector3D b ){
		CqVector3D r = new CqVector3D( x,y,z );
		return r.assignSub(b);
	}
	
	/**
	 * 除算演算子。
	 * 各要素それぞれを減算する。
	 * 
	 * @param b	除算するCqVector3D
	 * @return	計算結果
	 */
	public CqVector3D div( final CqVector3D b ){
		CqVector3D r = new CqVector3D( x,y,z );
		return r.assignDiv(b);
	}
	
	/**
	 * 乗算演算子。
	 * ただし、CqVector3Dでは乗算(*)は「内積」演算として扱われる。
	 * 
	 * @param b	内積対象のCqVector3D
	 * @return	計算結果
	 */
	public float mul( final CqVector3D b ){
//		return this.dot(b);
		return (x * b.x + y * b.y + z * b.z);
	}
	
	/**
	 * 剰余算演算子。
	 * ただし、CqVector3Dでは剰余算(%)は「外積」演算として扱われる。
	 * 
	 * @param b	外積対象のCqVector3D
	 * @return	計算結果
	 */
	public CqVector3D mod( final CqVector3D b ){
//		CqVector3D r =  new CqVector3D();
//		r.cross( this, b );
//		return r;
	    return ( new CqVector3D( ( y * b.z ) - ( z * b.y ),
                ( z * b.x ) - ( x * b.z ),
                ( x * b.y ) - ( y * b.x ) ) );

	}
	
	/**
	 * 符号反転。 <br>
	 * 全要素の符号を反転した値で新しいCqVector3Dオブジェクトを作成して返す
	 * 
	 * @return	符号が逆の新しいCqVector3Dオブジェクト
	 */
	public CqVector3D negative()
	{
//		CqVector3D r =  new CqVector3D(this);
//		r.negate();
//		return  r;
        return (new  CqVector3D( -x, -y, -z ) );

	}
	
	/**
	 * 2つのベクトルの外積を求める
	 * modと異なり２つの外積計算結果を自分自身に反映する
	 * @param r ベクトル1
	 * @param v ベクトル2
	 */
	public void exterior(final CqVector3D r,final CqVector3D v){
        double x,y;

        x = r.y*v.z - r.z*v.y;
        y = v.x*r.z - v.z*r.x;
        this.z = r.x*v.y - r.y*v.x;
        this.x = (float)x;
        this.y = (float)y;
	}
}
