// ===================================================================
// $Id: tricol.h,v 1.3 2009-05-03 03:14:55 xkrivanj Exp $
//
// tricol.h
//     Header file for CTriCol class.
//     Implements tristimulus color triple stored as three floats.
//    
// Initial coding by Jaroslav Krivanek,  14th may 2002
// 

#ifndef TRICOL_H
#define TRICOL_H

// standard headers
#include <cmath>
#include <cassert>
#include <iostream>


///Holds RGB color and provide some other useful functionality.
class CTriCol
{
public:
  // channels
  union {
    float v[3];
    struct {float r,g,b;};
    struct {float x,y,z;};
  };

  /// Do nothing.
  inline CTriCol () {}
  /// Set all components to 'c'.
  inline CTriCol (float c):r(c),g(c),b(c) {}
  /// Set components to 'c0', 'c1' and 'c2'
  inline CTriCol (float c0, float c1, float c2):r(c0),g(c1),b(c2) {}
  /// Set components from a float array.
  inline CTriCol (const float *xx) { Set(xx); }
  /// Set components from a double array.
  inline CTriCol (const double *xx) { r=xx[0]; g=xx[1]; b=xx[2]; }

  /// Set all components to zero.
  inline void setz() { r=g=b=0; }
  /// Set all components to 'c'.
  inline void Set(float c) { r=g=b=c; }
  /// Set components to 'c0', 'c1' and 'c2'
  inline void Set(float c0, float c1, float c2) {v[0]=c0; v[1]=c1; v[2]=c2;}
  /// Set components from a float array.
  inline void Set(const float *x) {v[0]=x[0];v[1]=x[1];v[2]=x[2];}
  /// Set components from a double array.
  inline void Set(const double *x) {v[0]=x[0];v[1]=x[1];v[2]=x[2];}
  /// Set the value for a given index.
  inline void Set(unsigned i, float c) {assert(i<3); v[i]=c;}

  /// Cast to float*.
  // operator       float* ()                { return &v[0]; }
  inline float* data()                       { return &v[0]; }
  /// Cast to const float*.
  // operator const float* () const          { return &v[0]; }
  inline const float* data() const           { return &v[0]; }

  inline void CopyData(float *d)  const { d[0]=v[0]; d[1]=v[1]; d[2]=v[2]; }
  inline void CopyData(double *d) const { d[0]=v[0]; d[1]=v[1]; d[2]=v[2]; }

  /// access to components
  const float& operator[] (int i) const {assert(i<GetCount());return v[i];}
  float& operator[] (int i) {assert(i<GetCount());return v[i];}

  /// Returns the value given an index
  inline const float & Get(int i) const {assert(i<GetCount());return v[i];}
  /// Returns the number of color components.
  inline int GetCount() const {return 3;}

  /// Arithmetic update operators.
  //@{
  inline CTriCol& CTriCol::operator+= (const CTriCol& c) { r+=c.r; g+=c.g; b+=c.b; return *this; }
  inline CTriCol& CTriCol::operator-= (const CTriCol& c) { r-=c.r; g-=c.g; b-=c.b; return *this; }
  inline CTriCol& CTriCol::operator*= (const CTriCol& c) { r*=c.r; g*=c.g; b*=c.b; return *this; }
  inline CTriCol& CTriCol::operator*= (float a)          { r*=a; g*=a; b*=a; return *this; }
  inline CTriCol& CTriCol::operator/= (const CTriCol& c) { r/=c.r; g/=c.g; b/=c.b; return *this; }
  inline CTriCol& CTriCol::operator/= (float a)          { return (*this)*=(1.0f/a); }

  inline CTriCol& operator+= (const float *c)  { v[0]+=c[0]; v[1]+=c[1]; v[2]+=c[2]; return *this; }
  inline CTriCol& operator-= (const float *c)  { v[0]-=c[0]; v[1]-=c[1]; v[2]-=c[2]; return *this; }
  inline CTriCol& operator*= (const float *c)  { v[0]*=c[0]; v[1]*=c[1]; v[2]*=c[2]; return *this; }
  inline CTriCol& operator/= (const float *c)  { v[0]/=c[0]; v[1]/=c[1]; v[2]/=c[2]; return *this; }

  inline CTriCol& operator+= (const double *c)  { v[0]+=c[0]; v[1]+=c[1]; v[2]+=c[2]; return *this; }
  inline CTriCol& operator-= (const double *c)  { v[0]-=c[0]; v[1]-=c[1]; v[2]-=c[2]; return *this; }
  inline CTriCol& operator*= (const double *c)  { v[0]*=c[0]; v[1]*=c[1]; v[2]*=c[2]; return *this; }
  inline CTriCol& operator/= (const double *c)  { v[0]/=c[0]; v[1]/=c[1]; v[2]/=c[2]; return *this; }
  //@}

  /// Arithmetic operators.
  //@{
  inline CTriCol operator+ (const CTriCol& c) const  { return CTriCol(r+c.r,g+c.g,b+c.b); }
  inline CTriCol operator- (const CTriCol& c) const  { return CTriCol(r-c.r,g-c.g,b-c.b); }
  inline CTriCol operator* (const CTriCol& c) const  { return CTriCol(r*c.r,g*c.g,b*c.b); }
  inline CTriCol operator/ (const CTriCol& c) const  { return CTriCol(r/c.r,g/c.g,b/c.b); }
  inline CTriCol operator* (float a) const { return CTriCol(r*a,g*a,b*a); }
  inline CTriCol operator/ (float a) const { return (*this) * (1.0f/a); }
  inline CTriCol operator- () const        { return CTriCol(-r,-g,-b); }

  inline CTriCol operator+ (const float *c) const  {return CTriCol(v[0]+c[0], v[1]+c[1], v[2]+c[2]); }
  inline CTriCol operator- (const float *c) const  {return CTriCol(v[0]-c[0], v[1]-c[1], v[2]-c[2]); }
  inline CTriCol operator* (const float *c) const  {return CTriCol(v[0]*c[0], v[1]*c[1], v[2]*c[2]); }
  inline CTriCol operator/ (const float *c) const  {return CTriCol(v[0]/c[0], v[1]/c[1], v[2]/c[2]); }

  inline CTriCol operator+ (const double *c) const  {return CTriCol(v[0]+c[0], v[1]+c[1], v[2]+c[2]); }
  inline CTriCol operator- (const double *c) const  {return CTriCol(v[0]-c[0], v[1]-c[1], v[2]-c[2]); }
  inline CTriCol operator* (const double *c) const  {return CTriCol(v[0]*c[0], v[1]*c[1], v[2]*c[2]); }
  inline CTriCol operator/ (const double *c) const  {return CTriCol(v[0]/c[0], v[1]/c[1], v[2]/c[2]); }
  //@}

  inline friend CTriCol operator* (float a,const CTriCol& x); 

  /// Aggregate functions.
  //@{
  /// Return the sum of all the components.
  inline float Sum() const;
  /// Returns the luminance of this color.
  inline float Luminance() const;
  /// Return the arithmetic mean of components.
  inline float Mean() const { return Sum()/GetCount(); }
  /// Returns the sum of absolute values of all components.
  inline float AbsSum() const;
  /// Return the squared norm of the (r,g,b) vector.
  inline float SqrMagnitude() const { return r*r + g*g + b*b; }
  /// Returns the sum of absolute values of all components.
  friend inline float fabs(const CTriCol& c) { return c.AbsSum(); }
  //@}
 
  /// Returns the maximum component.
  inline float Max() const;
  /// Returns the minimum component.
  inline float Min() const;

  /// Clip the components to given interval.
  //@{
  inline void Clamp(const float &lowerBound=0.0f, 
		    const float &upperBound=1.0f);
  inline void ClampLower(const float &lowerBound=0.0f);
  inline void ClampUpper(const float &upperBound=1.0f);
  inline void ClampUpperToOne(void);
  inline void ClampLowerToZero(void);
  //@}

  inline void ScaleByMax();

  friend inline std::ostream& operator<< (std::ostream &s, const CTriCol &c)
  {
    return s << "(" << c.r << ", " << c.g << ", " << c.b << ")";
  }
};

// =============================================================== //





inline CTriCol operator* (float a,const CTriCol& c)
{
  return CTriCol(c.r*a,c.g*a,c.b*a);
}

inline float CTriCol::Sum() const     
{ 
  return r + g + b; 
}

inline float CTriCol::AbsSum() const     
{ 
  return fabsf(r) + fabsf(g) + fabsf(b); 
}

inline float CTriCol::Luminance() const 
{ 
  return r*0.299f+g*0.587f+b*0.114f; 
} 

inline float CTriCol::Max() const
{
  float max = r>g ? r : g;
  return b>max ? b : max;
}

inline float CTriCol::Min() const
{
  float min = r < g ? r : g;
  return b < min ? b : min;
}


inline void CTriCol::Clamp(const float &lowerBound, 
			 const float &upperBound)
{
  if      (r>upperBound)       r=upperBound; 
  else if (r<lowerBound)       r=lowerBound;
  if      (g>upperBound)       g=upperBound; 
  else if (g<lowerBound)       g=lowerBound;
  if      (b>upperBound)       b=upperBound; 
  else if (b<lowerBound)       b=lowerBound;
}

inline void CTriCol::ClampLower(const float &lowerBound)
{
  if (r<lowerBound)       r=lowerBound;
  if (g<lowerBound)       g=lowerBound;
  if (b<lowerBound)       b=lowerBound;
}

inline void CTriCol::ClampUpper(const float &upperBound)
{
  if      (r>upperBound)       r=upperBound; 
  if      (g>upperBound)       g=upperBound; 
  if      (b>upperBound)       b=upperBound; 
}

inline void CTriCol::ClampUpperToOne()
{
  if      (r>1.0f)       r=1.0f; 
  if      (g>1.0f)       g=1.0f; 
  if      (b>1.0f)       b=1.0f; 
}

inline void CTriCol::ClampLowerToZero()
{
  if      (r<0.0f)       r=0.0f; 
  if      (g<0.0f)       g=0.0f; 
  if      (b<0.0f)       b=0.0f; 
}


inline void CTriCol::ScaleByMax()
{
  (*this)/=Max();
}

class CRgbw {
public:
	CTriCol col;
	float   w;

	CRgbw (){}
	CRgbw (float c):col(c),w(c) {}
	CRgbw (float c0, float c1, float c2, float c3):col(c0,c1,c2),w(c3) {}
	CRgbw (const CTriCol& inCol, float inW):col(inCol),w(inW) {}
};



#endif
