HIPO  4.3.0
High Performance Output data format for experimental physics
ascii.h
Go to the documentation of this file.
1 #ifndef INCLUDE_ASCII_ASCII_H_
2 #define INCLUDE_ASCII_ASCII_H_
3 
4 #include <cmath>
5 #include <codecvt>
6 #include <iomanip>
7 #include <map>
8 #include <sstream>
9 #include <string>
10 #include <unordered_map>
11 #include <vector>
12 
13 #include "color.h"
14 #include "constants.h"
15 #include "text.h"
16 
17 namespace ascii {
19 
35 class Asciichart {
36 public:
38  enum Type {
39  LINE = 0,
40  CIRCLE = 1
41  };
42 
43 public:
48  explicit Asciichart(const std::vector<double> &series)
49  : type_(LINE), height_(kDoubleNotANumber), min_(kDoubleInfinity),
50  max_(kDoubleNegInfinity), offset_(3), legend_padding_(10),
51  basic_width_of_label_(0), show_legend_(false) {
52  InitSeries(series);
53  InitStyles();
54  InitSymbols();
55  }
56 
61  explicit Asciichart(const std::vector<std::vector<double>> &series)
62  : type_(LINE), height_(kDoubleNotANumber), min_(kDoubleInfinity),
63  max_(kDoubleNegInfinity), offset_(3), legend_padding_(10),
64  basic_width_of_label_(0), show_legend_(false) {
65  InitSeries(series);
66  InitStyles();
67  InitSymbols();
68  }
69 
74  explicit Asciichart(
75  const std::unordered_map<std::string, std::vector<double>> &series)
76  : type_(LINE), height_(kDoubleNotANumber), min_(kDoubleInfinity),
77  max_(kDoubleNegInfinity), offset_(3), legend_padding_(10),
78  basic_width_of_label_(0), show_legend_(false) {
79  InitSeries(series);
80  InitStyles();
81  InitSymbols();
82  }
83 
90  type_ = type;
91  return *this;
92  }
93 
103  height_ = height;
104  return *this;
105  }
106 
112  Asciichart &styles(const std::vector<Style> &styles) {
113  styles_ = styles;
114  return *this;
115  }
116 
125  Asciichart &min(double min) {
126  min_ = min;
127  return *this;
128  }
129 
138  Asciichart &max(double max) {
139  max_ = max;
140  return *this;
141  }
142 
152  offset_ = offset;
153  return *this;
154  }
155 
161  Asciichart &legend_padding(size_t padding) {
162  legend_padding_ = padding;
163  return *this;
164  }
165 
171  Asciichart &show_legend(bool show) {
172  show_legend_ = show;
173  return *this;
174  }
175 
184  Asciichart &symbols(const std::map<std::string, std::string> &symbols) {
185  symbols_ = symbols;
186  return *this;
187  }
188 
193  std::string Plot() {
194  switch (type_) {
195  case LINE:
196  return PlotLineChart();
197  break;
198  case CIRCLE:
199  return PlotCircleChart();
200  break;
201  default:
202  return "";
203  }
204  }
205 
206 private:
207  std::map<std::string, std::string> symbols_;
208  std::unordered_map<std::string, std::vector<double>> series_;
209  std::vector<Style> styles_;
210 
211  Type type_;
212  double height_;
213  double min_;
214  double max_;
215  double offset_;
216  size_t legend_padding_;
217  size_t basic_width_of_label_;
218 
219  bool show_legend_;
220 
223  void InitSeries(const std::vector<double> &series) {
224  series_["series 0"] = series;
225  }
226 
229  void InitSeries(const std::vector<std::vector<double>> &series) {
230  unsigned n = 0;
231  for (const auto &s : series) {
232  series_["series " + std::to_string(n++)] = s;
233  }
234  }
235 
238  void InitSeries(
239  const std::unordered_map<std::string, std::vector<double>> &series) {
240  series_ = series;
241  }
242 
244  void InitStyles() {
245  styles_ = {
246  Style().fg(Foreground::From(Color::RED)),
247  Style().fg(Foreground::From(Color::CYAN)),
248  Style().fg(Foreground::From(Color::MAGENTA)),
249  Style().fg(Foreground::From(Color::YELLOW)),
250  Style().fg(Foreground::From(Color::WHITE)),
252  };
253  }
254 
256  void InitSymbols() {
257  switch (type_) {
258  case LINE:
259  symbols_ = {{"empty", " "}, {"center", "┼"}, {"axis", "┤"},
260  {"c1", "╶"}, {"c2", "╴"}, {"parellel", "─"},
261  {"down", "╰"}, {"up", "╭"}, {"ldown", "╮"},
262  {"lup", "╯"}, {"vertical", "│"}};
263  case CIRCLE:
264  break;
265  default:
266  break;
267  }
268  }
269 
277  void PutString(std::vector<std::vector<Text>> &screen, const std::string &str,
278  const Style &style, unsigned row, unsigned col) {
279  for (unsigned i = 0; i < str.length(); i++) {
280  if (str[i] == '\n') {
281  row += 1;
282  } else {
283  screen[row][col + i] = Text(str.substr(i, 1), style);
284  }
285  }
286  }
287 
291  std::string FormatLabel(int x) {
292  std::stringstream ss;
293  ss << std::setw(show_legend_ ? legend_padding_ + basic_width_of_label_
294  : basic_width_of_label_)
295  << std::setfill(' ') << std::setprecision(2);
296  ss << x;
297  return ss.str();
298  }
299 
303  std::string Print(const std::vector<std::vector<Text>> &screen) {
304  std::stringstream os;
305  for (auto &line : screen) {
306  for (auto &item : line) {
307  os << item;
308  }
309  os << "\n";
310  }
311  return os.str();
312  }
313 
317  std::string PlotLineChart() {
318  // 1. calculate min and max
319  for (auto &label_trace_pair : series_) {
320  for (auto &item : label_trace_pair.second) {
321  min_ = std::min(item, min_);
322  max_ = std::max(item, max_);
323  }
324  }
325 
326  // 2. calaculate range
327  auto range = max_ - min_;
328  if (range == 0) range = 1;
329 
330  // make basic padding as size of str(max)
331  basic_width_of_label_ = std::max(std::to_string((int)max_).length(),
332  std::to_string((int)min_).length());
333 
334  // 3. width and height
335  int width = 0;
336  for (auto &label_trace_pair : series_) {
337  width = std::max(width, (int)label_trace_pair.second.size());
338  }
339 
340  int legend_cols = 0, legend_rows = 0;
341  if (show_legend_) {
342  // determine width and height of legend, add to offset.
343  for (auto &label_trace_pair : series_) {
344  legend_rows++;
345  legend_cols =
346  std::max(legend_cols, (int)label_trace_pair.first.length());
347  }
348  }
349 
350  auto offset = offset_ + legend_cols;
351 
352  width += offset;
353 
354  if (std::isnan(height_)) {
355  height_ = range;
356  }
357 
358  // extend the height of the plot if we need more rows to display the
359  // legend than what the range of the data requires.
360  height_ = std::max((double)legend_rows, height_);
361 
362  // calculate ratio using height and range
363  auto ratio = height_ / range;
364 
365  int min2 = std::round(min_ * ratio);
366  int max2 = std::round(max_ * ratio);
367 
368  // 4. rows and cols of this chart
369  auto rows = max2 - min2;
370  auto cols = width;
371 
372  if (rows == 0) rows = 1;
373 
374  // 5. initialize chart using empty str
375  std::vector<std::vector<Text>> screen(
376  rows + 1, std::vector<Text>(cols, symbols_["empty"]));
377 
378  // 6. axis + labels
379  for (double y = min2; y <= max2; y++) {
380  auto label = FormatLabel(std::round(min_ + (y - min2) * range / rows));
381  // vertical reverse
382  screen[rows - (y - min2)][legend_cols] =
383  // -commented out by Gagik Text(label, Style().fg(Foreground::From(Color::BLUE)));
384  Text(label, Style().fg(Foreground::From(Color::CYAN)));
385  screen[rows - (y - min2)][offset - 1] =
386  Text((y == 0) ? symbols_["center"] : symbols_["axis"],
387  Style().fg(Foreground::From(Color::CYAN)));
388  }
389 
390  if (show_legend_) {
391  // 7. Legend
392  {
393  unsigned j = 0;
394  for (auto &label_trace_pair : series_) {
395  auto style = styles_[j % styles_.size()];
396  PutString(screen, label_trace_pair.first, style, j++, 0);
397  }
398  }
399  }
400 
401  // 8. Content
402  {
403  unsigned j = 0;
404  for (auto &label_trace_pair : series_) {
405  auto &trace = label_trace_pair.second;
406  auto style = styles_[j++ % styles_.size()];
407  auto y0 = std::round(trace[0] * ratio) - min2;
408  // vertical reverse
409  screen[rows - y0][offset - 1] = Text(symbols_["center"], style);
410 
411  for (size_t i = 0; i < trace.size() - 1; i++) {
412  auto y0 = std::round(trace[i] * ratio) - min2;
413  auto y1 = std::round(trace[i + 1] * ratio) - min2;
414 
415  if (y0 == y1) {
416  screen[rows - y0][i + offset] = Text(symbols_["parellel"], style);
417  } else {
418  screen[rows - y1][i + offset] =
419  Text(y0 > y1 ? symbols_["down"] : symbols_["up"], style);
420  screen[rows - y0][i + offset] =
421  Text(y0 > y1 ? symbols_["ldown"] : symbols_["lup"], style);
422  auto from = std::min(y0, y1);
423  auto to = std::max(y0, y1);
424  for (size_t y = from + 1; y < to; y++) {
425  screen[rows - y][i + offset] = Text(symbols_["vertical"], style);
426  }
427  }
428  }
429  }
430  }
431 
432  return Print(screen);
433  }
434 
437  std::string PlotCircleChart() { return ""; }
438 };
439 } // namespace ascii
440 #endif
ASCII chart renderer for plotting data series in the terminal.
Definition: ascii.h:35
Type
Chart rendering mode.
Definition: ascii.h:38
@ CIRCLE
Circle/point chart mode.
Definition: ascii.h:40
@ LINE
Line chart mode.
Definition: ascii.h:39
Asciichart & legend_padding(size_t padding)
Set the padding between series labels and the chart.
Definition: ascii.h:161
Asciichart & type(Type type)
Set the chart rendering type.
Definition: ascii.h:89
Asciichart & height(double height)
Set the height of the chart in data units.
Definition: ascii.h:102
Asciichart(const std::unordered_map< std::string, std::vector< double >> &series)
Construct an Asciichart from named data series (label -> values map).
Definition: ascii.h:74
Asciichart & min(double min)
Set the minimum value displayed on the y-axis.
Definition: ascii.h:125
Asciichart(const std::vector< double > &series)
Construct an Asciichart from a single data series.
Definition: ascii.h:48
Asciichart & max(double max)
Set the maximum value displayed on the y-axis.
Definition: ascii.h:138
std::string Plot()
Generate and return the ASCII chart as a string.
Definition: ascii.h:193
Asciichart & offset(size_t offset)
Set the offset (left margin) for the chart content.
Definition: ascii.h:151
Asciichart & styles(const std::vector< Style > &styles)
Set the styles (colors and decorations) for each data series.
Definition: ascii.h:112
Asciichart & show_legend(bool show)
Enable or disable the legend display.
Definition: ascii.h:171
Asciichart(const std::vector< std::vector< double >> &series)
Construct an Asciichart from multiple unnamed data series.
Definition: ascii.h:61
Asciichart & symbols(const std::map< std::string, std::string > &symbols)
Set custom symbols used to draw the chart.
Definition: ascii.h:184
static Foreground From(T color)
Create a Foreground color from a color value.
Definition: color.h:164
ANSI terminal color and decoration classes for styled output.
Definition: ascii.h:17
constexpr double kDoubleNegInfinity
Double value representing negative infinity.
Definition: constants.h:25
constexpr double kDoubleNotANumber
Double value representing "Not a Number" (NaN).
Definition: constants.h:19
constexpr double kDoubleInfinity
Double value representing positive infinity.
Definition: constants.h:22
Styled text element for terminal output with ANSI color support.