/**
*  ofPoint2f.h
*  by stefanix
*/

#ifndef _OF_POINT2f
#define _OF_POINT2f

#include "ofVec2f.h"



class ofPoint2f {

  public:
	
	
    //----------------  Constructors
    
    ofPoint2f( const float _x = 0.0f, const float _y = 0.0f )
    { 
        x = _x;
        y = _y;
    }
	
    ofPoint2f( const ofPoint2f& pnt )  
    {
        x = pnt.x;
        y = pnt.y;
    }
    

    
    
    //----------------  Data Members
    float x;
    float y;
    


   
    //---------------- Methods


    // Getters and Setters.
    //
    //   
    void set( float _x, float _y )
    {
        x = _x;
        y = _y;
    }
    
    void set( const ofPoint2f& pnt )
    {
        x = pnt.x;
        y = pnt.y;
    }

    float& operator[]( const int &i )
    { 
        switch(i) {
            case 0:  return x;
            case 1:  return y;
            default: return x;
        }
    }
    
    
            
    // Check similarity/equality.
    //
    //        
    bool operator==( const ofPoint2f pnt )           
    { 
        return (x == pnt.x) && (y == pnt.y);
    }

    bool operator!=( const ofPoint2f pnt )           
    { 
        return (x != pnt.x) || (y != pnt.y);
    }
    
    bool match( const ofPoint2f pnt, float tollerance=0.0001) {
        return (fabs(x - pnt.x) < tollerance) 
            && (fabs(y - pnt.y) < tollerance);
    }
    


    // Translate point by vector.
    //
    //     
    ofPoint2f operator+( const ofVec2f& vec ) const          
    { 
        return ofPoint2f( x+vec.x, y+vec.y); 
    } 
    
    ofPoint2f& operator+=( const ofVec2f& vec )              
    { 
        x += vec.x;
        y += vec.y;
        return *this; 
    } 

    ofPoint2f operator-( const ofVec2f& vec ) const          
    { 
        return ofPoint2f(x-vec.x, y-vec.y); 
    }
     
    ofPoint2f& operator-=( const ofVec2f& vec )              
    { 
        x -= vec.x;
        y -= vec.y;
        return *this; 
    }
    
    ofPoint2f operator-() const                               
    { 
        return ofPoint2f(-x, -y); 
    }      



    // Scale point (by scalar or vector).
    //
    //
    ofPoint2f operator*( const float f ) const                
    { 
        return ofPoint2f(x*f, y*f);
    } 
    
    ofPoint2f& operator*=( const float f )                    
    { 
        x*=f;
        y*=f;
        return *this; 
    }    
        
    ofPoint2f operator*( const ofVec2f& vec ) const
    { 
        return ofPoint2f(x*vec.x, y*vec.y); 
    } 
    
    ofPoint2f& operator*=( const ofVec2f& vec )      
    { 
        x*=vec.x;
        y*=vec.y;
        return *this; 
    }
    
    ofPoint2f operator/( const float f ) const                
    { 
        return ofPoint2f(x/f, y/f);
    } 
    
    ofPoint2f& operator/=( const float f )                    
    { 
        x/=f;
        y/=f;
        return *this; 
    }    
        
    ofPoint2f operator/( const ofVec2f& vec ) const
    { 
        return ofPoint2f(x/vec.x, y/vec.y); 
    } 
    
    ofPoint2f& operator/=( const ofVec2f& vec )      
    { 
        x/=vec.x;
        y/=vec.y;
        return *this; 
    }    



    // Rotate point by angle (deg) around pivot point.
    //
    //     
    ofPoint2f rotated( float angle, const ofPoint2f& pivot ) const
    {
        float a = angle * DEG_TO_RAD;
        return ofPoint2f( ((x-pivot.x)*cos(a) - (y-pivot.y)*sin(a)) + pivot.x, 
                          ((x-pivot.x)*sin(a) + (y-pivot.y)*cos(a)) + pivot.y );
    }
    
    ofPoint2f& rotate( float angle, const ofPoint2f& pivot )
    {
        float a = angle * DEG_TO_RAD;
        float xrot = ((x-pivot.x)*cos(a) - (y-pivot.y)*sin(a)) + pivot.x;
        y = ((x-pivot.x)*sin(a) + (y-pivot.y)*cos(a)) + pivot.y;
        x = xrot;
        return *this;
    }  
    
    
    
    // Map point to coordinate system defined by origin, vx, and vy.
    //
    //      
    ofPoint2f mapped( const ofPoint2f& origin, 
                   const ofVec2f& vx, 
                   const ofVec2f& vy ) const
    {
        return ofPoint2f( origin.x + x*vx.x + y*vy.x, 
                          origin.y + x*vx.y + y*vy.y );      
    }

    ofPoint2f& map( const ofPoint2f& origin, 
                       const ofVec2f& vx, const ofVec2f& vy )
    {
        float xmap = origin.x + x*vx.x + y*vy.x;
        y = origin.y + x*vx.y + y*vy.y;      
        x = xmap;
        return *this;
    }
        


    /**
    * Return the difference vector between two points.
    */
    ofVec2f operator-( const ofPoint2f& pnt ) const          
    { 
        return ofVec2f( x-pnt.x, y-pnt.y ); 
    }
            


    // Distance between two points.
    //
    //
    float distance( const ofPoint2f& pnt) const                
    { 
        float vx = x-pnt.x;
        float vy = y-pnt.y;
        return (float)sqrt(vx*vx + vy*vy); 
    } 
    
    float distanceSquared( const ofPoint2f& pnt ) const                
    { 
        float vx = x-pnt.x;
        float vy = y-pnt.y;
        return vx*vx + vy*vy;  
    } 
    
    

    // 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.
    */      
    ofPoint2f interpolate( const ofPoint2f& pnt, float p ) const
    {
        return ofPoint2f( x*(1-p) + pnt.x*p, y*(1-p) + pnt.y*p );
    }

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



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

    // Perpendicular normalized vector from two points.
    //
    //      
    ofVec2f perpendicular( const ofPoint2f& pnt ) const
    {
        //float a = -atan2( pnt.y - y, pnt.x - x );
        //return ofVec2f( sin(a), cos(a) );
        
        //the following seems simpler and about 3x faster
                
        float vy = -(x - pnt.x);
        float vx = y - pnt.y;
        float length = (float)sqrt(vx*vx + vy*vy);
        if( length > 0 )
            return ofVec2f( vx/length, vy/length );
        else
            return ofVec2f(x, y);
    }
    
	
};



#endif
