text: // Copyright 2017-2019, Nicholas Sharp and the Polyscope contributors. http://polyscope.run.
  #pragma once
  
  #include "glm/glm.hpp"
  
  namespace polyscope {
  
  inline std::string defaultColorMap(DataType type) {
    switch (type) {
    case DataType::STANDARD:
      return "viridis";
      break;
    case DataType::SYMMETRIC:
      return "coolwarm";
    case DataType::MAGNITUDE:
      return "blues";
      break;
    }
    return "viridis";
  }
  
  // Helpers used mainy to treat vectors as fields
  namespace {
  
  
  // Field "size" used to compare elements for SIGNED bigness
  template <typename T>
  typename FIELD_MAG<T>::type FIELD_BIGNESS(T x) {
    return x;
  }
  template <>
  typename FIELD_MAG<glm::vec3>::type FIELD_BIGNESS(glm::vec3 x) {
    return glm::length(x);
  }
  
  // Multiplicative identity
  template <typename T>
  T FIELD_ONE() {
    return 1;
  }
  template <>
  glm::vec3 FIELD_ONE() {
    return glm::vec3{1., 1., 1.};
  }
  
  // Additive identity
  template <typename T>
  T FIELD_ZERO() {
    return 0;
  }
  template <>
  glm::vec3 FIELD_ZERO() {
    return glm::vec3{0., 0., 0.};
  }
  }; // namespace
  
  
  template <typename T>
  std::pair<typename FIELD_MAG<T>::type, typename FIELD_MAG<T>::type> robustMinMax(const std::vector<T>& data,
                                                                                   typename FIELD_MAG<T>::type rangeEPS) {
  
    if (data.size() == 0) {
      return std::make_pair(-1.0, 1.0);
    }
  
    // Compute max and min of data for mapping
    typename FIELD_MAG<T>::type minVal = std::numeric_limits<typename FIELD_MAG<T>::type>::infinity();
    typename FIELD_MAG<T>::type maxVal = -std::numeric_limits<typename FIELD_MAG<T>::type>::infinity();
    bool anyFinite = false;
    for (const T& x : data) {
      if (std::isfinite(FIELD_BIGNESS(x))) {
        minVal = std::min(minVal, FIELD_BIGNESS(x));
        maxVal = std::max(maxVal, FIELD_BIGNESS(x));
        anyFinite = true;
      }
    }
    if (!anyFinite) {
      return std::make_pair(-1.0, 1.0);
    }
    typename FIELD_MAG<T>::type maxMag = std::max(std::abs(minVal), std::abs(maxVal));
  
    // Hack to do less ugly things when constants (or near-constant) are passed in
    if (maxMag < rangeEPS) {
      maxVal = rangeEPS;
      minVal = -rangeEPS;
    } else if ((maxVal - minVal) / maxMag < rangeEPS) {
      typename FIELD_MAG<T>::type mid = (minVal + maxVal) / 2.0;
      maxVal = mid + maxMag * rangeEPS;
      minVal = mid - maxMag * rangeEPS;
    }
  
    return std::make_pair(minVal, maxVal);
  }
  
  template <typename T>
  AffineRemapper<T>::AffineRemapper(T offset_, typename FIELD_MAG<T>::type scale_)
      : offset(offset_), scale(scale){
  
                         };
  
  template <typename T>
  AffineRemapper<T>::AffineRemapper()
      : offset(FIELD_ZERO<T>()), scale(1.0), minVal(std::numeric_limits<typename FIELD_MAG<T>::type>::quiet_NaN()),
        maxVal(std::numeric_limits<typename FIELD_MAG<T>::type>::quiet_NaN()){
  
        };
  
  template <typename T>
  AffineRemapper<T>::AffineRemapper(const std::vector<T>& data, DataType datatype) {
    // Compute max and min of data for mapping
    minVal = std::numeric_limits<typename FIELD_MAG<T>::type>::infinity();
    T minElem;
    maxVal = -std::numeric_limits<typename FIELD_MAG<T>::type>::infinity();
    for (const T& x : data) {
      if (FIELD_BIGNESS(x) < minVal) {
        minElem = x;
        minVal = FIELD_BIGNESS(x);
      }
      maxVal = std::max(maxVal, FIELD_BIGNESS(x));
    }
    typename FIELD_MAG<T>::type maxMag = std::max(std::abs(minVal), std::abs(maxVal));
  
    // Hack to do less ugly things when constants (or near-constant) are passed in
    typename FIELD_MAG<T>::type rangeEPS = 1E-12;
    if (maxMag < rangeEPS) {
      maxVal = rangeEPS;
      minVal = -rangeEPS;
      minElem = minVal * FIELD_ONE<T>();
    } else if ((maxVal - minVal) / maxMag < rangeEPS) {
      typename FIELD_MAG<T>::type mid = (minVal + maxVal) / 2.0;
      maxVal = mid + maxMag * rangeEPS;
      minVal = mid - maxMag * rangeEPS;
      minElem = minVal * FIELD_ONE<T>();
    }
    maxMag = std::max(std::abs(minVal), std::abs(maxVal));
  
    if (datatype == DataType::STANDARD) {
      offset = minElem;
      scale = 1.0 / (maxVal - minVal);
    } else if (datatype == DataType::SYMMETRIC) {
      offset = -FIELD_ONE<T>() * maxMag;
      scale = 1.0 / (2 * maxMag);
    } else if (datatype == DataType::MAGNITUDE) {
      offset = FIELD_ZERO<T>();
      scale = 1.0 / (maxMag);
    }
  }
  
  template <typename T>
  AffineRemapper<T>::AffineRemapper(typename FIELD_MAG<T>::type minVal, typename FIELD_MAG<T>::type maxVal,
                                    DataType datatype) {
    if (datatype == DataType::STANDARD) {
      offset = minVal * FIELD_ONE<T>();
      scale = 1.0 / (maxVal - minVal);
    } else if (datatype == DataType::SYMMETRIC) {
      typename FIELD_MAG<T>::type maxMag = std::max(std::abs(minVal), std::abs(maxVal));
      offset = -FIELD_ONE<T>() * maxMag;
      scale = 1.0 / (2 * maxMag);
    } else if (datatype == DataType::MAGNITUDE) {
      typename FIELD_MAG<T>::type maxMag = std::max(std::abs(minVal), std::abs(maxVal));
      offset = FIELD_ZERO<T>();
      scale = 1.0 / (maxMag);
    }
  }
  
  template <typename T>
  void AffineRemapper<T>::setMinMax(const std::vector<T>& data) {
    minVal = std::numeric_limits<typename FIELD_MAG<T>::type>::infinity();
    maxVal = -std::numeric_limits<typename FIELD_MAG<T>::type>::infinity();
    for (const T& x : data) {
      minVal = std::min(minVal, FIELD_BIGNESS(x));
      maxVal = std::max(maxVal, FIELD_BIGNESS(x));
    }
  }
  
  template <typename T>
  inline T AffineRemapper<T>::map(const T& x) {
    return (x - offset) * scale;
  }
  
  template <typename T>
  std::string AffineRemapper<T>::printBounds() {
    const size_t bSize = 32;
    std::array<char, bSize> b;
    snprintf(b.data(), bSize, "[%6.2e, %6.2e]", minVal, maxVal);
    return std::string(b.data());
  }
  
  }; // namespace polyscope
  