Commit 88e92baf authored by Paul Asmuth's avatar Paul Asmuth
Browse files

basic linechart implementation

parent f16d41ea
Loading
Loading
Loading
Loading
+1 −234
Original line number Diff line number Diff line

/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
@@ -8,11 +7,9 @@
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include "canvas.h"
#include "linechart.h"
#include "rendertarget.h"
#include <fnordmetric/util/runtimeexception.h>

namespace fnordmetric {
@@ -23,237 +20,7 @@ double LineChart::kDefaultLineWidth = 2.0f;
char LineChart::kDefaultPointStyle[] = "none";
double LineChart::kDefaultPointSize = 3.0f;

LineChart::LineChart(
    Canvas* canvas,
    Domain* x_domain /* = nullptr */,
    Domain* y_domain /* = nullptr */) :
    canvas_(canvas),
    x_domain_(x_domain),
    y_domain_(y_domain),
    num_series_(0) {}

void LineChart::addSeries(
      Series2D<double, double>* series,
      const std::string& line_style /* = kDefaultLineStyle */,
      double line_width /* = kDefaultLineWidth */,
      const std::string& point_style /* = kDefaultLineStyle */,
      double point_size /* = kDefaultPointsize */,
      bool smooth /* = false */) {

  Line<double> line;
  line.color = seriesColor(series);
  line.line_style = line_style;
  line.line_width = line_width;
  line.point_style = point_style;
  line.point_size = point_size;
  line.smooth = smooth;

  for (const auto& spoint : series->getData()) {
    line.points.emplace_back(std::get<0>(spoint), std::get<1>(spoint));
  }

  lines_.emplace_back(line);
}

void LineChart::addSeries(
      Series2D<std::string, double>* series,
      const std::string& line_style /* = kDefaultLineStyle */,
      double line_width /* = kDefaultLineWidth */,
      const std::string& point_style /* = kDefaultLineStyle */,
      double point_size /* = kDefaultPointsize */,
      bool smooth /* = false */) {
  Line<std::string> line;
  line.color = seriesColor(series);
  line.line_style = line_style;
  line.line_width = line_width;
  line.point_style = point_style;
  line.point_size = point_size;
  line.smooth = smooth;

  for (const auto& spoint : series->getData()) {
    line.points.emplace_back(std::get<0>(spoint), std::get<1>(spoint));
  }

  categorical_lines_.emplace_back(line);
}

AxisDefinition* LineChart::addAxis(AxisDefinition::kPosition position) {
  switch (position) {
    case AxisDefinition::TOP:
      return canvas_->addAxis(position, getXDomain());

    case AxisDefinition::RIGHT:
      return canvas_->addAxis(position, getYDomain());

    case AxisDefinition::BOTTOM:
      return canvas_->addAxis(position, getXDomain());

    case AxisDefinition::LEFT:
      return canvas_->addAxis(position, getYDomain());
  }
}

// FIXPAUL: copy&paste form barchart...
Domain* LineChart::getXDomain() const {
  if (x_domain_ != nullptr) {
    return x_domain_;
  }

  if (x_domain_auto_.get() == nullptr) {
    double x_min = 0.0f;
    double x_max = 0.0f;

    for (const auto& line : lines_) {
      for (const auto& point : line.points) {
        if (point.first > x_max) {
          x_max = point.first;
        }
        if (point.first < x_min) {
          x_min = point.first;
        }
      }
    }

    if (x_max > 0) {
      x_max *= 1.1;
    }

    if (x_max < 0) {
      x_max *= 0.9;
    }

    if (x_min > 0) {
      x_min *= 0.9;
    }

    if (x_min < 0) {
      x_min *= 1.1;
    }

    x_domain_auto_.reset(new NumericalDomain(x_min, x_max));
  }

  return x_domain_auto_.get();
}

// FIXPAUL: copy&paste form barchart...
Domain* LineChart::getYDomain() const {
  if (y_domain_ != nullptr) {
    return y_domain_;
  }

  if (y_domain_auto_.get() == nullptr) {
    double y_min = 0.0f;
    double y_max = 0.0f;

    for (const auto& line : lines_) {
      for (const auto& point : line.points) {
        if (point.second > y_max) {
          y_max = point.second;
        }
        if (point.second < y_min) {
          y_min = point.second;
        }
      }
    }

    if (y_max > 0) {
      y_max *= 1.1;
    }

    if (y_max < 0) {
      y_max *= 0.9;
    }

    if (y_min > 0) {
      y_min *= 0.9;
    }

    if (y_min < 0) {
      y_min *= 1.1;
    }

    y_domain_auto_.reset(new NumericalDomain(y_min, y_max));
  }


  return y_domain_auto_.get();
}

void LineChart::render(
    RenderTarget* target,
    int width,
    int height,
    std::tuple<int, int, int, int>* padding) const {
  auto padding_top = std::get<0>(*padding);
  auto padding_right = std::get<1>(*padding);
  auto padding_bottom = std::get<2>(*padding);
  auto padding_left = std::get<3>(*padding);
  auto inner_width = width - padding_right - padding_left;
  auto inner_height = height - padding_top - padding_bottom;
  auto x_domain = getXDomain();
  auto y_domain = getYDomain();

  target->beginGroup("lines");

  for (const auto& line : lines_) {
    std::vector<std::pair<double, double>> coords;

    for (const auto& point : line.points) {
      //coords.emplace_back(
      //    padding_left + x_domain->scale(point.first) * inner_width,
      //    padding_top + (1.0 - y_domain->scale(point.second)) * inner_height);
    }
  }

  for (const auto& line : categorical_lines_) {
    std::vector<std::pair<double, double>> coords;

    //for (const auto& point : line.points) {
    //  double x_offset = x_domain->offsetFor(point.first);
    //  double y_offset = y_domain->offsetFor(point.second);

    //  coords.emplace_back(
    //      padding_left + x_offset * inner_width,
    //      padding_top + (1.0 - y_offset) * inner_height);
    //}
  }

  target->finishGroup();
}


void LineChart::addSeries(Series2D<std::string, std::string>* series) {
  RAISE(
      util::RuntimeException,
      "unsupported series format for LineChart: <string, float>");
}

template <typename T>
void LineChart::drawLine(
    RenderTarget* target,
    T line,
    std::vector<std::pair<double, double>>& coords) {
  target->drawPath(
      coords,
      line.line_style,
      line.line_width,
      line.smooth,
      line.color,
      "line");

  if (line.point_style != "none") {
    for (const auto& point : coords) {
      target->drawPoint(
        point.first,
        point.second,
        line.point_style,
        line.point_size,
        line.color,
        "point");
    }
  }
}
LineChart::LineChart(ui::Canvas* canvas) : Drawable(canvas) {}

}
}
+96 −11
Original line number Diff line number Diff line
@@ -11,14 +11,16 @@
#define _FNORDMETRIC_LINECHART_H
#include <stdlib.h>
#include <assert.h>
#include "../base/series.h"
#include "axisdefinition.h"
#include "domain.h"
#include "drawable.h"
#include <fnordmetric/base/series.h>
#include <fnordmetric/ui/axisdefinition.h>
#include <fnordmetric/ui/domain.h>
#include <fnordmetric/ui/drawable.h>
#include <fnordmetric/ui/canvas.h>
#include <fnordmetric/ui/colorpalette.h>
#include <fnordmetric/ui/rendertarget.h>

namespace fnordmetric {
namespace ui {
class Canvas;

/**
 * This draws the series data as points.
@@ -29,17 +31,22 @@ class Canvas;
 *   line_width = default: 2
 *
 */
template <typename TX_, typename TY_>
class LineChart2D : public Drawable {
class LineChart : public Drawable {
public:
  typedef TX_ TX;
  typedef TY_ TY;

  static char kDefaultLineStyle[];
  static double kDefaultLineWidth;
  static char kDefaultPointStyle[];
  static double kDefaultPointSize;

  LineChart(ui::Canvas* canvas);
};

template <typename TX_, typename TY_>
class LineChart2D : public LineChart {
public:
  typedef TX_ TX;
  typedef TY_ TY;

  /**
   * Create a new line chart
   *
@@ -77,6 +84,17 @@ protected:
      RenderTarget* target,
      Viewport* viewport) const override;

  void drawLine(
      RenderTarget* target,
      Viewport* viewport,
      const std::vector<std::pair<double, double>>& coords,
      const std::string& line_style,
      double line_width,
      const std::string& point_style,
      double point_size,
      bool smooth,
      const std::string& color) const;

  DomainAdapter x_domain_;
  DomainAdapter y_domain_;
  std::vector<Series2D<TX, TY>*> series_;
@@ -84,7 +102,7 @@ protected:
};

template <typename TX, typename TY>
LineChart2D<TX, TY>::LineChart2D(Canvas* canvas) : Drawable(canvas) {}
LineChart2D<TX, TY>::LineChart2D(Canvas* canvas) : LineChart(canvas) {}

template <typename TX, typename TY>
void LineChart2D<TX, TY>::addSeries(Series2D<TX, TY>* series) {
@@ -120,6 +138,30 @@ template <typename TX, typename TY>
void LineChart2D<TX, TY>::render(
    RenderTarget* target,
    Viewport* viewport) const {
  target->beginGroup("lines");

  for (const auto& series : series_) {
    std::vector<std::pair<double, double>> coords;

    for (const auto& point : series->getData()) {
      coords.emplace_back(
          x_domain_.getAs<Domain<TX>>()->scale(point.x()),
          y_domain_.getAs<Domain<TY>>()->scale(point.y()));
    }

    drawLine(
        target,
        viewport,
        coords,
        "solid",
        2,
        "circle",
        4,
        false,
        series->getProperty(Series::P_COLOR));
  }

  target->finishGroup();
}

template <typename TX, typename TY>
@@ -148,6 +190,49 @@ AxisDefinition* LineChart2D<TX, TY>::addAxis(
  }
}

template <typename TX, typename TY>
void LineChart2D<TX, TY>::drawLine(
    RenderTarget* target,
    Viewport* viewport,
    const std::vector<std::pair<double, double>>& coords,
    const std::string& line_style,
    double line_width,
    const std::string& point_style,
    double point_size,
    bool smooth,
    const std::string& color) const {
  std::vector<std::pair<double, double>> coords_ss;

  for (const auto& coord : coords) {
    auto ss_x =
        viewport->paddingLeft() + coord.first * viewport->innerWidth();
    auto ss_y =
        viewport->paddingTop() + (1.0 - coord.second) * viewport->innerHeight();

    coords_ss.emplace_back(ss_x, ss_y);
  }

  target->drawPath(
      coords_ss,
      line_style,
      line_width,
      smooth,
      color,
      "line");

  if (point_style != "none") {
    for (const auto& point : coords_ss) {
      target->drawPoint(
        point.first,
        point.second,
        point_style,
        point_size,
        color,
        "point");
    }
  }
}

}
}
#endif