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

linechart: split linechart into plot and plot_lines

parent 9519a9a1
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -17,9 +17,10 @@ include_directories(${CAIRO_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ${HARFBUZZ_IN
add_definitions(-DFNORDMETRIC_VERSION="unstable")

add_library(plotfxlib STATIC
    elements/line_chart.cc
    elements/gridlines.cc
    elements/plot.cc
    elements/plot_axis.cc
    elements/plot_lines.cc
    common/config_helpers.cc
    common/domain.cc
    common/format.cc
+2 −2
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "element_factory.h"
#include "elements/line_chart.h"
#include "elements/plot.h"
#include <unordered_map>

namespace plotfx {
@@ -36,7 +36,7 @@ namespace plotfx {
using ElementConfigureFn = std::function<ReturnCode (const PropertyList&, ElementRef*)>;

static std::unordered_map<std::string, ElementConfigureFn> elems = {
  {"linechart", &linechart::configure}
  {"plot", &plot::configure}
};

ReturnCode buildElement(
+18 −102
Original line number Diff line number Diff line
@@ -28,28 +28,19 @@
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "plot.h"
#include <plotfx.h>
#include <graphics/path.h>
#include <graphics/brush.h>
#include <graphics/text.h>
#include <graphics/layout.h>
#include "line_chart.h"
#include "common/config_helpers.h"
#include "plot_lines.h"

namespace plotfx {
namespace linechart {
namespace plot {

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

LinechartSeries::LinechartSeries() :
    line_width(from_pt(2)) {}

LinechartConfig::LinechartConfig() :
PlotConfig::PlotConfig() :
    margins({
        Measure(Unit::REM, 4.0f),
        Measure(Unit::REM, 4.0f),
@@ -58,74 +49,14 @@ LinechartConfig::LinechartConfig() :
  domain_y.padding = 0.1f;
}

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;
  }

  // draw line
  {
    Path path;
    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 + (1.0 - domain_translate(domain_y, y)) * clip.h;

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

    StrokeStyle style;
    style.line_width = series.line_width;
    style.colour = series.line_colour;
    strokePath(layer, clip, path, style);
  }

  // draw points
  auto point_size = to_px(layer->measures, series.point_size);
  if (point_size > 0) {
    FillStyle style;
    style.colour = series.point_colour;
    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 + (1.0 - domain_translate(domain_y, y)) * clip.h;

      // FIXME point style
      Path path;
      path.moveTo(sx + point_size, sy);
      path.arcTo(sx, sy, point_size, 0, M_PI * 2);
      fillPath(layer, clip, path, style);
    }
  }

  return OK;
}

ReturnCode draw(
    const LinechartConfig& config,
    const PlotConfig& config,
    const Rectangle& clip,
    Layer* layer) {
  // setup domains
  auto domain_x = config.domain_x;
  auto domain_y = config.domain_y;

  for (const auto& s : config.series) {
    domain_fit(s.xs, &domain_x);
    domain_fit(s.ys, &domain_y);
  }

  // setup layout
  auto border_box = layout_margin_box(
      clip,
@@ -150,7 +81,7 @@ ReturnCode draw(

  // render series
  for (const auto& s : config.series) {
    if (auto rc = drawSeries(s, domain_x, domain_y, border_box, layer); !rc) {
    if (auto rc = s.draw(config, border_box, layer); !rc) {
      return rc;
    }
  }
@@ -158,38 +89,16 @@ ReturnCode draw(
  return ReturnCode::success();
}

ReturnCode configureSeries(const plist::Property& prop, LinechartConfig* config) {
  if (!prop.child) {
    return ERROR_INVALID_ARGUMENT;
  }

  LinechartSeries series;
  static const ParserDefinitions pdefs = {
    {"xs", std::bind(&parseDataSeries, std::placeholders::_1, &series.xs)},
    {"ys", std::bind(&parseDataSeries, std::placeholders::_1, &series.ys)},
    {
      "colour",
      configure_multiprop({
          std::bind(&configure_colour, std::placeholders::_1, &series.line_colour),
          std::bind(&configure_colour, std::placeholders::_1, &series.point_colour),
      })
    },
    {"line-colour", std::bind(&configure_colour, std::placeholders::_1, &series.line_colour)},
    {"line-width", std::bind(&parseMeasureProp, std::placeholders::_1, &series.line_width)},
    {"point-colour", std::bind(&configure_colour, std::placeholders::_1, &series.point_colour)},
    {"point-size", std::bind(&parseMeasureProp, std::placeholders::_1, &series.point_size)},
  };

  if (auto rc = parseAll(*prop.child, pdefs); !rc) {
ReturnCode configure_series(const plist::Property& prop, PlotConfig* config) {
  if (auto rc = lines::configure(prop, config); !rc) {
    return rc;
  }

  config->series.emplace_back(std::move(series));
  return OK;
}

ReturnCode configure(const plist::PropertyList& plist, ElementRef* elem) {
  LinechartConfig config;
  PlotConfig config;
  static const ParserDefinitions pdefs = {
    {
      "margin",
@@ -214,13 +123,20 @@ ReturnCode configure(const plist::PropertyList& plist, ElementRef* elem) {
    {"xmax", std::bind(&configure_float_opt, std::placeholders::_1, &config.domain_x.max)},
    {"ymin", std::bind(&configure_float_opt, std::placeholders::_1, &config.domain_y.min)},
    {"ymax", std::bind(&configure_float_opt, std::placeholders::_1, &config.domain_y.max)},
    {"series", std::bind(&configureSeries, std::placeholders::_1, &config)},
  };

  if (auto rc = parseAll(plist, pdefs); !rc.isSuccess()) {
    return rc;
  }

  static const ParserDefinitions pdefs_series = {
    {"series", std::bind(&configure_series, std::placeholders::_1, &config)}
  };

  if (auto rc = parseAll(plist, pdefs_series); !rc.isSuccess()) {
    return rc;
  }

  auto e = std::make_unique<Element>();
  e->draw = std::bind(&draw, config, std::placeholders::_1, std::placeholders::_2);
  *elem = std::move(e);
@@ -228,6 +144,6 @@ ReturnCode configure(const plist::PropertyList& plist, ElementRef* elem) {
  return ReturnCode::success();
}

} // namespace linechart
} // namespace plot
} // namespace plotfx
+9 −14
Original line number Diff line number Diff line
@@ -38,20 +38,15 @@
#include "plot_axis.h"

namespace plotfx {
namespace linechart {
namespace plot {
struct PlotConfig;

struct LinechartSeries {
  LinechartSeries();
  std::vector<double> xs;
  std::vector<double> ys;
  Measure line_width;
  Colour line_colour;
  Measure point_size;
  Colour point_colour;
struct PlotSeries {
  std::function<ReturnCode (const PlotConfig& plot, const Rectangle& clip, Layer* frame)> draw;
};

struct LinechartConfig {
  LinechartConfig();
struct PlotConfig {
  PlotConfig();
  DomainConfig domain_x;
  DomainConfig domain_y;
  AxisDefinition axis_top;
@@ -59,15 +54,15 @@ struct LinechartConfig {
  AxisDefinition axis_bottom;
  AxisDefinition axis_left;
  Measure margins[4];
  std::vector<LinechartSeries> series;
  std::vector<PlotSeries> series;
};

ReturnCode draw(const LinechartConfig& config, const Rectangle& clip, Layer* frame);
ReturnCode draw(const PlotConfig& config, const Rectangle& clip, Layer* frame);

ReturnCode configure(
    const plist::PropertyList& plist,
    ElementRef* elem);

} // namespace linechart
} // namespace plot
} // namespace plotfx

elements/plot_lines.cc

0 → 100644
+147 −0
Original line number Diff line number Diff line
/**
 * This file is part of the "plotfx" project
 *   Copyright (c) 2018 Paul Asmuth
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "plot_lines.h"
#include <plotfx.h>
#include <graphics/path.h>
#include <graphics/brush.h>
#include <graphics/text.h>
#include <graphics/layout.h>
#include "common/config_helpers.h"

namespace plotfx {
namespace plot {
namespace lines {

PlotLinesConfig::PlotLinesConfig() :
    line_width(from_pt(2)) {}

ReturnCode draw_lines(
    const PlotConfig& plot,
    const PlotLinesConfig& series,
    const Rectangle& clip,
    Layer* layer) {
  const auto& domain_x = plot.domain_x;
  const auto& domain_y = plot.domain_y;

  if (series.xs.size() != series.ys.size()) {
    // FIXME error msg
    return ERROR_INVALID_ARGUMENT;
  }

  // draw line
  {
    Path path;
    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 + (1.0 - domain_translate(domain_y, y)) * clip.h;

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

    StrokeStyle style;
    style.line_width = series.line_width;
    style.colour = series.line_colour;
    strokePath(layer, clip, path, style);
  }

  // draw points
  auto point_size = to_px(layer->measures, series.point_size);
  if (point_size > 0) {
    FillStyle style;
    style.colour = series.point_colour;
    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 + (1.0 - domain_translate(domain_y, y)) * clip.h;

      // FIXME point style
      Path path;
      path.moveTo(sx + point_size, sy);
      path.arcTo(sx, sy, point_size, 0, M_PI * 2);
      fillPath(layer, clip, path, style);
    }
  }

  return OK;
}

ReturnCode configure(const plist::Property& prop, PlotConfig* config) {
  if (!prop.child) {
    return ERROR_INVALID_ARGUMENT;
  }

  PlotLinesConfig series;
  static const ParserDefinitions pdefs = {
    {"xs", std::bind(&parseDataSeries, std::placeholders::_1, &series.xs)},
    {"ys", std::bind(&parseDataSeries, std::placeholders::_1, &series.ys)},
    {
      "colour",
      configure_multiprop({
          std::bind(&configure_colour, std::placeholders::_1, &series.line_colour),
          std::bind(&configure_colour, std::placeholders::_1, &series.point_colour),
      })
    },
    {"line-colour", std::bind(&configure_colour, std::placeholders::_1, &series.line_colour)},
    {"line-width", std::bind(&parseMeasureProp, std::placeholders::_1, &series.line_width)},
    {"point-colour", std::bind(&configure_colour, std::placeholders::_1, &series.point_colour)},
    {"point-size", std::bind(&parseMeasureProp, std::placeholders::_1, &series.point_size)},
  };

  if (auto rc = parseAll(*prop.child, pdefs); !rc) {
    return rc;
  }

  domain_fit(series.xs, &config->domain_x);
  domain_fit(series.ys, &config->domain_y);

  config->series.emplace_back(PlotSeries {
    .draw = std::bind(
        &draw_lines,
        std::placeholders::_1,
        series,
        std::placeholders::_2,
        std::placeholders::_3),
  });

  return OK;
}

} // namespace lines
} // namespace plot
} // namespace plotfx
Loading