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

update 'legend' element

parent bd645c6c
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ points {
  colors: #80699b;
}


axis {
  position: left;
  layout: linear(5);
@@ -65,3 +64,10 @@ axis {
  layout: linear(1, align 1);
  labels: csv('tests/testdata/city_temperatures_pivot.csv', month_name);
}

legend {
  position: bottom left inside;
  items: "New York", "Beijing", "Moscow", "Berlin";
  colors: #4572a7, #aa4643, #89a54e, #80699b;
}
+8 −0
Original line number Diff line number Diff line
@@ -102,4 +102,12 @@
  <text x="930.999366" y="456.000000" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">Oct</text>
  <text x="1020.745991" y="456.000000" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">Nov</text>
  <text x="1112.125428" y="456.000000" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">Dec</text>
  <path fill="#4572a7" d="M107.870833 399.466667 M97.870833 399.466667 a5.0 5.0 0 1 0 10.0 0 a5.0 5.0 0 1 0 -10.0 0 "/>
  <text x="114.370833" y="404.466667" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">New York</text>
  <path fill="#aa4643" d="M219.055208 399.466667 M209.055208 399.466667 a5.0 5.0 0 1 0 10.0 0 a5.0 5.0 0 1 0 -10.0 0 "/>
  <text x="225.555208" y="404.466667" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">Beijing</text>
  <path fill="#89a54e" d="M312.302083 399.466667 M302.302083 399.466667 a5.0 5.0 0 1 0 10.0 0 a5.0 5.0 0 1 0 -10.0 0 "/>
  <text x="318.802083" y="404.466667" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">Moscow</text>
  <path fill="#80699b" d="M415.314583 399.466667 M405.314583 399.466667 a5.0 5.0 0 1 0 10.0 0 a5.0 5.0 0 1 0 -10.0 0 "/>
  <text x="421.814583" y="404.466667" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">Berlin</text>
</svg>
 No newline at end of file
+8 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include "elements/axis.h"
#include "elements/bars.h"
#include "elements/gridlines.h"
#include "elements/legend.h"
#include "elements/lines.h"
#include "elements/points.h"

@@ -70,6 +71,13 @@ static std::unordered_map<std::string, ElementBuilder> elems = {
        &gridlines::layout,
        &gridlines::draw)
  },
  {
    "legend",
    elem_builder<LegendConfig>(
        &legend::configure,
        &legend::layout,
        &legend::draw)
  },
  {
    "lines",
    elem_builder<plot::lines::PlotLinesConfig>(
+141 −173
Original line number Diff line number Diff line
@@ -51,16 +51,9 @@ LegendConfig::LegendConfig() :
    position_horiz(HAlign::LEFT),
    position_vert(VAlign::TOP) {}

void legend_items_add(
    const std::string& key,
    LegendItemGroup items,
    LegendItemMap* map) {
  (*map)[key].emplace_back(std::move(items));
}

ReturnCode legend_layout_items(
    const LegendConfig& legend,
    const LegendItemList& legend_items,
    const std::vector<std::string>& legend_items,
    Point origin,
    std::function<ReturnCode (size_t idx, Point p)> on_label,
    const Layer* layer,
@@ -70,15 +63,14 @@ ReturnCode legend_layout_items(
      legend.item_margins[1],
      from_em(kDefaultItemPaddingHorizEM, font_size));

  double point_size = 5; // FIXME
  double line_height = font_size; // FIXME
  double point_size = 5; // TODO
  double line_height = font_size; // TODO

  double sx = origin.x;
  double sy = origin.y;

  for (size_t idx = 0; idx < legend_items.size(); ++idx) {
    const auto& e = legend_items[idx];
    const auto& label_text = e.title;
    const auto& label_text = legend_items[idx];

    if (on_label) {
      if (auto rc = on_label(idx, Point(sx, sy + line_height / 2)); !rc) {
@@ -103,7 +95,7 @@ ReturnCode legend_layout_items(
      return rc;
    }

    sx += point_size * 2.8; // FIXME
    sx += point_size * 2.8; // TODO
    sx += label_bbox.w;

    if (idx + 1 < legend_items.size()) {
@@ -146,20 +138,22 @@ ReturnCode legend_draw_inside(
      legend.item_margins[1],
      from_em(kDefaultItemPaddingHorizEM, font_size));

  double point_size = 5; // FIXME
  for (const auto& group : legend.groups) {
  double point_size = 5; // TODO
  auto draw_label = [&] (size_t idx, Point pos) {
      const auto& e = group.items[idx];
      const auto& label_text = e.title;
    const auto& label_text = legend.items[idx];

    const auto& color = legend.colors.empty()
        ? Color{}
        : legend.colors[idx % legend.colors.size()];

    {
      FillStyle style;
        style.color = e.color;
      style.color = color;
      Path path;
      path.moveTo(pos.x + point_size + point_size / 2, pos.y);
      path.arcTo(pos.x + point_size / 2, pos.y, point_size, 0, M_PI * 2);
      fillPath(layer, path, style);
        pos.x += point_size * 2.8; // FIXME
      pos.x += point_size * 2.8; // TODO
    }

    {
@@ -185,7 +179,7 @@ ReturnCode legend_draw_inside(
  Rectangle content_bbox;
  if (auto rc = legend_layout_items(
      legend,
        group.items,
      legend.items,
      Point(0, 0),
      nullptr,
      layer,
@@ -226,7 +220,7 @@ ReturnCode legend_draw_inside(

  if (auto rc = legend_layout_items(
      legend,
        group.items,
      legend.items,
      origin,
      draw_label,
      layer,
@@ -234,34 +228,6 @@ ReturnCode legend_draw_inside(
      !rc) {
    return rc;
  }
  }

  return OK;
}

ReturnCode legend_draw(
    const LegendConfig& config,
    const Rectangle& bbox,
    Layer* layer) {
  switch (config.placement) {
    case LegendPlacement::INSIDE:
      return legend_draw_inside(config, bbox, layer);
    case LegendPlacement::OUTSIDE:
    case LegendPlacement::OFF:
    default:
      return OK;
  }
}

ReturnCode legend_draw(
    const LegendMap& legends,
    const Rectangle& bbox,
    Layer* layer) {
  for (const auto& e : legends) {
    if (auto rc = legend_draw(e.second, bbox, layer); !rc) {
      return rc;
    }
  }

  return OK;
}
@@ -326,52 +292,79 @@ ReturnCode legend_configure_position(
  return OK;
}

ReturnCode legend_configure(
    const Document& doc,
    const plist::Property& prop,
    const LegendItemMap& item_map,
    LegendMap* map) {
  if (!plist::is_map(prop)) {
    return ERROR;
  }
namespace legend {

  const auto& plist = *prop.next;
ReturnCode draw(
    const LegendConfig& config,
    const LayoutInfo& layout,
    Layer* layer) {
  switch (config.placement) {
    case LegendPlacement::INSIDE:
      return legend_draw_inside(config, layout.content_box, layer);
    case LegendPlacement::OUTSIDE:
    case LegendPlacement::OFF:
    default:
      return ERROR; // TODO: not implemented
  }
}

  LegendConfig config;
  config.font = doc.font_sans;
  config.border_color = doc.border_color;
  config.text_color = doc.text_color;
ReturnCode layout(
    const LegendConfig& config,
    const Layer& layer,
    LayoutInfo* layout) {
  switch (config.placement) {
    case LegendPlacement::INSIDE:
      /* nothing to do */
      return OK;
    case LegendPlacement::OUTSIDE:
    case LegendPlacement::OFF:
    default:
      return ERROR; // TODO: not implemented
  }
}

ReturnCode configure(
    const plist::PropertyList& plist,
    const DataContext& data,
    const Document& doc,
    const Environment& env,
    LegendConfig* config) {
  /* inherit defaults */
  config->font = doc.font_sans;
  config->border_color = doc.border_color;
  config->text_color = doc.text_color;

  /* parse properties */
  static const ParserDefinitions pdefs = {
    {
      "position",
      bind(
          &legend_configure_position,
          _1,
          &config.placement,
          &config.position_horiz,
          &config.position_vert)
          &config->placement,
          &config->position_horiz,
          &config->position_vert)
    },
    {
      "title",
      bind(
          &configure_string,
          _1,
          &config.title)
          &config->title)
      },
    {
      "text-color",
      bind(
          &configure_color,
          _1,
          &config.text_color)
          &config->text_color)
    },
    {
      "border-color",
      bind(
          &configure_color,
          _1,
          &config.border_color)
          &config->border_color)
    },
    {
      "item-margin",
@@ -379,19 +372,19 @@ ReturnCode legend_configure(
          bind(
              &configure_measure,
              _1,
              &config.item_margins[0]),
              &config->item_margins[0]),
          bind(
              &configure_measure,
              _1,
              &config.item_margins[1]),
              &config->item_margins[1]),
          bind(
              &configure_measure,
              _1,
              &config.item_margins[2]),
              &config->item_margins[2]),
          bind(
              &configure_measure,
              _1,
              &config.item_margins[3])
              &config->item_margins[3])
      })
    },
    {
@@ -399,58 +392,32 @@ ReturnCode legend_configure(
      bind(
          &configure_measure,
          _1,
          &config.item_margins[0])
          &config->item_margins[0])
    },
    {
      "item-margin-right",
      bind(
          &configure_measure,
          _1,
          &config.item_margins[1])
          &config->item_margins[1])
    },
    {
      "item-margin-bottom",
      bind(
          &configure_measure,
          _1,
          &config.item_margins[2])
          &config->item_margins[2])
    },
    {
      "item-margin-left",
      bind(
          &configure_measure,
          _1,
          &config.item_margins[3])
    },
  };

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

  if (auto items = find_ptr(item_map, config.key); items) {
    config.groups.insert(config.groups.end(), items->begin(), items->end());
  }

  map->emplace(config.key, config);
  return OK;
}

ReturnCode legend_configure_all(
    const Document& doc,
    const plist::PropertyList& plist,
    const LegendItemMap& items,
    LegendMap* config) {
  static const ParserDefinitions pdefs = {
    {
      "legend",
      bind(
          &legend_configure,
          doc,
          _1,
          items,
          config),
          &config->item_margins[3])
    },
    {"color", configure_vec<Color>(bind(&configure_color, _1, _2), &config->colors)},
    {"colors", configure_vec<Color>(bind(&configure_color, _1, _2), &config->colors)},
    {"items", bind(&configure_strings, _1, &config->items)},
  };

  if (auto rc = parseAll(plist, pdefs); !rc.isSuccess()) {
@@ -460,5 +427,6 @@ ReturnCode legend_configure_all(
  return OK;
}

} // namespace legend
} // namespace plotfx
+94 −0
Original line number Diff line number Diff line
/**
 * This file is part of the "plotfx" project
 *   Copyright (c) 2018 Paul Asmuth
 *   Copyright (c) 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.
 */
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <tuple>
#include "graphics/layer.h"
#include "graphics/layout.h"
#include "source/document.h"

namespace plotfx {

const std::string LEGEND_DEFAULT = "default";

enum class LegendPlacement {
  OFF,
  INSIDE,
  OUTSIDE
};

using LegendItemList = std::vector<std::string>;

struct LegendConfig {
  LegendConfig();
  std::string key;
  Color text_color;
  Color border_color;
  FontInfo font;
  Measure padding_horiz;
  Measure padding_vert;
  Measure padding_item_horiz;
  Measure padding_item_vert;
  Measure margins[4];
  Measure item_margins[4];
  LegendPlacement placement;
  HAlign position_horiz;
  VAlign position_vert;
  std::string title;
  std::vector<std::string> items;
  std::vector<Color> colors;
};

namespace legend {

ReturnCode draw(
    const LegendConfig& config,
    const LayoutInfo& clip,
    Layer* layer);

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

ReturnCode configure(
    const plist::PropertyList& plist,
    const DataContext& data,
    const Document& doc,
    const Environment& env,
    LegendConfig* config);


} // namespace legned
} // namespace plotfx
Loading