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


/**
 * 
 * 4次元ベクトル<br>
 * 2次元/3次元ベクトルからの変換や、単位ベクトル化するメソッドを持つ。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqVector4D{

	private static final long serialVersionUID = -739144048439837317L;

	public float x;
	public float y;
	public float z;
	public float w;
	//************************************************************************//
	//*** コンストラクタの定義
	//************************************************************************//
	
	/**
	 * デフォルトコンストラクタ。各要素を初期化する。
	 */
	public CqVector4D() {
		this.x = 0.0f;
		this.y = 0.0f;
		this.z = 0.0f;
		this.w = 1.0f;
	}
	
	/**
	 * floatの引数値をもとに４次元ベクトルを作成。
	 * @param x	行列の第１要素
	 * @param y	行列の第２要素
	 * @param z	行列の第３要素
	 * @param w	行列の第４要素
	 */
	public CqVector4D( float x, float y, float z, float w ){
		this.x = x;
		this.y = y;
		this.z = z;
		this.w = w;
	}
	
	/**
	 * ３次元ベクトルをもとに４次元ベクトルを作成。<br>
	 * 第４要素には必ず１が代入される。
	 * @param From	３次元ベクトル
	 */
	public CqVector4D( final CqVector3D From ){
		
	    this.x = From.x;
	    this.y = From.y;
	    this.z = From.z;
	    this.w = 1.0f;
	}
	
	/**
	 * float型４次元配列をもとに４次元ベクトルを作成。
	 * @param array	float型４次元配列
	 */
	public CqVector4D( final float[] array ){
		
		this.x = array[0];
		this.y = array[1];
		this.z = array[2];
		this.w = array[3];
	}
	
	/**
	 * コピーコンストラクタ
	 * @param v	コピー元
	 */
	public CqVector4D( final CqVector4D v ){
//		this.set(v);
		this.x = v.x;
		this.y = v.y;
		this.z = v.z;
		this.w = v.w;
	}
	
	//************************************************************************//
	//*** メソッドの定義
	//************************************************************************//
	
	/**
	 * 第１要素のセッタ
	 * @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;
	}
	
	/**
	 * 第４要素のセッタ
	 * @param h	第４要素
	 */
	public void h( float h ){
		this.w = h;
	}
	
	/**
	 * ベクトル長の２乗を返す
	 * @return	ベクトル長の２乗
	 */
	public float Magnitude2()
    {
	    if ( w == 1.0f ){
	        return ( ( x * x ) + ( y * y ) + ( z * z ) );
	    }
	    else{
	        return ( ( ( x * x ) + ( y * y ) + ( z * z ) ) / ( w * w ) );
	    }
    }
	
	/**
	 * ベクトルの長さを返す
	 * @return	ベクトルの長さ
	 */
	public float Magnitude()
    {
		return (float)Math.sqrt( Magnitude2() );
    }
	
	/**
	 *  単位化する 
	 */
	public void Unit()
	{
		this.w = this.Magnitude();
	}
	
	/**
	 * 均質化する
	 */
	public void Homogenize()
	{
        if ( w != 1.0 )
        {
            x /= w;
            y /= w;
            z /= w;
            w = 1.0f;
        }
	}
	
	
	
	//************************************************************************//
	//*** 演算子のオーバーロード
	//************************************************************************//
	
	/**
	 * 配列参照演算子。
	 * 0,1,2,3がそれぞれx,y,z,wに相当する。それ以外の値の場合はwを返す。
	 * 
	 * @param i	0,1,2,3の整数
	 * @return	相当するx,y,z,w
	 */
	public float valueAt( int i )
	{
        switch ( i )
        {
        case 0:		return ( x );
        case 1:		return ( y );
        case 2:		return ( z );
        case 3:		return ( w );
        default:	return ( w );
        }
	}
	
	/**
	 * 代入演算子。
	 * ３次元ベクトルから値をコピー。<br>
	 * 第４要素には必ず１が代入される
	 * 
	 * @param From	代入するCqVector3D
	 * @return	計算結果（this）
	 */
	public CqVector4D assignment( final CqVector3D From )
	{
	    this.x = From.x;
	    this.y = From.y;
	    this.z = From.z;
	    this.w = 1.0f;
	    
	    return this;
	}
	
	/**
	 * 代入演算子。
	 * ３次元ベクトルから値をコピー。<br>
	 * 第４要素には必ず１が代入される
	 * 
	 * @param From	代入するCqVector3D
	 * @return	計算結果（this）
	 */
	public CqVector4D assignment( final float a_x,final float a_y,final float a_z )
	{
	    this.x = a_x;
	    this.y = a_y;
	    this.z = a_z;
	    this.w = 1.0f;
	    
	    return this;
	}
	
	/**
	 * 代入演算子。
	 * 
	 * @param From	代入するCqVector4D
	 * @return	計算結果（this）
	 */
	public CqVector4D assignment( final CqVector4D From )
	{
	    this.x = From.x;
	    this.y = From.y;
	    this.z = From.z;
	    this.w = From.w;
	    
	    return this;
	}
	
	
	public CqVector4D assignment( final float a_x,final float a_y,final float a_z ,final float a_w)
	{
	    this.x = a_x;
	    this.y = a_y;
	    this.z = a_z;
	    this.w = a_w;
	    
	    return this;
	}
	
	/**
	 * 加算代入。
	 * 第４要素の割合を各要素に掛けて加算代入を行う。
	 * 
	 * @param From	加算するCqVector4D
	 * @return	計算結果（this）
	 */
	public CqVector4D assignAdd( final CqVector4D From )
	{
	    float Hom = w / From.w;

	    x += From.x * Hom;
	    y += From.y * Hom;
	    z += From.z * Hom;

	    return this;
	}
	
	/**
	 * 減算代入。
	 * 第４要素の割合を各要素に掛けて減算代入を行う。
	 * 
	 * @param From	減算するCqVector4D
	 * @return	計算結果（this）
	 */
	public CqVector4D assignSub( final CqVector4D From )
	{
	    float Hom = w / From.w;

	    x -= From.x * Hom;
	    y -= From.y * Hom;
	    z -= From.z * Hom;

	    return this;
	}
	
	/**
	 * 剰余算代入。
	 * ただし、CqVector4Dでは剰余算(%)は「外積」演算として扱われる。
	 * 
	 * @param From	外積対象のCqVector4D
	 * @return	計算結果（this）
	 */
	public CqVector4D assignMod( final CqVector4D From )
	{
	    CqVector4D	A = new CqVector4D( this );
	    CqVector4D	B = new CqVector4D( From );

	    A.Homogenize();
	    B.Homogenize();

	    x = ( A.y * B.z ) - ( A.z * B.y );
	    y = ( A.z * B.x ) - ( A.x * B.z );
	    z = ( A.x * B.y ) - ( A.y * B.x );

	    return this;
	}
	
	/**
	 * 乗算代入。
	 * 第１〜３要素までをスカラ倍する。
	 * 
	 * @param Scale	乗算する値
	 * @return	計算結果（this）
	 */
	public CqVector4D assignMul( final float Scale )
	{
		x *= Scale;
		y *= Scale;
		z *= Scale;
		
		return this;
	}
	
	/**
	 * 除算代入。
	 * ただし、CqVector4Dでは第４要素のスカラ倍を示す。
	 * 
	 * @param Scale	スカラ値
	 * @return	計算結果（this）
	 */
	public CqVector4D assignDiv( final float Scale )
	{
		w *= Scale;
		return this;
	}
	
	/**
	 * 比較演算子。
	 * 全ての要素が等しいときにtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVectorD
	 * @return	true:  全ての要素が等しい<br>
	 * 			false: いずれかの要素が等しくない
	 */
	public boolean equals( final CqVector4D Cmp )
	{
		// exact equality of floats isn't actually very useful because of slight
		// differences due to rounding errors. a fuzzy compare is generally more useful.
		// if the vectors have different h values the above is unlikely to
		// return true, so we might as well just test for exact equality of each component.
		return ((x == Cmp.x) &&
				 (y == Cmp.y) &&
				 (z == Cmp.z) &&
				 (w == Cmp.w) );
	}
	
	/**
	 * 右比較演算子。
	 * 第４要素の割合を各要素に掛けた上で、他の要素が引数のそれより大きいか等しければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector4D
	 * @return	true：	全要素が大きいか等しい<br>
	 * 			false：	いずれかの要素が小さい
	 */
	public boolean ge ( final CqVector4D Cmp ){
		
	    float Hom = w / Cmp.w;

	    return (( x >= ( Cmp.x * Hom ) ) &&
	             ( y >= ( Cmp.y * Hom ) ) &&
	             ( z >= ( Cmp.z * Hom ) ) );
	}
	
	/**
	 * 左比較演算子。
	 * 第４要素の割合を各要素に掛けた上で、他の要素が引数のそれより小さいか等しければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector4D
	 * @return	true：	全要素が小さいか等しい<br>
	 * 			false：	いずれかの要素が大きい
	 */
	public boolean le ( final CqVector4D Cmp ){
		
	    float Hom = w / Cmp.w;

	    return (( x <= ( Cmp.x * Hom ) ) &&
	             ( y <= ( Cmp.y * Hom ) ) &&
	             ( z <= ( Cmp.z * Hom ) ) );
	}
	
	/**
	 * 右比較演算子。
	 * 第４要素の割合を各要素に掛けた上で、他の要素が引数のそれより大きければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector4D
	 * @return	true：	全要素が大きい<br>
	 * 			false：	いずれかの要素が小さいか等しい
	 */
	public boolean gt ( final CqVector4D Cmp ){
		
	    float Hom = w / Cmp.w;

	    return (( x > ( Cmp.x * Hom ) ) &&
	             ( y > ( Cmp.y * Hom ) ) &&
	             ( z > ( Cmp.z * Hom ) ) );
	}
	
	/**
	 * 左比較演算子。
	 * 第４要素の割合を各要素に掛けた上で、他の要素が引数のそれより小さければtrueを返す。
	 * 
	 * @param Cmp	比較対象のCqVector4D
	 * @return	true：	全要素が小さい<br>
	 * 			false：	いずれかの要素が大きいか等しい
	 */
	public boolean lt ( final CqVector4D Cmp ){
		
	    float Hom = w / Cmp.w;

	    return (( x < ( Cmp.x * Hom ) ) &&
	             ( y < ( Cmp.y * Hom ) ) &&
	             ( z < ( Cmp.z * Hom ) ) );
	}
	
	/**
	 * 乗算演算子。
	 * 第１〜３要素までをスカラ倍する。
	 * 
	 * @param f	乗算する値
	 * @return	計算結果
	 */
	public CqVector4D mul( final float f )
	{
		CqVector4D r= new CqVector4D( this );
		return	r.assignMul(f);
	}
	
	/**
	 * 除算演算子。
	 * ただし、CqVector4Dでは第４要素のスカラ倍を示す。
	 * 
	 * @param f	スカラ値
	 * @return	計算結果
	 */
	public CqVector4D div( final float f )
	{
		CqVector4D r= new CqVector4D( this );
		return	r.assignDiv(f);
	}
	
	/**
	 * 逆除算演算子。
	 * 除算演算と同様の計算。
	 * 
	 * @param f	スカラ値
	 * @return	計算結果
	 */
	public CqVector4D divInv( final float f )
	{
		CqVector4D r= new CqVector4D( this );
		return	r.assignDiv(f);
	}
	
	/**
	 * 加算演算子。
	 * 第４要素の割合を各要素に掛けて加算代入を行う。
	 * 
	 * @param b	加算するCqVector4D
	 * @return	計算結果
	 */
	public CqVector4D add( final CqVector4D b )
	{		
		CqVector4D r= new CqVector4D( this );
		return	r.assignAdd(b);
		
	}
	
	/**
	 * 加算演算子。
	 * 第４要素の割合を各要素に掛けて加算代入を行う。
	 * 
	 * @param b	加算するCqVector4D
	 * @return	計算結果
	 */
	public CqVector4D sub( final CqVector4D b )
	{
		CqVector4D r= new CqVector4D( this );
		return	r.assignSub(b);
	}
	

	/**
	 * 乗算演算子。
	 * ただし、CqVector3Dでは乗算(*)は「内積」演算として扱われる。
	 * 
	 * @param b	内積対象のCqVector4D
	 * @return	計算結果
	 */
	public float mul( final CqVector4D b )
	{
	    CqVector4D A = new CqVector4D( this );
	    CqVector4D B = new CqVector4D( b );

	    A.Homogenize();
	    B.Homogenize();

	    return (( A.x * B.x ) +
	             ( A.y * B.y ) +
	             ( A.z * B.z ) );
	}
	
	/**
	 * 剰余算演算子。
	 * ただし、CqVector4Dでは剰余算(%)は「外積」演算として扱われる。
	 * 
	 * @param b	外積対象のCqVector4D
	 * @return	計算結果
	 */
	public CqVector4D mod( final CqVector4D b )
	{
	    CqVector4D Temp = new CqVector4D( this );
	    Temp.assignMod(b);
	    return ( Temp );
	}
	
	
}
