/**
*  ofPoint4f.h
*  by stefanix
*/


#ifndef _OF_POINT4f
#define _OF_POINT4f

#include "ofVec4f.h"



class ofPoint4f : public ofPoint {


  public:
  
    float w;
    
    
    ofPoint4f( const float _x=0.0f, 
               const float _y=0.0f, 
               const float _z=0.0f, 
               const float _w=0.0f )
    { 
        x = _x;
        y = _y;
        z = _z;
        w = _w;
    }
	    


    
    // Getters and Setters.
    // 
    //
    void set( float _x, float _y, float _z, float _w ) {
        x = _x;
        y = _y;
        z = _z;
        w = _w;
    }
    
    void set( const ofPoint4f& pnt ) {
        x = pnt.x;
        y = pnt.y;
        z = pnt.z;
        w = pnt.w;
    }

    float& operator[]( const int &i ) { 
        switch(i) {
            case 0:  return x;
            case 1:  return y;
            case 2:  return z;
            case 3:  return w;
            default: return x;
        }
    }



    // Check similarity/equality.
    //
    //    
    bool operator==( const ofPoint4f pnt ) { 
        return (x == pnt.x) && (y == pnt.y) && (z == pnt.z) && (w == pnt.w);
    }

    bool operator!=( const ofPoint4f pnt ) { 
        return (x != pnt.x) || (y != pnt.y) || (z != pnt.z) || (w != pnt.w);
    }

    bool match( const ofPoint4f pnt, float tollerance=0.0001) {
        return (fabs(x - pnt.x) < tollerance) 
            && (fabs(y - pnt.y) < tollerance)
            && (fabs(z - pnt.z) < tollerance)
            && (fabs(w - pnt.w) < tollerance);
    }
        
    

    // Translate point by vector.
    //        
    //
    ofPoint4f operator+( const ofVec4f& vec ) const { 
        return ofPoint4f( x+vec.x, y+vec.y, z+vec.z, w+vec.w); 
    } 
    
    ofPoint4f& operator+=( const ofVec4f& vec ) { 
        x += vec.x;
        y += vec.y;
        z += vec.z;
        w += vec.w;
        return *this; 
    } 
	
	ofPoint4f operator+( const float f ) const { 
        return ofPoint4f( x+f, y+f, z+f, w+f );
    } 
    
    ofPoint4f& operator+=( const float f ) { 
        x+=f;
        y+=f;
        z+=f;
        w+=f;
        return *this; 
    }   

    ofPoint4f operator-( const ofVec4f& vec ) const { 
        return ofPoint4f( x-vec.x, y-vec.y, z-vec.z, w-vec.w ); 
    }
           
    ofPoint4f& operator-=( const ofVec4f& vec ) { 
        x -= vec.x;
        y -= vec.y;
        z -= vec.z;
        w -= vec.w;
        return *this; 
    }
	
	ofPoint4f operator-( const float f ) const { 
        return ofPoint4f( x-f, y-f, z-f, w-f );
    } 
    
    ofPoint4f& operator-=( const float f ) { 
        x-=f;
        y-=f;
        z-=f;
        w-=f;
        return *this; 
    }   
    
    ofPoint4f operator-() const { 
        return ofPoint4f( -x, -y, -z, -w ); 
    }    
     
    

    // Scale point (by scalar or vector).
    //
    //
    ofPoint4f operator*( const float f ) const { 
        return ofPoint4f( x*f, y*f, z*f, w*f );
    } 
    
    ofPoint4f& operator*=( const float f ) { 
        x*=f;
        y*=f;
        z*=f;
        w*=f;
        return *this; 
    }    
        
    ofPoint4f operator*( const ofVec4f& vec ) const { 
        return ofPoint4f( x*vec.x, y*vec.y, z*vec.z, w*vec.w ); 
    } 
    
    ofPoint4f& operator*=( const ofVec4f& vec ) { 
        x*=vec.x;
        y*=vec.y;
        z*=vec.z;
        w*=vec.w;
        return *this; 
    }
    
    ofPoint4f operator/( const float f ) const { 
        return ofPoint4f( x/f, y/f, z/f, w/f );
    } 
    
    ofPoint4f& operator/=( const float f ) { 
        x/=f;
        y/=f;
        z/=f;
        w/=f;
        return *this; 
    }    
        
    ofPoint4f operator/( const ofVec4f& vec ) const
    { 
        return ofPoint4f( x/vec.x, y/vec.y, z/vec.z, w/vec.w ); 
    } 
    
    ofPoint4f& operator/=( const ofVec4f& vec ) { 
        x/=vec.x;
        y/=vec.y;
        z/=vec.z;
        w/=vec.w;
        return *this; 
    }     
    
 
    
    /**
    * Return the difference vector between two points.
    */
    ofVec4f operator-( const ofPoint4f& pnt ) const { 
        return ofVec4f( x-pnt.x, y-pnt.y, z-pnt.z, w-pnt.w ); 
    }


    
    // Distance between two points.
    //
    //
    float distance( const ofPoint4f& pnt) const { 
        float vx = x-pnt.x;
        float vy = y-pnt.y;
        float vz = z-pnt.z;
        float vw = w-pnt.w;
        return (float)sqrt( vx*vx + vy*vy + vz*vz + vw*vw ); 
    } 
    
    float distanceSquared( const ofPoint4f& pnt ) const { 
        float vx = x-pnt.x;
        float vy = y-pnt.y;
        float vz = z-pnt.z;
        float vw = w-pnt.w;
        return vx*vx + vy*vy + vz*vz + vw*vw;  
    } 
    
    
    
    // Linear interpolation.
    //
    //
    /**
    * p==0.0 results in this point, p==0.5 results in the 
    * midpoint, and p==1.0 results in pnt being returned.
    */      
    ofPoint4f interpolate( const ofPoint4f& pnt, float p ) const {
        return ofPoint4f( x*(1-p) + pnt.x*p, 
                          y*(1-p) + pnt.y*p, 
                          z*(1-p) + pnt.z*p,
                          w*(1-p) + pnt.w*p );
    }

    ofPoint4f middle( const ofPoint4f& pnt ) const {
        return ofPoint4f( (x+pnt.x)/2.0, (y+pnt.y)/2.0, 
                          (z+pnt.z)/2.0, (w+pnt.w)/2.0 );
    }



    // Average (centroid) among points.
    // (Addition is sometimes useful for calculating averages too)
    //
    //
    ofPoint4f average( const ofPoint4f* points, int num ) const {
        ofPoint4f centroid;
        for( int i=0; i<num; i++) {
            centroid.x += points[i].x;
            centroid.y += points[i].y;
            centroid.z += points[i].z;
            centroid.w += points[i].w;
        }
        centroid.x /= num;
        centroid.y /= num;
        centroid.z /= num;
        centroid.w /= num;
        return centroid;
    }
    
    ofPoint4f operator+( const ofPoint4f& pnt ) const { 
        return ofPoint4f( x+pnt.x, y+pnt.y, z+pnt.z,  w+pnt.w ); 
    } 
    
    ofPoint4f& operator+=( const ofPoint4f& pnt ) { 
        x+=pnt.x;
        y+=pnt.y;
        z+=pnt.z;
        w+=pnt.w;
        return *this; 
    }

	
};



#endif
