/* Copyright (c) 1997-2005
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program 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, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program 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.
*/

#ifndef _POLYMAKE_COLOR_H
#define _POLYMAKE_COLOR_H "$Project: polymake $$Id: color.h,v 1.16 2005/09/22 17:12:11 gawrilow Exp $"

#if defined(__GNUC__) && __GNUC__==3 && __GNUC_MINOR__<3 && !defined(__APPLE__) && !defined(__COMO__)
#pragma interface
#endif

#include <stdexcept>
#include <cmath>
#include <limits>
#include <iterators.h>

namespace pm {

/** Exception type
    An exception of this type is thrown by an attempt to assign a wrong value to some color component.
*/
class color_error : public std::domain_error {
public:
   color_error(const std::string& what_arg) : std::domain_error(what_arg) { }
};

class HSV;

/** Color description in RGB space
    !!R!!ed-!!G!!reen-!!B!!lue additive color model.
*/
template <typename T>
class RGB : public fixed_array<typename if_else< std::numeric_limits<T>::is_exact, int, T>::result, 3> {
public:
   enum { R, G, B };

   RGB()
   {
      (*this)[R]=0;
      (*this)[G]=0;
      (*this)[B]=0;
   }

   /// Constructs the color from the components.
   RGB(T red, T green, T blue)
   {
      set_red(red);
      set_green(green);
      set_blue(blue);
   }

   template <typename T2>
   RGB(const RGB<T2>& c)
   {
      const double k= (std::numeric_limits<T>::is_exact ? double(std::numeric_limits<T>::max()) : 1) /
	              (std::numeric_limits<T2>::is_exact ? double(std::numeric_limits<T2>::max()) : 1);
      for (int i=R; i<=B; ++i)
	 (*this)[i]=k * c[i];
   }

   /// Converts from HSV representation.
   RGB(const HSV&);

   T red() const { return (*this)[R]; }
   T green() const { return (*this)[G]; }
   T blue() const { return (*this)[B]; }

   void set_red(T red)
   {
      const T max= std::numeric_limits<T>::is_exact ? std::numeric_limits<T>::max() : T(1);
      if (red>max) throw color_error("RGB: Red out of range");
      (*this)[R]=red;
   }
   void set_green(T green)
   {
      const T max= std::numeric_limits<T>::is_exact ? std::numeric_limits<T>::max() : T(1);
      if (green>max) throw color_error("RGB: Green out of range");
      (*this)[G]=green;
   }
   void set_blue(T blue)
   {
      const T max= std::numeric_limits<T>::is_exact ? std::numeric_limits<T>::max() : T(1);
      if (blue>max) throw color_error("RGB: Blue out of range");
      (*this)[B]=blue;
   }
};

template <typename T>
struct spec_object_traits< RGB<T> >
   : spec_object_traits<is_container> { };

/** Color description in HSV space
    !!H!!ue-!!S!!aturation-!!V!!alue color model.  This color model is more natural with
    respect to human physiology than the RGB model.
*/
class HSV : public fixed_array<double,3> {
public:
   enum { H, S, V };

   HSV() {
      (*this)[H]=0;
      (*this)[S]=0;
      (*this)[V]=0;
   }

   /// Constructs the color from the components.
   HSV(double hue, double saturation, double value)
   {
      set_hue(hue);
      set_saturation(saturation);
      set_value(value);
   }

   /// Converts from RGB representation.
   template <typename T>
   HSV(const RGB<T>&);

   const double& hue() const { return (*this)[H]; }
   const double& saturation() const { return (*this)[S]; }
   const double& value() const { return (*this)[V]; }

   void set_hue(double hue)
   {
      if (hue<0 || hue>360) throw color_error("HSV: Hue out of range");
      (*this)[H]=hue;
   }
   void set_saturation(double saturation)
   {
      if (saturation<0 || saturation>1) throw color_error("HSV: Saturation out of range");
      (*this)[S]=saturation;
   }
   void set_value(double value)
   {
      if (value<0 || value>1) throw color_error("HSV: Value out of range");
      (*this)[V]=value;
   }
};

template <>
struct spec_object_traits<HSV>
   : spec_object_traits<is_container> { };

} // end namespace pm

namespace polymake {
   using pm::RGB;
   using pm::HSV;
}

#include <color.tcc>

#endif // _POLYMAKE_COLOR_H

// Local Variables:
// mode:C++
// End:
