Commit 2125478c authored by Paul Asmuth's avatar Paul Asmuth
Browse files

update 'areas' element

parent 9b6cc8d0
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "element_factory.h"
#include "elements/areas.h"
#include "elements/axis.h"
#include "elements/bars.h"
#include "elements/lines.h"
@@ -40,6 +41,13 @@ namespace plotfx {
using ElementConfigureFn = std::function<ReturnCode (const Document&, const PropertyList&, ElementRef*)>;

static std::unordered_map<std::string, ElementBuilder> elems = {
  {
    "areas",
    elem_builder<plot::area::PlotAreaConfig>(
        &plot::area::configure,
        &plot::area::layout,
        &plot::area::draw)
  },
  {"axis", elem_builder<AxisDefinition>(&axis::configure, &axis::layout, &axis::draw)},
  {
    "bars",
+236 −0
Original line number Diff line number Diff line
@@ -28,14 +28,15 @@
 * 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 "areas.h"
#include "plotfx.h"
#include "graphics/path.h"
#include "graphics/brush.h"
#include "graphics/text.h"
#include "graphics/layout.h"
#include "config_helpers.h"

#include <numeric>
#include "plot_area.h"
#include <plotfx.h>
#include <graphics/path.h>
#include <graphics/brush.h>
#include <graphics/text.h>
#include <graphics/layout.h>
#include "source/config_helpers.h"

using namespace std::placeholders;

@@ -43,29 +44,51 @@ namespace plotfx {
namespace plot {
namespace area {

static const double kDefaultLineWidthPT = 2;
PlotAreaConfig::PlotAreaConfig() :
    direction(Direction::VERTICAL) {}

ReturnCode draw(
    const PlotAreaConfig& config,
    const Rectangle& clip,
ReturnCode draw_horizontal(
    PlotAreaConfig config,
    const LayoutInfo& layout,
    Layer* layer) {
  for (const auto& group : config.groups) {
  const auto& clip = layout.content_box;

  /* convert units */
  convert_units(
      {
        bind(&convert_unit_typographic, layer->dpi, layer->font_size.value, _1),
        bind(&convert_unit_user, domain_translate_fn(config.scale_x), _1),
        bind(&convert_unit_relative, clip.w, _1)
      },
      &*config.x.begin(),
      &*config.x.end());

  convert_units(
      {
        bind(&convert_unit_typographic, layer->dpi, layer->font_size.value, _1),
        bind(&convert_unit_user, domain_translate_fn(config.scale_y), _1),
        bind(&convert_unit_relative, clip.h, _1)
      },
      &*config.y.begin(),
      &*config.y.end());

  /* draw areas */
  Path path;

    for (auto i : group.index) {
      auto sx = clip.x + config.x[i] * clip.w;
      auto sy = clip.y + (1.0 - config.y[i]) * clip.h;
  for (size_t i = 0; i < config.x.size(); ++i) {
    auto sx = clip.x + config.x[i];
    auto sy = clip.y + clip.h - config.y[i];

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

    for (auto i = group.index.rbegin(); i != group.index.rend(); ++i) {
      auto sx = clip.x + config.x[*i] * clip.w;
      auto sy = clip.y + (1.0 - config.yoffset[*i]) * clip.h;
  for (int i = config.x.size() - 1; i >= 0; --i) {
    auto sx = clip.x;
    auto sy = clip.y + clip.h - config.y[i];
    path.lineTo(sx, sy);
  }

@@ -74,130 +97,134 @@ ReturnCode draw(
  FillStyle style;
  style.color = config.colors.empty()
      ? Color{}
        : config.colors[group.index[0]];
      : config.colors[0];

  fillPath(layer, clip, path, style);
  }

  return OK;
}

ReturnCode build_legend(
    const PlotAreaConfig& config,
    const std::string& title,
    const std::string& legend_key,
    LegendItemMap* legend) {
  LegendItemGroup legend_items;

  for (const auto& g : config.groups) {
    LegendItem li;
    li.title = g.key.empty() ? title : g.key;
    li.color = config.colors.empty()
ReturnCode draw_vertical(
    PlotAreaConfig config,
    const LayoutInfo& layout,
    Layer* layer) {
  const auto& clip = layout.content_box;

  /* convert units */
  convert_units(
      {
        bind(&convert_unit_typographic, layer->dpi, layer->font_size.value, _1),
        bind(&convert_unit_user, domain_translate_fn(config.scale_x), _1),
        bind(&convert_unit_relative, clip.w, _1)
      },
      &*config.x.begin(),
      &*config.x.end());

  convert_units(
      {
        bind(&convert_unit_typographic, layer->dpi, layer->font_size.value, _1),
        bind(&convert_unit_user, domain_translate_fn(config.scale_y), _1),
        bind(&convert_unit_relative, clip.h, _1)
      },
      &*config.y.begin(),
      &*config.y.end());

  /* draw areas */
  Path path;

  for (size_t i = 0; i < config.x.size(); ++i) {
    auto sx = clip.x + config.x[i];
    auto sy = clip.y + clip.h - config.y[i];

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

  for (int i = config.x.size() - 1; i >= 0; --i) {
    auto sx = clip.x + config.x[i];
    auto sy = clip.y + clip.h;
    path.lineTo(sx, sy);
  }

  path.closePath();

  FillStyle style;
  style.color = config.colors.empty()
      ? Color{}
        : config.colors[g.index[0] % config.colors.size()];
      : config.colors[0];

  fillPath(layer, clip, path, style);

    legend_items.items.emplace_back(li);
  return OK;
}

  legend_items_add(legend_key, legend_items, legend);
ReturnCode layout(
    const PlotAreaConfig& config,
    const Layer& layer,
    LayoutInfo* layout) {
  /* nothing to do */
  return OK;
}

ReturnCode draw(
    const PlotAreaConfig& config,
    const LayoutInfo& layout,
    Layer* layer) {
  switch (config.direction) {
    case Direction::HORIZONTAL:
      return draw_horizontal(config, layout, layer);
    case Direction::VERTICAL:
      return draw_vertical(config, layout, layer);
    default:
      return ERROR;
  }
}

ReturnCode configure(
    const plist::PropertyList& plist,
    const DataContext& data,
    const Document& doc,
    const DomainMap& scales,
    LegendItemMap* legend,
    const Environment& env,
    PlotAreaConfig* config) {
  SeriesRef data_x = find_maybe(data.defaults, "x");
  SeriesRef data_y = find_maybe(data.defaults, "y");
  SeriesRef data_yoffset;
  SeriesRef data_group = find_maybe(data.defaults, "group");

  std::string scale_x = SCALE_DEFAULT_X;
  std::string scale_y = SCALE_DEFAULT_Y;

  std::string title;

  std::string legend_key = LEGEND_DEFAULT;

  std::optional<Color> color;
  SeriesRef colors = find_maybe(data.defaults, "colors");
  DomainConfig color_domain;
  ColorScheme color_palette;
  /* set defaults from environment */
  config->scale_x = env.scale_x;
  config->scale_y = env.scale_y;

  /* parse properties */
  static const ParserDefinitions pdefs = {
    {"x", configure_series_fn(data, &data_x)},
    {"scale-x", bind(&configure_string, _1, &scale_x)},
    {"y", configure_series_fn(data, &data_y)},
    {"y-offset", configure_series_fn(data, &data_yoffset)},
    {"scale-y", bind(&configure_string, _1, &scale_y)},
    {"group", configure_series_fn(data, &data_group)},
    {"title", bind(&configure_string, _1, &title)},
    {"color", configure_color_opt(&color)},
    {"colors", configure_series_fn(data, &colors)},
    {"xs", bind(&configure_measures, _1, &config->x)},
    {"ys", bind(&configure_measures, _1, &config->y)},
    {"scale-x", bind(&domain_configure, _1, &config->scale_x)},
    {"scale-x-min", bind(&configure_float_opt, _1, &config->scale_x.min)},
    {"scale-x-max", bind(&configure_float_opt, _1, &config->scale_x.max)},
    {"scale-x-padding", bind(&configure_float, _1, &config->scale_x.padding)},
    {"scale-y", bind(&domain_configure, _1, &config->scale_y)},
    {"scale-y-min", bind(&configure_float_opt, _1, &config->scale_y.min)},
    {"scale-y-max", bind(&configure_float_opt, _1, &config->scale_y.max)},
    {"scale-y-padding", bind(&configure_float, _1, &config->scale_y.padding)},
    {"direction", bind(&configure_direction, _1, &config->direction)},
    {"color", configure_vec<Color>(bind(&configure_color, _1, _2), &config->colors)},
    {"colors", configure_vec<Color>(bind(&configure_color, _1, _2), &config->colors)},
  };

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

  /* check dataset */
  if (!data_x || !data_y) {
    return ReturnCode::error("EARG", "the following properties are required: x, y");
  }

  if ((data_x->size() != data_y->size()) ||
      (data_yoffset && data_x->size() != data_yoffset->size()) ||
      (data_group && data_x->size() != data_group->size())) {
    return ReturnCode::error(
        "EARG",
        "the length of the 'x', 'y', 'x-offset', 'y-offset' and 'group' properties must be equal");
  /* scale autoconfig */
  for (const auto& v : config->x) {
    if (v.unit == Unit::USER) {
      domain_fit(v.value, &config->scale_x);
    }

  /* fetch domains */
  auto domain_x = find_ptr(scales, scale_x);
  if (!domain_x) {
    return ReturnCode::errorf("EARG", "scale not found: $0", scale_x);
  }

  auto domain_y = find_ptr(scales, scale_y);
  if (!domain_y) {
    return ReturnCode::errorf("EARG", "scale not found: $0", scale_y);
  for (const auto& v : config->y) {
    if (v.unit == Unit::USER) {
      domain_fit(v.value, &config->scale_y);
    }

  /* group data */
  if (data_group) {
    if (data_x->size() != data_group->size()) {
      return ERROR;
    }

    config->groups = plotfx::series_group(*data_group);
  } else {
    DataGroup g;
    g.index = std::vector<size_t>(data_x->size());
    std::iota(g.index.begin(), g.index.end(), 0);
    config->groups.emplace_back(g);
  }

  /* setup config */
  //config->x = domain_translate(*domain_x, *data_x);
  //config->y = domain_translate(*domain_y, *data_y);
  //config->yoffset = domain_translate(
  //    *domain_y,
  //    data_yoffset
  //        ? *data_yoffset
  //        : std::vector<Value>(data_y->size(), "0.0"));

  config->colors = fallback(
      color,
      series_to_colors(colors, color_domain, color_palette),
      groups_to_colors(data_x->size(), config->groups, color_palette));

  /* build legend items */
  if (auto rc = build_legend(*config, title, legend_key, legend); !rc) {
    return rc;
  }

  return OK;
+15 −7
Original line number Diff line number Diff line
@@ -46,24 +46,32 @@ namespace plot {
namespace area {

struct PlotAreaConfig {
  std::vector<double> x;
  std::vector<double> y;
  std::vector<double> yoffset;
  std::vector<DataGroup> groups;
  PlotAreaConfig();
  Direction direction;
  std::vector<Measure> x;
  std::vector<Measure> xoffset;
  std::vector<Measure> y;
  std::vector<Measure> yoffset;
  DomainConfig scale_x;
  DomainConfig scale_y;
  std::vector<Color> colors;
};

ReturnCode draw(
    const PlotAreaConfig& config,
    const Rectangle& clip,
    const LayoutInfo& layout,
    Layer* layer);

ReturnCode layout(
    const PlotAreaConfig& config,
    const Layer& layer,
    LayoutInfo* layout);

ReturnCode configure(
    const plist::PropertyList& plist,
    const DataContext& data,
    const Document& doc,
    const DomainMap& scales,
    LegendItemMap* legend,
    const Environment& env,
    PlotAreaConfig* config);

} // namespace area
+0 −3
Original line number Diff line number Diff line
@@ -37,9 +37,6 @@
#include "source/config_helpers.h"
#include "source/element_factory.h"
#include "source/utils/algo.h"
#include "plot_area.h"
#include "plot_labels.h"
#include "legend.h"

using namespace std::placeholders;
using std::ref;
+1 −1
Original line number Diff line number Diff line
@@ -129,7 +129,7 @@ int main(int argc, const char** argv) {

  if (!plotfx_render_file(ctx, flag_out.c_str(), fmt.c_str())) {
    std::cerr
        << "ERROR: error while rendering"
        << "ERROR: error while rendering: "
        << plotfx_geterror(ctx)
        << std::endl;
    return EXIT_FAILURE;
Loading