Commit ec95d7c6 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

linechart: basic series rendering

parent 02427b8d
Loading
Loading
Loading
Loading
+33 −145
Original line number Diff line number Diff line
@@ -40,165 +40,47 @@ namespace plotfx {
namespace linechart {

/*
 *
char LineChart::kDefaultLineStyle[] = "solid";
char LineChart::kDefaultLineWidth[] = "2";
char LineChart::kDefaultPointStyle[] = "none";
char LineChart::kDefaultPointSize[] = "3";
*/

LineChart::LineChart(
    AnyDomain* x_domain,
    AnyDomain* y_domain) :
    show_labels_(false) {
  x_domain_.reset(x_domain);
  y_domain_.reset(y_domain);
}

void LineChart::addSeries(Series2D<TX, TY>* series) {
  Domain<TX>* x_domain;
  if (x_domain_.empty()) {
    x_domain = Domain<TX>::mkDomain();
    x_domain_.reset(x_domain, true);
  } else {
    x_domain = x_domain_.getAs<Domain<TX>>();
  }

  Domain<TY>* y_domain;
  if (y_domain_.empty()) {
    y_domain = Domain<TY>::mkDomain();
    y_domain_.reset(y_domain, true);

    auto cont = dynamic_cast<AnyContinuousDomain*>(y_domain);
    if (cont != nullptr) {
      cont->setPadding(
          AnyDomain::kDefaultDomainPadding,
          AnyDomain::kDefaultDomainPadding);
    }
  } else {
    y_domain = y_domain_.getAs<Domain<TY>>();
  }

  for (const auto& point : series->getData()) {
    x_domain->addValue(point.x());
    y_domain->addValue(point.y());
  }

  series_.push_back(series);

  if (!series->hasProperty(Series::P_COLOR)) {
    color_palette_.setNextColor(series);
  }

  series->setDefaultProperty(
      Series::P_LINE_STYLE,
      LineChart::kDefaultLineStyle);

  series->setDefaultProperty(
      Series::P_LINE_WIDTH,
      LineChart::kDefaultLineWidth);

  series->setDefaultProperty(
      Series::P_POINT_STYLE,
      LineChart::kDefaultPointStyle);

  series->setDefaultProperty(
      Series::P_POINT_SIZE,
      LineChart::kDefaultPointSize);

  //Drawable::addSeries(series);
}
LinechartConfig::LinechartConfig() :
    x_domain(PlotDomain::LINEAR),
    y_domain(PlotDomain::LINEAR),
    margin_rem(4.0f) {}

void LineChart::render(
    Layer* target,
    Viewport* viewport) const {
  if (x_domain_.get() == nullptr || y_domain_.get() == nullptr) {
    RAISE(kRuntimeError, "could not build domains");
ReturnCode drawSeries(
    const LinechartSeries& series,
    const DomainConfig& domain_x,
    const DomainConfig& domain_y,
    const Rectangle& clip,
    Layer* layer) {
  if (series.xs.size() != series.ys.size()) {
    // FIXME error msg
    return ERROR_INVALID_ARGUMENT;
  }

  x_domain_.get()->build();
  y_domain_.get()->build();


  for (const auto& series : series_) {
  Path path;

    auto point_style = series->getProperty(Series::P_POINT_STYLE);
    double point_size;
    auto line_style = series->getProperty(Series::P_LINE_STYLE);
    double line_width;

    try {
      line_width = std::stod(series->getProperty(Series::P_LINE_WIDTH));
    } catch (const std::exception& e) {
      RAISE(
          kRuntimeError,
          "invalid line width: %s",
          series->getProperty(Series::P_LINE_WIDTH).c_str());
    }

    try {
      point_size = std::stod(series->getProperty(Series::P_POINT_SIZE));
    } catch (const std::exception& e) {
      RAISE(
          kRuntimeError,
          "invalid point size: %s",
          series->getProperty(Series::P_POINT_SIZE).c_str());
    }

    auto color = series->getProperty(Series::P_COLOR);

    for (const auto& point : series->getData()) {
      auto x = x_domain_.getAs<Domain<TX>>()->scale(point.x());
      auto y = y_domain_.getAs<Domain<TY>>()->scale(point.y());
      auto ss_x = viewport->paddingLeft() + x * viewport->innerWidth();
      auto ss_y = viewport->paddingTop() + (1.0 - y) * viewport->innerHeight();
      auto label = series->labelFor(&point);

      if (show_labels_) {
        drawText(
            target,
            label,
            ss_x,
            ss_y - point_size - kLabelPadding,
            "middle",
            "text-after-edge",
            "label");
      }

      //target->drawPoint(
      //    ss_x,
      //    ss_y,
      //    point_style,
      //    point_size,
      //    color,
      //    "point",
      //    label,
      //    series->name());

      if (path.empty()) {
        path.moveTo(ss_x, ss_y);
  for (size_t i = 0; i < series.xs.size(); ++i) {
    auto x = series.xs[i];
    auto y = series.ys[i];
    auto sx = clip.x + domain_translate(domain_x, x) * clip.w;
    auto sy = clip.y + domain_translate(domain_y, y) * clip.h;

    if (i == 0) {
      path.moveTo(sx, sy);
    } else {
        path.lineTo(ss_x, ss_y);
      path.lineTo(sx, sy);
    }
  }

  StrokeStyle style;
    style.line_width = line_width;
    //style.colour = colour;
    strokePath(
        target,
        path.data(),
        path.size(),
        style);
  }
  strokePath(layer, path, style);

  return OK;
}
*/

LinechartConfig::LinechartConfig() :
    x_domain(PlotDomain::LINEAR),
    y_domain(PlotDomain::LINEAR),
    margin_rem(4.0f) {}

ReturnCode draw(
    const LinechartConfig& config,
@@ -258,6 +140,12 @@ ReturnCode draw(
    return rc;
  }

  for (const auto& s : config.series) {
    if (auto rc = drawSeries(s, domain_x, domain_y, border_box, layer); !rc) {
      return rc;
    }
  }

  return ReturnCode::success();
}

+12 −0
Original line number Diff line number Diff line
@@ -43,6 +43,18 @@ void domain_fit(const std::vector<double>& data, DomainConfig* domain) {
  }
}

double domain_translate(const DomainConfig& domain, double v) {
  auto min = domain.min.value_or(0.0f);
  auto max = domain.max.value_or(0.0f);

  switch (domain.space) {
    case PlotDomain::LINEAR:
      return (v - min) / (max - min);
    default:
      return 0.0f;
  }
}


namespace chart {

+3 −1
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@
namespace plotfx {

enum class PlotDomain {
  LINEAR, LOGARITHMIC
  LINEAR
};

struct DomainConfig {
@@ -51,6 +51,8 @@ struct DomainConfig {

void domain_fit(const std::vector<double>& data, DomainConfig* domain);

double domain_translate(const DomainConfig& domain, double v);

namespace chart {

/**