Program Listing for File HSVInterpolation.cpp

Return to documentation for file (src/flamegpu/visualiser/color/HSVInterpolation.cpp)

// @todo - ifdef FLAMEGPU_VISUALISATION
#include "flamegpu/visualiser/color/HSVInterpolation.h"

#include <sstream>

#include "flamegpu/exception/FLAMEGPUException.h"

namespace flamegpu {
namespace visualiser {

HSVInterpolation HSVInterpolation::REDGREEN(const std::string& variable_name, const float& _min_bound, const float& _max_bound) {
    auto rtn = HSVInterpolation(variable_name, 0.0f, 100.0f, 1.0f, 0.88f);
    rtn.setBounds(_min_bound, _max_bound);
    return rtn;
}
HSVInterpolation HSVInterpolation::GREENRED(const std::string& variable_name, const float& _min_bound, const float& _max_bound) {
    auto rtn = HSVInterpolation(variable_name, 100.0f, 0.0f, 1.0f, 0.88f);
    rtn.setBounds(_min_bound, _max_bound);
    return rtn;
}

HSVInterpolation::HSVInterpolation(const std::string &_variable_name, const float& hMin, const float& hMax, const float& s, const float& v)
    : hue_min(hMin)
    , hue_max(hMax)
    , saturation(s)
    , val(v)
    , variable_name(_variable_name) {
    if (hue_min < 0.0f || hue_min > 360.0f) {
        THROW exception::InvalidArgument("%f is not a valid hue value, hue components must be in the inclusive range [0.0, 360.0], "
        "in HSVInterpolation::HSVInterpolation()\n", hue_min);
    }
    if (hue_max < 0.0f || hue_max > 360.0f) {
        THROW exception::InvalidArgument("%f is not a valid hue value, hue components must be in the inclusive range [0.0, 360.0], "
        "in HSVInterpolation::HSVInterpolation()\n", hue_max);
    }
    if (hue_max == hue_min) {
        THROW exception::InvalidArgument("hue_min and _hue_max must differ, "
        "in HSVInterpolation::HSVInterpolation()\n", hue_max);
    }
    if (saturation < 0.0f || saturation > 1.0f) {
        THROW exception::InvalidArgument("%f is not a valid saturation, saturation must be in the inclusive range [0.0, 1.0], "
        "in HSVInterpolation::HSVInterpolation()\n", saturation);
    }
    if (val < 0.0f || val > 1.0f) {
        THROW exception::InvalidArgument("%f is not a valid val, val must be in the inclusive range [0.0, 1.0], "
        "in HSVInterpolation::HSVInterpolation()\n", val);
    }
}
HSVInterpolation& HSVInterpolation::setBounds(const float& _min_bound, const float& _max_bound) {
    if (_min_bound >= _max_bound) {
        THROW exception::InvalidArgument("max_bound (%f) must be greater than min_bound (%f), "
            "in HSVInterpolation::setBounds()\n",
            _max_bound, _min_bound);
    }
    min_bound = _min_bound;
    max_bound = _max_bound;
    return *this;
}
HSVInterpolation& HSVInterpolation::setWrapHue(const bool& _wrapHue) {
    wrap_hue = _wrapHue;
    return *this;
}
std::string HSVInterpolation::getSrc(const unsigned int array_len) const {
static const char* HEADER = R"###(
uniform samplerBuffer color_arg;
//hsv(0-360,0-1,0-1)
vec3 hsv2rgb(vec3 hsv) {
  if(hsv.g==0)//Grey
    return vec3(hsv.b);

  float h = hsv.r/60;
  int i = int(floor(h));
  float f = h-i;
  float p = hsv.b * (1-hsv.g);
  float q = hsv.b * (1-hsv.g * f);
  float t = hsv.b * (1-hsv.g * (1-f));
  switch(i) {
    case 0:
      return vec3(hsv.b,t,p);
    case 1:
      return vec3(q,hsv.b,p);
    case 2:
      return vec3(p,hsv.b,p);
    case 3:
      return vec3(p,q,hsv.b);
    case 4:
      return vec3(t,p,hsv.b);
    default: //case 5
      return vec3(hsv.b,p,q);
  }
}
)###";
    std::stringstream ss;
    ss << HEADER;
    ss << "vec4 calculateColor() {" << "\n";
    // Fetch the modifier from texture cache
    ss << "    float modifier = texelFetch(color_arg, gl_InstanceID * " << array_len << " + " << element << ").x;" << "\n";
    // Clamp the modifier to bounds
    ss << "    modifier = clamp(modifier, float(" << min_bound << "), float(" << max_bound << "));" << "\n";
    // Scale modifier to range [0.0, 1.0]
    ss << "    modifier = (modifier - " << min_bound << ") / float(" << (max_bound - min_bound) << ");" << "\n";
    // Apply HSV interpolation
    if (wrap_hue) {
        // Calculate hue
        if (hue_min > hue_max) {
            ss << "    modifier = " << hue_min << " + (modifier * " << (360.0f + hue_max - hue_min) << ");" << "\n";
        } else {
            ss << "    modifier = " << hue_min << " + (modifier * " << -(360.0f - (hue_max - hue_min)) << ");" << "\n";
        }
        // Wrap hue
        ss << "    modifier = modifier < 0.0 ? modifier + 360.0 : modifier;" << "\n";  // Only required by hue_min > hue_max?
        ss << "    modifier = modifier > 360.0 ? modifier - 360.0 : modifier;" << "\n";  // Only required by hue_max > hue_min?
        ss << "    return vec4(hsv2rgb(vec3(modifier, " << saturation << ", " << val << ")), 1.0);" << "\n";
    } else {
        if (hue_min < hue_max) {
            ss << "    return vec4(hsv2rgb(vec3(" << hue_min << " + (modifier * " << (hue_max - hue_min) << "), " << saturation << ", " << val << ")), 1.0);" << "\n";
        } else {
            ss << "    modifier = 1.0 - modifier;" << "\n";
            ss << "    return vec4(hsv2rgb(vec3(" << hue_max << " + (modifier * " << (hue_min - hue_max) << "), " << saturation << ", " << val << ")), 1.0);" << "\n";
        }
    }
    ss << "}" << "\n";
    return ss.str();
}
std::string HSVInterpolation::getSamplerName() const {
    return "color_arg";
}
std::string HSVInterpolation::getAgentVariableName() const {
    return variable_name;
}
std::type_index HSVInterpolation::getAgentVariableRequiredType() const {
    return std::type_index(typeid(float));
}

}  // namespace visualiser
}  // namespace flamegpu