Commit 854c9a63 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

draw plot axes using new, stateful API

parent e04a3f07
Loading
Loading
Loading
Loading
+88 −46
Original line number Diff line number Diff line
@@ -8,60 +8,56 @@
 * <http://www.gnu.org/licenses/>.
 */
#include "axes.h"
#include <assert.h>
#include <elements/context.h>
#include <graphics/text.h>
#include <graphics/brush.h>

namespace signaltk {

AxisDefinition::AxisDefinition() :
    enabled_(false),
    has_ticks_(false),
    has_labels_(false),
    mode(AxisMode::DISABLED),
    label_placement(AxisLabelPlacement::OFF),
    label_padding_horiz_rem(kDefaultLabelPaddingHorizREM),
    label_padding_vert_rem(kDefaultLabelPaddingVertREM),
    tick_length_rem(kDefaultTickLengthREM) {}

void AxisDefinition::addTick(double tick_position) {
  ticks_.push_back(tick_position);
}

const std::vector<double> AxisDefinition::getTicks() const {
  return ticks_;
}

void AxisDefinition::addLabel(
    double label_position,
    const std::string& label_text) {
  has_labels_ = true;
  labels_.emplace_back(label_position, label_text);
}

void AxisDefinition::removeLabels() {
  labels_.clear();
}
Status plot_axis_add(Context* ctx, AxisPosition pos) {
  auto axis_config = std::make_unique<AxisDefinition>();
  axis_config->position = pos;

const std::vector<std::pair<double, std::string>> AxisDefinition::getLabels()
    const {
  return labels_;
}

bool AxisDefinition::hasLabels() const {
  return has_labels_;
  switch (pos) {
    case AxisPosition::LEFT:
      axis_config->label_placement = AxisLabelPlacement::LEFT;
      break;
    case AxisPosition::RIGHT:
      axis_config->label_placement = AxisLabelPlacement::RIGHT;
      break;
    case AxisPosition::TOP:
      axis_config->label_placement = AxisLabelPlacement::TOP;
      break;
    case AxisPosition::BOTTOM:
      axis_config->label_placement = AxisLabelPlacement::BOTTOM;
      break;
  }

void AxisDefinition::setTitle(const std::string& title) {
  title_ = title;
  ctx->plot_config.axes.emplace_back(std::move(axis_config));
  return OK;
}

const std::string& AxisDefinition::getTitle() {
  return title_;
Status plot_axis_addtick(Context* ctx, float offset) {
  auto& axis_config = ctx->plot_config.axes.back();
  axis_config->ticks.emplace_back(offset);
  return OK;
}

bool AxisDefinition::hasTitle() const {
  return title_.length() > 0;
Status plot_axis_addlabel(Context* ctx, float offset, const char* label) {
  auto& axis_config = ctx->plot_config.axes.back();
  axis_config->labels.emplace_back(offset, label);
  return OK;
}

Status renderAxisVertical(
Status plot_render_axis_vertical(
    const AxisDefinition& axis_config,
    double x,
    double y0,
@@ -75,10 +71,10 @@ Status renderAxisVertical(

  double label_placement = 0;
  switch (axis_config.label_placement) {
    case AxisDefinition::LABELS_RIGHT:
    case AxisLabelPlacement::RIGHT:
      label_placement = 1;
      break;
    case AxisDefinition::LABELS_LEFT:
    case AxisLabelPlacement::LEFT:
      label_placement = -1;
      break;
    default:
@@ -86,7 +82,7 @@ Status renderAxisVertical(
  }

  /* draw ticks */
  for (const auto& tick : axis_config.getTicks()) {
  for (const auto& tick : axis_config.ticks) {
    auto y = y0 + (y1 - y0) * tick;
    StrokeStyle style;
    strokeLine(
@@ -100,7 +96,7 @@ Status renderAxisVertical(

  /* draw labels */
  auto label_padding = from_rem(*target, axis_config.label_padding_horiz_rem);
  for (const auto& label : axis_config.getLabels()) {
  for (const auto& label : axis_config.labels) {
    auto [ tick, label_text ] = label;
    auto sy = y0 + (y1 - y0) * tick;
    auto sx = x + label_padding * label_placement;
@@ -116,7 +112,7 @@ Status renderAxisVertical(
  return OK;
}

Status renderAxisHorizontal(
Status plot_render_axis_horizontal(
    const AxisDefinition& axis_config,
    double y,
    double x0,
@@ -130,10 +126,10 @@ Status renderAxisHorizontal(

  double label_placement = 0;
  switch (axis_config.label_placement) {
    case AxisDefinition::LABELS_BOTTOM:
    case AxisLabelPlacement::BOTTOM:
      label_placement = 1;
      break;
    case AxisDefinition::LABELS_TOP:
    case AxisLabelPlacement::TOP:
      label_placement = -1;
      break;
    default:
@@ -141,7 +137,7 @@ Status renderAxisHorizontal(
  }

  /* draw ticks */
  for (const auto& tick : axis_config.getTicks()) {
  for (const auto& tick : axis_config.ticks) {
    auto x = x0 + (x1 - x0) * tick;
    StrokeStyle style;
    strokeLine(
@@ -155,7 +151,7 @@ Status renderAxisHorizontal(

  /* draw labels */
  auto label_padding = from_rem(*target, axis_config.label_padding_vert_rem);
  for (const auto& label : axis_config.getLabels()) {
  for (const auto& label : axis_config.labels) {
    auto [ tick, label_text ] = label;
    auto sx = x0 + (x1 - x0) * tick;
    auto sy = y + label_padding * label_placement;
@@ -163,7 +159,7 @@ Status renderAxisHorizontal(
    TextStyle style;
    style.halign = TextHAlign::CENTER;
    style.valign = label_placement > 0 ? TextVAlign::TOP : TextVAlign::BOTTOM;
    if (auto rc = drawText(label_text, sx, sy, style, target); rc != OK) {
    if (auto rc = drawText(label_text, sx, sy, style, target); rc) {
      return rc;
    }
  }
@@ -171,4 +167,50 @@ Status renderAxisHorizontal(
  return OK;
}

Status plot_render_axis(Context* ctx, int i) {
  assert(i < ctx->plot_config.axes.size());

  int padding = 80;
  const auto& axis = ctx->plot_config.axes[i];

  Status rc;
  switch (axis->position) {
    case AxisPosition::LEFT:
      rc = plot_render_axis_vertical(
          *axis,
          padding,
          padding,
          ctx->frame.height - padding,
          &ctx->frame);
      break;
    case AxisPosition::RIGHT:
      rc = plot_render_axis_vertical(
          *axis,
          ctx->frame.width - padding,
          padding,
          ctx->frame.height - padding,
          &ctx->frame);
      break;
    case AxisPosition::TOP:
      rc = plot_render_axis_horizontal(
          *axis,
          padding,
          padding,
          ctx->frame.width - padding,
          &ctx->frame);
      break;
    case AxisPosition::BOTTOM:
      rc = plot_render_axis_horizontal(
          *axis,
          ctx->frame.height - padding,
          padding,
          ctx->frame.width - padding,
          &ctx->frame);
      break;
  }

  return rc;
}


}
+35 −121
Original line number Diff line number Diff line
@@ -20,136 +20,50 @@ static const double kDefaultLabelPaddingVertREM = 0.8f;
static const double kDefaultLabelPaddingHorizREM = 1.0f;
static const double kDefaultTickLengthREM = 0.4f;

class AxisDefinition {
public:

  /**
   * The axis tick position
   */
  enum kLabelPlacement {
    LABELS_LEFT,
    LABELS_RIGHT,
    LABELS_TOP,
    LABELS_BOTTOM,
    LABELS_OFF
enum class AxisPosition {
  TOP,
  RIGHT,
  BOTTOM,
  LEFT,
  CENTER_HORIZ,
  CENTER_VERT
};

  /**
   * Create a new axis definition
   *
   * @param axis_position the position of the axis ({TOP,RIGHT,BOTTOM,LEFT})
   */
  AxisDefinition();

  /**
   * Add a "tick" to this axis
   *
   * @param tick_position the position of the tick (0.0-1.0)
   */
  void addTick(double tick_position);

  /**
   * Returns the ticks of this axis
   */
  const std::vector<double> getTicks() const;

  /**
   * Add a label to this axis
   *
   * @param label_position the position of the label (0.0-1.0)
   * @param label_text the label text
   */
  void addLabel(double label_position, const std::string& label_text);

  /**
   * Removes the labels from this axis
   */
  void removeLabels();

  /**
   * Returns the labels of this axis
   */
  const std::vector<std::pair<double, std::string>> getLabels() const;

  /**
   * Returns true if this axis has labels, false otherwise
   */
  bool hasLabels() const;

  /**
   * Set the label position for this axis
   */
  void setLabelPlacement(kLabelPlacement pos);

  /**
   * Return the label position for this axis
   */
  kLabelPlacement getLabelPlacement() const;

  /**
   * Set the label rotation for this axis
   */
  void setLabelRotation(double deg);

  /**
   * Return the label rotaitoj for this axis
   */
  double getLabelRotation() const;

  /**
   * Set the title for this axis
   */
  void setTitle(const std::string& title);

  /**
   * Get the title for this axis
   */
  const std::string& getTitle();
enum class AxisMode {
  DISABLED,
  AUTO,
  MANUAL
};

  /**
   * Returns true if the title of this axis is a string with len > 0 and false
   * otherwise
   */
  bool hasTitle() const;
enum class AxisLabelPlacement {
  LEFT,
  RIGHT,
  TOP,
  BOTTOM,
  OFF
};

  bool enabled_;
  std::string title_;
  std::vector<double> ticks_;
  bool has_ticks_;
  std::vector<std::pair<double, std::string>> labels_;
  bool has_labels_;
  kLabelPlacement label_placement;
struct AxisDefinition {
  AxisDefinition();
  AxisPosition position;
  AxisMode mode;
  std::string title;
  std::vector<double> ticks;
  std::vector<std::pair<double, std::string>> labels;
  AxisLabelPlacement label_placement;
  double label_padding_horiz_rem;
  double label_padding_vert_rem;
  double tick_length_rem;
};

struct AxisDefinitions {
  AxisDefinition top;
  AxisDefinition right;
  AxisDefinition bottom;
  AxisDefinition left;
};
Status plot_add(Context* ctx);
Status plot_axis_add(Context* ctx, AxisPosition pos);
Status plot_axis_addtick(Context* ctx, float offset);
Status plot_axis_addlabel(Context* ctx, float offset, const char* label);
Status plot_axis_setauto(Context* ctx);
Status plot_axis_setmanual(Context* ctx);

/**
 * Render a vertical axis
 */
Status renderAxisVertical(
    const AxisDefinition& axis_config,
    double x,
    double y0,
    double y1,
    Layer* target);

/**
 * Render a horizontal axis
 */
Status renderAxisHorizontal(
    const AxisDefinition& axis_config,
    double y,
    double x0,
    double x1,
    Layer* target);
Status plot_render_axis(Context* ctx, int idx);

} // namespace signaltk
+2 −4
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include "signaltk.h"
#include "axes.h"
#include "plot_domain.h"
#include <memory>

namespace signaltk {

@@ -21,10 +22,7 @@ struct PlotConfig {
  PlotDomain y_domain;
  double y_min;
  double y_max;
  AxisDefinition axis_top;
  AxisDefinition axis_right;
  AxisDefinition axis_bottom;
  AxisDefinition axis_left;
  std::vector<std::unique_ptr<AxisDefinition>> axes;
};

} // namespace signaltk
+3 −21
Original line number Diff line number Diff line
@@ -14,27 +14,9 @@
namespace signaltk {

Status plot_render(Context* ctx) {
  double padding = 80;

  // draw left axis
  {
    AxisDefinition axis;
    axis.label_placement = AxisDefinition::LABELS_LEFT;
    axis.addTick(0.0);
    axis.addTick(0.2);
    axis.addTick(0.4);
    axis.addTick(0.6);
    axis.addTick(0.8);
    axis.addTick(1.0);
    axis.addLabel(0.0, "x");
    axis.addLabel(0.2, "x");
    axis.addLabel(0.4, "x");
    axis.addLabel(0.6, "x");
    axis.addLabel(0.8, "x");
    axis.addLabel(1.0, "x");
    auto rc = renderAxisVertical(axis, padding, padding, ctx->frame.height - padding, &ctx->frame);

    if (!rc) {
  // draw axes
  for (size_t i = 0; i < ctx->plot_config.axes.size(); ++i) {
    if (auto rc = plot_render_axis(ctx, i); rc) {
      return rc;
    }
  }
+1 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ namespace signaltk {
struct Context;

enum Status : int {
  OK,
  OK = 0,
  ERROR,
  ERROR_IO,
  ERROR_NOT_IMPLEMENTED,
Loading