//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#include "TransferFunction.hpp"

#include <QTextStream>
#include <QStringList>
#include <algorithm>

#include <QTextStream>
#include <stdio.h>

namespace mgx {
  const double TransferFunction::epsilon = 1e-6;
  
  TransferFunction::TransferFunction(Interpolation i)
    : _interpolation(i), clamp(true), exteriorColor(0, 0, 0, 0) {}
  
  TransferFunction::TransferFunction(const TransferFunction& copy)
    : _interpolation(copy._interpolation), values(copy.values), keys(copy.keys), 
  	  clamp(copy.clamp), exteriorColor(copy.exteriorColor) {}
  
  TransferFunction TransferFunction::load(QString spec)
  {
    TransferFunction fct;
    QStringList all = spec.split("\n");
    for(int line_number = 0; line_number < all.size(); ++line_number) {
      QString line = all[line_number].trimmed();
      if(line.isEmpty() or line[0] == '#')
        continue;
      QStringList fields = line.split(":");
      QString header = fields[0].trimmed();
      fields.pop_front();
      QString value = fields.join(":").trimmed().toLower();
      if(header == "Interpolation") {
        if(value == "rgb")
          fct._interpolation = RGB;
        else if(value == "hsv")
          fct._interpolation = HSV;
        else if(value == "cyclic_hsv")
          fct._interpolation = CYCLIC_HSV;
      } else if(header == "Clamp") {
        fct.clamp = value == "true";
      } else if(header == "ExteriorColor") {
        QStringList cs = value.split(",");
        Colorf col;
        for(int i = 0; i < 4; ++i)
          col[i] = cs[i].toFloat();
        fct.exteriorColor = col;
      } else if(header == "ColoredPos") {
        QStringList vs = value.split("-");
        double pos = vs[0].toDouble();
        vs.pop_front();
        QString cols = vs.join("-");
        Colorf col;
        QStringList cs = cols.split(",");
        for(int i = 0; i < 4; ++i)
          col[i] = cs[i].toFloat();
        fct.values.push_back(std::make_pair(pos, col));
      }
    }
    fct.clamp = true;
    if(fct.size() < 2)
      fct.clear();
    else
      fct.update_keys();
    return fct;
  }
  
  QString TransferFunction::dump() const
  {
    QString result;
    QTextStream ts(&result, QIODevice::WriteOnly);
    ts << "Interpolation: ";
    switch(_interpolation) {
    case RGB:
      ts << "rgb";
      break;
    case HSV:
      ts << "hsv";
      break;
    case CYCLIC_HSV:
      ts << "cyclic_hsv";
      break;
    }
    ts << endl;
    ts << "Clamp: " << (clamp ? "true" : "false") << endl;
    ts << "ExteriorColor: "
       << QString("%1,%2,%3,%4").arg(exteriorColor[0]).arg(exteriorColor[1]).arg(exteriorColor[2]).arg(exteriorColor[3])
       << endl;
    for(size_t i = 0; i < values.size(); ++i) {
      Colorf col = values[i].second;
      if(_interpolation == RGB) {
        if(col[0] < 0)
          col[0] = 0;
        else if(col[0] > 1)
          col[0] = 1;
      } else {
        if(col[0] < 0)
          col[0] += 360;
        else if(col[0] >= 360)
          col[0] -= 360;
      }
      for(int j = 1; j < 4; ++j) {
        if(col[j] < 0)
          col[j] = 0;
        else if(col[j] > 1)
          col[j] = 1;
      }
      ts << "ColoredPos: " << values[i].first << "-"
         << QString("%1,%2,%3,%4").arg(values[i].second[0]).arg(values[i].second[1]).arg(values[i].second[2]).arg(
        values[i].second[3]) << endl;
    }
    return result;
  }
  
  template <typename T1, typename T2> struct ComparePairFirst {
    bool operator()(const std::pair<T1, T2>& p1, const std::pair<T1, T2>& p2) {
      return p1.first < p2.first;
    }
  };
  
  void TransferFunction::update_keys()
  {
    std::sort(values.begin(), values.end(), ComparePairFirst<double, Colorf>());
    keys.clear();
    for(size_t i = 0; i < values.size(); ++i) {
      keys[values[i].first] = i;
    }
  }
  
  void TransferFunction::adjust(double minValue, double maxValue)
  {
    if(values.size() < 2)
      return;
    double lowest = values.front().first;
    double highest = values.back().first;
    double ratio = (maxValue - minValue) / (highest - lowest);
    for(size_t i = 0; i < values.size(); ++i) {
      values[i].first = (values[i].first - lowest) * ratio + minValue;
    }
    update_keys();
  }
  
  TransferFunction::Colorf TransferFunction::interpolate(double position, double p1, Colorf col1, double p2,
                                                         Colorf col2) const
  {
    double delta = p2 - p1;
    double dp1 = (p2 - position) / delta;
    double dp2 = 1.0 - dp1;
    if(_interpolation == CYCLIC_HSV) {
      if(col2[0] - col1[0] > 180)
        col1[0] += 360;
      else if(col1[0] - col2[0] > 180)
        col2[0] += 360;
    }
    Colorf col(dp1 * col1 + dp2 * col2);
    if(_interpolation == CYCLIC_HSV) {
      if(col[0] < 0)
        col[0] += 360;
      else if(col[0] > 360)
        col[0] -= 360;
    }
    return col;
  }
  
  void TransferFunction::add_rgba_point(double pos, Colorf col)
  {
    if(_interpolation != RGB)
      col = convertRGBtoHSV(col);
    add_point(pos, col);
  }
  
  void TransferFunction::add_point(double pos, Colorf col)
  {
    key_map::iterator it_found = keys.find(pos);
    if(it_found != keys.end()) {
      values[it_found.value()] = std::make_pair(pos, col);
    } else {
      values.push_back(std::make_pair(pos, col));
      update_keys();
    }
  }
  
  void TransferFunction::add_hsva_point(double pos, Colorf col)
  {
    if(_interpolation == RGB)
      col = convertHSVtoRGB(col);
    add_point(pos, col);
  }
  
  void TransferFunction::remove_point(double pos)
  {
    key_map::iterator it_found = keys.find(pos);
    if(it_found != keys.end()) {
      values.erase(values.begin() + it_found.value());
      keys.erase(it_found);
    }
  }
  
  TransferFunction::Colorf TransferFunction::rgba(double pos) const
  {
    Colorf col = color(pos);
    if(_interpolation != RGB)
      return convertHSVtoRGB(col);
    return col;
  }
  
  TransferFunction::Colorf TransferFunction::color(double pos) const
  {
    Colorf prev_col;
    double prev_pos = -1;
    for(size_t i = 0; i < size(); ++i) {
      const double& p = values[i].first;
      const Colorf& col = values[i].second;
      if((p == 0 and fabs(pos) < epsilon)or (fabs(pos - p) / fabs(pos + p) < epsilon)) {
        return col;
      }
      if(pos < p) {
        if(prev_pos < 0) {
          if(clamp)
            return col;
          else
            return exteriorColor;
        }
        return interpolate(pos, prev_pos, prev_col, p, col);
      }
      prev_col = col;
      prev_pos = p;
    }
    if(clamp)
      return prev_col;
    else
      return exteriorColor;
  }
  
  double TransferFunction::alpha(double pos) const
  {
    Colorf prev_col;
    double prev_pos = -1;
    for(size_t i = 0; i < size(); ++i) {
      const double& p = values[i].first;
      const Colorf& col = values[i].second;
      if((p == 0 and fabs(pos) < epsilon)or (fabs(pos - p) / fabs(pos + p) < epsilon)) {
        return col.a();
      }
      if(pos < p) {
        if(prev_pos < 0) {
          if(clamp)
            return col.a();
          else
            return exteriorColor.a();
        }
        double delta = p - prev_pos;
        double dp1 = fabs((pos - prev_pos) / delta);
        double dp2 = fabs((pos - p) / delta);
        return dp1 * col.a() + dp2 * prev_col.a();
      }
      prev_col = col;
      prev_pos = p;
    }
    if(clamp)
      return prev_col.a();
    else
      return exteriorColor.a();
  }
  
  TransferFunction::Colorf TransferFunction::hsva(double pos) const
  {
    Colorf col = color(pos);
    if(_interpolation != RGB)
      return col;
    return convertRGBtoHSV(col);
  }
  
  void TransferFunction::setInterpolation(Interpolation i)
  {
    if(i != _interpolation) {
      if(i * _interpolation == 0)     // Change from HSV to RGB
      {
        if(i == RGB) {
          for(size_t i = 0; i < size(); ++i)
            values[i].second = convertHSVtoRGB(values[i].second);
        } else {
          for(size_t i = 0; i < size(); ++i)
            values[i].second = convertRGBtoHSV(values[i].second);
        }
      }
      _interpolation = i;
    }
  }
  
  void TransferFunction::reverse()
  {
    for(size_t i = 0; i < values.size(); ++i)
      values[i].first = 1 - values[i].first;
    update_keys();
  }
  
  void TransferFunction::clear()
  {
    values.clear();
    keys.clear();
  }
  
  void TransferFunction::move_point(double old_pos, double new_pos)
  {
    key_map::iterator it_found = keys.find(old_pos);
    if(it_found != keys.end()) {
      values[it_found.value()].first = new_pos;
      update_keys();
    }
  }
  
  double TransferFunction::next_pos(double old_pos) const
  {
    key_map::const_iterator it_found = keys.find(old_pos);
    if(it_found != keys.end()) {
      int p = it_found.value();
      if(p < int(size() - 1))
        return values[p + 1].first;
    }
    return -1;
  }
  
  double TransferFunction::prev_pos(double old_pos) const
  {
    key_map::const_iterator it_found = keys.find(old_pos);
    if(it_found != keys.end()) {
      int p = it_found.value();
      if(p > 0)
        return values[p - 1].first;
    }
    return -1;
  }
  
  TransferFunction TransferFunction::scale(Colorf min, Colorf max, Interpolation interpolation)
  {
    TransferFunction fct(interpolation);
    if(interpolation == RGB) {
      fct.add_rgba_point(0, min);
      fct.add_rgba_point(1, max);
    } else {
      fct.add_hsva_point(0, min);
      fct.add_hsva_point(1, max);
    }
    return fct;
  }
  
  TransferFunction TransferFunction::hue_scale()
  {
    TransferFunction fct(CYCLIC_HSV);
    fct.add_hsva_point(0, Colorf(0, 1, 1, 0));
    fct.add_hsva_point(0.3, Colorf(0.3 * 360, 1, 1, 0.3));
    fct.add_hsva_point(0.7, Colorf(0.7 * 360, 1, 1, 0.7));
    fct.add_hsva_point(1, Colorf(0, 1, 1, 1));
    return fct;
  }
  
  TransferFunction TransferFunction::french_flag()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0, Colorf(0, 0, 1, 1));
    fct.add_rgba_point(0.5, Colorf(1, 1, 1, 0));
    fct.add_rgba_point(1, Colorf(1, 0, 0, 1));
    return fct;
  }
  
  TransferFunction TransferFunction::jet()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(1, Colorf(0.5, 0.0, 0.0, 1));
    fct.add_rgba_point(7. / 8, Colorf(1.0, 0.0, 0.0, 1));
    fct.add_rgba_point(5. / 8, Colorf(1.0, 1.0, 0.0, 1));
    fct.add_rgba_point(3. / 8, Colorf(0.0, 1.0, 1.0, 1));
    fct.add_rgba_point(1. / 8, Colorf(0.0, 0.0, 1.0, 1));
    fct.add_rgba_point(0, Colorf(0.0, 0.0, 0.5, 1));
    return fct;
  }

  TransferFunction TransferFunction::blackbody()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0,Colorf(0.0629875558825137,1.62521938976164e-06,1.92853645997135e-07,1));
    fct.add_rgba_point(1./14,Colorf(0.225622725743595,1.30016932785929e-05,1.5428218299092e-06,1));
    fct.add_rgba_point(2./14,Colorf(0.331422451178709,2.55144601930992e-05,8.20714344653053e-07,1));
    fct.add_rgba_point(3./14,Colorf(0.443412893994424,4.8462802116668e-05,2.97921960514393e-06,1));
    fct.add_rgba_point(4./14,Colorf(0.560709601121062,7.77797187572487e-05,2.24628659995216e-06,1));
    fct.add_rgba_point(5./14,Colorf(0.682674506928839,0.000117468391341338,9.18904422618841e-07,1));
    fct.add_rgba_point(6./14,Colorf(0.808824098499757,0.000175093502775194,4.85506012752858e-06,1));
    fct.add_rgba_point(7./14,Colorf(0.938780435566096,0.000241391744354743,3.08712922923976e-06,1));
    fct.add_rgba_point(8./14,Colorf(0.999999789871659,0.249054726814225,0.080662912449573,1));
    fct.add_rgba_point(9./14,Colorf(0.949090032655725,0.47849486395747,1.18794992554375e-05,1));
    fct.add_rgba_point(10./14,Colorf(0.949163706301567,0.606229243441685,1.32737368347202e-05,1));
    fct.add_rgba_point(11./14,Colorf(0.979205858859301,0.708826930403844,2.24008305440848e-05,1));
    fct.add_rgba_point(12./14,Colorf(0.999999995450108,0.809377343803524,0.267199751985062,1));
    fct.add_rgba_point(13./14,Colorf(0.999999693975126,0.906632843069238,0.586667865495948,1));
    fct.add_rgba_point(14./14,Colorf(0.999999487239432,0.990242715417565,0.93762183020173,1));
    return fct;
  }
  TransferFunction TransferFunction::uniform_jet()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0.0,Colorf(0.191607361046299,0.191568818533201,0.716093527571352,1));
    fct.add_rgba_point(0.1,Colorf(1.12990923561218e-05,0.432909983092997,0.664415209769246,1));
    fct.add_rgba_point(0.2,Colorf(3.45781406535461e-05,0.570349733228818,0.689897408431366,1));
    fct.add_rgba_point(0.3,Colorf(1.4744326075885e-05,0.670053203577743,0.700436088043035,1));
    fct.add_rgba_point(0.4,Colorf(0.0598413367509357,0.73541673822207,0.639649866255257,1));
    fct.add_rgba_point(0.5,Colorf(0.369772091273309,0.744735962100061,0.503521770485113,1));
    fct.add_rgba_point(0.6,Colorf(0.556098775946103,0.701377422457934,0.320387267561088,1));
    fct.add_rgba_point(0.7,Colorf(0.662925801573994,0.612891921175565,1.39082902284035e-05,1));
    fct.add_rgba_point(0.8,Colorf(0.66999442830355,0.493421121701985,2.04572205855072e-05,1));
    fct.add_rgba_point(0.9,Colorf(0.628525557045094,0.336274394889423,7.19950828735619e-06,1));
    fct.add_rgba_point(1.0,Colorf(0.571132259537014,0.0259940114090371,0.025909664957821,1));
    return fct;
  }
  TransferFunction TransferFunction::helix_warm()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0./18,Colorf(2.99645305167733e-07,0.0763254162182793,0.0996566550865541,1));
    fct.add_rgba_point(1./18,Colorf(2.98552806203771e-06,0.118874673578686,0.172409490374521,1));
    fct.add_rgba_point(2./18,Colorf(5.39584287527387e-06,0.154932463869966,0.268797784828491,1));
    fct.add_rgba_point(3./18,Colorf(0.122856348370984,0.177410420522738,0.366314103553006,1));
    fct.add_rgba_point(4./18,Colorf(0.258175929596072,0.182186704335898,0.456954375423335,1));
    fct.add_rgba_point(5./18,Colorf(0.396901375222762,0.166050215350065,0.533248912237097,1));
    fct.add_rgba_point(6./18,Colorf(0.533394396001543,0.127417129873763,0.588419389819933,1));
    fct.add_rgba_point(7./18,Colorf(0.661214176636478,0.0673863681208148,0.617826167793999,1));
    fct.add_rgba_point(8./18,Colorf(0.775647929586465,0.0186884031513646,0.61882420219871,1));
    fct.add_rgba_point(9./18,Colorf(0.874075261920352,0.0826560173905496,0.589221426238657,1));
    fct.add_rgba_point(10./18,Colorf(0.955371061801016,0.185299961767651,0.524309805869404,1));
    fct.add_rgba_point(11./18,Colorf(0.999999426831968,0.315419249224793,0.420102881460218,1));
    fct.add_rgba_point(12./18,Colorf(0.999999332712928,0.450215038520153,0.311835026585963,1));
    fct.add_rgba_point(13./18,Colorf(0.99999938423896,0.5564505708327,0.169939248998888,1));
    fct.add_rgba_point(14./18,Colorf(0.986770720795004,0.655087708505464,1.91092980244156e-05,1));
    fct.add_rgba_point(15./18,Colorf(0.967668531966126,0.746250690494315,8.73493064872333e-06,1));
    fct.add_rgba_point(16./18,Colorf(0.946289006103403,0.832696274161194,4.28795716591179e-05,1));
    fct.add_rgba_point(17./18,Colorf(0.911344019648029,0.919295730840851,1.54182915587536e-05,1));
    fct.add_rgba_point(18./18,Colorf(0.861273061207335,0.99999980638626,0.39719347328802,1));
    return fct;
  }
  TransferFunction TransferFunction::helix_cool()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0./18,Colorf(0.103349951781665,0.057084372989613,5.31201492789913e-07,1));
    fct.add_rgba_point(1./18,Colorf(0.178832809857127,0.0842654768351634,1.22211788882214e-06,1));
    fct.add_rgba_point(2./18,Colorf(0.263110719185051,0.0981141332861687,0.0286695266464055,1));
    fct.add_rgba_point(3./18,Colorf(0.351818431064541,0.101762347612331,0.117344544810111,1));
    fct.add_rgba_point(4./18,Colorf(0.443472063557582,0.0902813374391871,0.219676626221924,1));
    fct.add_rgba_point(5./18,Colorf(0.534349786537676,0.0540794537925999,0.337238928220601,1));
    fct.add_rgba_point(6./18,Colorf(0.615286065500854,2.46578386624102e-06,0.468145889344007,1));
    fct.add_rgba_point(7./18,Colorf(0.67828433894753,5.87389626844972e-07,0.608427312807965,1));
    fct.add_rgba_point(8./18,Colorf(0.719130273244915,0.0829628101323027,0.760044971853699,1));
    fct.add_rgba_point(9./18,Colorf(0.712935997694984,0.213305397233019,0.909633477192473,1));
    fct.add_rgba_point(10./18,Colorf(0.619947603586265,0.377906800209137,0.999999667633419,1));
    fct.add_rgba_point(11./18,Colorf(0.470799514759969,0.523458909123238,0.999998086609647,1));
    fct.add_rgba_point(12./18,Colorf(0.296348230521777,0.629345084048098,0.999998302979857,1));
    fct.add_rgba_point(13./18,Colorf(0.0487661563252994,0.715937977473331,0.985366206936671,1));
    fct.add_rgba_point(14./18,Colorf(4.81340318999091e-05,0.787537677340144,0.960575290121561,1));
    fct.add_rgba_point(15./18,Colorf(1.35785166283164e-05,0.855540387435811,0.951964289087123,1));
    fct.add_rgba_point(16./18,Colorf(0.292851147766308,0.913934014740892,0.936371983954324,1));
    fct.add_rgba_point(17./18,Colorf(0.530835129906128,0.959491905688872,0.923029444334224,1));
    fct.add_rgba_point(18./18,Colorf(0.714200087957391,0.99999981401237,0.931266271113249,1));
    return fct;
  }
  TransferFunction TransferFunction::helix_full()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0./18,Colorf(2.3424659861018e-06,0.0820737282141749,0.0354873454913317,1));
    fct.add_rgba_point(1./18,Colorf(0.0312583609995888,0.128484419896508,2.87245676260425e-07,1));
    fct.add_rgba_point(2./18,Colorf(0.131382051949291,0.160279334930176,7.79028671234819e-07,1));
    fct.add_rgba_point(3./18,Colorf(0.217723974316859,0.190536177902344,7.99012316557272e-06,1));
    fct.add_rgba_point(4./18,Colorf(0.315869048673932,0.213541774201361,0.010062656573044,1));
    fct.add_rgba_point(5./18,Colorf(0.421633051070366,0.225154518047955,0.130194083280704,1));
    fct.add_rgba_point(6./18,Colorf(0.525849527161095,0.225981704598751,0.272108699522441,1));
    fct.add_rgba_point(7./18,Colorf(0.616248814882713,0.224181770490039,0.431462025101624,1));
    fct.add_rgba_point(8./18,Colorf(0.672678525866132,0.245395098101225,0.601940822526985,1));
    fct.add_rgba_point(9./18,Colorf(0.663686377040635,0.320478027846883,0.766866780056137,1));
    fct.add_rgba_point(10./18,Colorf(0.537203721289775,0.452008431348025,0.871912745930068,1));
    fct.add_rgba_point(11./18,Colorf(0.318045715666458,0.579530768105703,0.869532419787959,1));
    fct.add_rgba_point(12./18,Colorf(6.73031483817477e-06,0.672209711760358,0.832351250564102,1));
    fct.add_rgba_point(13./18,Colorf(2.23830426071459e-05,0.742683824638324,0.785984792482808,1));
    fct.add_rgba_point(14./18,Colorf(3.20652979764136e-06,0.811372611242672,0.743751859419098,1));
    fct.add_rgba_point(15./18,Colorf(0.199459552956046,0.876700266700284,0.667357908571943,1));
    fct.add_rgba_point(16./18,Colorf(0.4750745614558,0.926049036141041,0.54473957279373,1));
    fct.add_rgba_point(17./18,Colorf(0.733675438664802,0.957983185511649,0.379215080990148,1));
    fct.add_rgba_point(18./18,Colorf(0.980732091795116,0.971913525460065,0.203610161638981,1));
    return fct;
  }
  
  TransferFunction TransferFunction::batlow_10()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0./9,Colorf(0.0052, 0.0982, 0.34,1));
    fct.add_rgba_point(1./9,Colorf(0.0631, 0.2471, 0.375,1));
    fct.add_rgba_point(2./9,Colorf(0.1097, 0.3531, 0.3842,1));
    fct.add_rgba_point(3./9,Colorf(0.2371, 0.4283, 0.3356,1));
    fct.add_rgba_point(4./9,Colorf(0.4095, 0.4824, 0.2413,1));
    fct.add_rgba_point(5./9,Colorf(0.616, 0.5372, 0.1698,1));
    fct.add_rgba_point(6./9,Colorf(0.8255, 0.5777, 0.2642,1));
    fct.add_rgba_point(7./9,Colorf(0.9708, 0.6324, 0.4833,1));
    fct.add_rgba_point(8./9,Colorf(0.9923, 0.7162, 0.7371,1));
    fct.add_rgba_point(9./9,Colorf(0.9814, 0.8004, 0.9813,1));
    return fct;
  }

  TransferFunction TransferFunction::parula()
  {
    TransferFunction fct(RGB);
    fct.add_rgba_point(0.,Colorf(0.207843,0.164706,0.529412,1));
    fct.add_rgba_point(1./9,Colorf(0.0117647,0.415686,0.882353,1));
    fct.add_rgba_point(2./9,Colorf(0.0784314,0.517647,0.835294,1));
    fct.add_rgba_point(3./9,Colorf(0.0235294,0.627451,0.803922,1));
    fct.add_rgba_point(4./9,Colorf(0.0901961,0.698039,0.698039,1));
    fct.add_rgba_point(5./9,Colorf(0.34902,0.741176,0.54902,1));
    fct.add_rgba_point(6./9,Colorf(0.647059,0.745098,0.419608,1));
    fct.add_rgba_point(7./9,Colorf(0.823529,0.733333,0.345098,1));
    fct.add_rgba_point(8./9,Colorf(0.996078,0.752941,0.227451,1));
    fct.add_rgba_point(1,Colorf(0.976471,0.984314,0.054902,1));
    return fct;
  }

  TransferFunction& TransferFunction::operator=(const TransferFunction& other)
  {
    clamp = other.clamp;
    _interpolation = other._interpolation;
    exteriorColor = other.exteriorColor;
    values = other.values;
    keys = other.keys;
    return *this;
  }
  
  double TransferFunction::operator[](int n) const {
    return values[n].first;
  }
  
  TransferFunction::Colorf TransferFunction::hsva_point(double pos) const
  {
    key_map::const_iterator it_found = keys.find(pos);
    if(it_found != keys.end()) {
      Colorf col = values[it_found.value()].second;
      if(_interpolation == RGB)
        col = convertRGBtoHSV(col);
      return col;
    }
    return Colorf(-1.0);
  }
  
  TransferFunction::Colorf TransferFunction::rgba_point(double pos) const
  {
    key_map::const_iterator it_found = keys.find(pos);
    if(it_found != keys.end()) {
      Colorf col = values[it_found.value()].second;
      if(_interpolation != RGB)
        col = convertHSVtoRGB(col);
      return col;
    }
    return Colorf(-1.0);
  }
  
  void TransferFunction::setPointList(const value_list& lst)
  {
    values = lst;
    update_keys();
  }
  
  bool TransferFunction::operator==(const TransferFunction& other) const {
    return values == other.values;
  }
  
  bool TransferFunction::operator!=(const TransferFunction& other) const {
    return values != other.values;
  }
}
