Commit 97b7cc81 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

add the 'gradient' and 'step' color maps

parent ee1d0f07
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
@@ -15,5 +15,108 @@

namespace fviz {

ColorMap color_map_gradient(std::vector<std::pair<double, Color>> gradient) {
  std::sort(
      gradient.begin(),
      gradient.end(),
      [] (const auto& a, const auto& b) {
        return a.first < b.first;
      });

  return [gradient] (const auto& value, Color* color) -> ReturnCode {
    if (gradient.empty()) {
      *color = Color{};
      return OK;
    }

    double value_num  = 0.0;
    try {
      value_num = std::clamp(std::stod(value), 0.0, 1.0);
    } catch (...) {
      return errorf(
          ERROR,
          "invalid data; can't map '{}' to a color",
          value);
    }

    auto step = std::lower_bound(
        gradient.begin(),
        gradient.end(),
        value_num,
        [] (const auto& a, auto b) {
          return a.first < b;
        });

    double limit0 = 0.0;
    auto limit1 = step->first;
    Color color0;
    auto color1 = step->second;

    if (step != gradient.end() && step != gradient.begin()) {
      limit0 = (step - 1)->first;
      color0 = (step - 1)->second;
    }

    auto weight1 = (value_num - limit0) / (limit1 - limit0);
    auto weight0 = 1.0 - weight1;

    if (value_num >= limit1) {
      weight1 = 1.0;
      weight0 = 0.0;
    }

    *color = Color::fromRGBA(
        color0[0] * weight0 + color1[0] * weight1,
        color0[1] * weight0 + color1[1] * weight1,
        color0[2] * weight0 + color1[2] * weight1,
        color0[3] * weight0 + color1[3] * weight1);

    return OK;
  };
}


ColorMap color_map_steps(std::vector<std::pair<double, Color>> steps) {
  std::sort(
      steps.begin(),
      steps.end(),
      [] (const auto& a, const auto& b) {
        return a.first < b.first;
      });

  return [steps] (const auto& value, Color* color) -> ReturnCode {
    if (steps.empty()) {
      *color = Color{};
      return OK;
    }

    double value_num  = 0.0;
    try {
      value_num = std::clamp(std::stod(value), 0.0, 1.0);
    } catch (...) {
      return errorf(
          ERROR,
          "invalid data; can't map '{}' to a color",
          value);
    }

    auto step = std::upper_bound(
        steps.begin(),
        steps.end(),
        value_num,
        [] (const auto& a, const auto& b) {
          return a < b.first;
        });

    if (step == steps.begin()) {
      *color = {};
    } else {
      *color = (step - 1)->second;
    }

    return OK;
  };
}

} // namespace fviz
+4 −2
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@

namespace fviz {

using ColorMap = std::function<ReturnCode (double v, Color* c)>;
using ColorMap = std::function<ReturnCode (const std::string& v, Color* c)>;

ColorMap color_map_raw(std::vector<Color> colors);
ColorMap color_map_gradient(std::vector<std::pair<double, Color>> gradient);

ColorMap color_map_steps(std::vector<std::pair<double, Color>> steps);

} // namespace fviz
+86 −0
Original line number Diff line number Diff line
@@ -125,6 +125,84 @@ ReturnCode color_read_opt(
  return OK;
}

ReturnCode color_map_read_gradient(
    const Environment& env,
    const Expr* expr,
    ColorMap* color_map) {
  std::vector<std::pair<double, Color>> gradient;

  for (; expr; expr = expr_next(expr)) {
    if (!expr_is_list(expr)) {
      return errorf(
          ERROR,
          "invalid argument to 'gradient'; expected a 2-tuple, but got: '{}'",
          expr_inspect(expr));
    }

    auto args = expr_collect(expr_get_list(expr));
    if (args.size() != 2) {
      return errorf(
          ERROR,
          "invalid argument to 'gradient'; expected a 2-tuple, but got: '{}'",
          expr_inspect(expr));
    }

    double step;
    if (auto rc = expr_to_ratio(args[0], &step); !rc) {
      return rc;
    }

    Color color;
    if (auto rc = color_read(env, args[1], &color); !rc) {
      return rc;
    }

    gradient.emplace_back(step, color);
  }

  *color_map = color_map_gradient(gradient);
  return OK;
}

ReturnCode color_map_read_steps(
    const Environment& env,
    const Expr* expr,
    ColorMap* color_map) {
  std::vector<std::pair<double, Color>> steps;

  for (; expr; expr = expr_next(expr)) {
    if (!expr_is_list(expr)) {
      return errorf(
          ERROR,
          "invalid argument to 'steps'; expected a 2-tuple, but got: '{}'",
          expr_inspect(expr));
    }

    auto args = expr_collect(expr_get_list(expr));
    if (args.size() != 2) {
      return errorf(
          ERROR,
          "invalid argument to 'steps'; expected a 2-tuple, but got: '{}'",
          expr_inspect(expr));
    }

    double step;
    if (auto rc = expr_to_ratio(args[0], &step); !rc) {
      return rc;
    }

    Color color;
    if (auto rc = color_read(env, args[1], &color); !rc) {
      return rc;
    }

    steps.emplace_back(step, color);
  }

  *color_map = color_map_steps(steps);
  return OK;
}

ReturnCode color_map_read(
    const Environment& env,
    const Expr* expr,
@@ -138,6 +216,14 @@ ReturnCode color_map_read(

  expr = expr_get_list(expr);

  if (expr_is_value(expr, "gradient")) {
    return color_map_read_gradient(env, expr_next(expr), color_map);
  }

  if (expr_is_value(expr, "steps")) {
    return color_map_read_steps(env, expr_next(expr), color_map);
  }

  return errorf(
      ERROR,
      "invalid value to <color-map>; got '{}', but expected one of: \n"
+6 −0
Original line number Diff line number Diff line
@@ -125,6 +125,12 @@ ReturnCode expr_to_float64_opt_pair(
  return OK;
}

ReturnCode expr_to_ratio(
    const Expr* expr,
    double* value) {
  return expr_to_float64(expr, value);
}

ReturnCode expr_to_switch(
    const Expr* expr,
    bool* value) {
+4 −0
Original line number Diff line number Diff line
@@ -52,6 +52,10 @@ ReturnCode expr_to_float64_opt_pair(
    std::optional<double>* v1,
    std::optional<double>* v2);

ReturnCode expr_to_ratio(
    const Expr* expr,
    double* value);

ReturnCode expr_to_switch(
    const Expr* expr,
    bool* value);
Loading