1 #ifndef INCLUDE_ASCII_ASCII_H_
2 #define INCLUDE_ASCII_ASCII_H_
10 #include <unordered_map>
51 basic_width_of_label_(0), show_legend_(false) {
61 explicit Asciichart(
const std::vector<std::vector<double>> &series)
64 basic_width_of_label_(0), show_legend_(false) {
75 const std::unordered_map<std::string, std::vector<double>> &series)
78 basic_width_of_label_(0), show_legend_(false) {
162 legend_padding_ = padding;
196 return PlotLineChart();
199 return PlotCircleChart();
207 std::map<std::string, std::string> symbols_;
208 std::unordered_map<std::string, std::vector<double>> series_;
209 std::vector<Style> styles_;
216 size_t legend_padding_;
217 size_t basic_width_of_label_;
223 void InitSeries(
const std::vector<double> &series) {
224 series_[
"series 0"] = series;
229 void InitSeries(
const std::vector<std::vector<double>> &series) {
231 for (
const auto &s : series) {
232 series_[
"series " + std::to_string(n++)] = s;
239 const std::unordered_map<std::string, std::vector<double>> &series) {
259 symbols_ = {{
"empty",
" "}, {
"center",
"┼"}, {
"axis",
"┤"},
260 {
"c1",
"╶"}, {
"c2",
"╴"}, {
"parellel",
"─"},
261 {
"down",
"╰"}, {
"up",
"╭"}, {
"ldown",
"╮"},
262 {
"lup",
"╯"}, {
"vertical",
"│"}};
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') {
283 screen[row][col + i] = Text(str.substr(i, 1), style);
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);
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) {
317 std::string PlotLineChart() {
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_);
327 auto range = max_ - min_;
328 if (range == 0) range = 1;
331 basic_width_of_label_ = std::max(std::to_string((
int)max_).length(),
332 std::to_string((
int)min_).length());
336 for (
auto &label_trace_pair : series_) {
337 width = std::max(width, (
int)label_trace_pair.second.size());
340 int legend_cols = 0, legend_rows = 0;
343 for (
auto &label_trace_pair : series_) {
346 std::max(legend_cols, (
int)label_trace_pair.first.length());
350 auto offset = offset_ + legend_cols;
354 if (std::isnan(height_)) {
360 height_ = std::max((
double)legend_rows, height_);
363 auto ratio = height_ / range;
365 int min2 = std::round(min_ * ratio);
366 int max2 = std::round(max_ * ratio);
369 auto rows = max2 - min2;
372 if (rows == 0) rows = 1;
375 std::vector<std::vector<Text>> screen(
376 rows + 1, std::vector<Text>(cols, symbols_[
"empty"]));
379 for (
double y = min2; y <= max2; y++) {
380 auto label = FormatLabel(std::round(min_ + (y - min2) * range / rows));
382 screen[rows - (y - min2)][legend_cols] =
385 screen[rows - (y - min2)][
offset - 1] =
386 Text((y == 0) ? symbols_[
"center"] : symbols_[
"axis"],
394 for (
auto &label_trace_pair : series_) {
395 auto style = styles_[j % styles_.size()];
396 PutString(screen, label_trace_pair.first, style, 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;
409 screen[rows - y0][
offset - 1] = Text(symbols_[
"center"], style);
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;
416 screen[rows - y0][i +
offset] = Text(symbols_[
"parellel"], style);
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);
432 return Print(screen);
437 std::string PlotCircleChart() {
return ""; }
ASCII chart renderer for plotting data series in the terminal.
Type
Chart rendering mode.
@ CIRCLE
Circle/point chart mode.
Asciichart & legend_padding(size_t padding)
Set the padding between series labels and the chart.
Asciichart & type(Type type)
Set the chart rendering type.
Asciichart & height(double height)
Set the height of the chart in data units.
Asciichart(const std::unordered_map< std::string, std::vector< double >> &series)
Construct an Asciichart from named data series (label -> values map).
Asciichart & min(double min)
Set the minimum value displayed on the y-axis.
Asciichart(const std::vector< double > &series)
Construct an Asciichart from a single data series.
Asciichart & max(double max)
Set the maximum value displayed on the y-axis.
std::string Plot()
Generate and return the ASCII chart as a string.
Asciichart & offset(size_t offset)
Set the offset (left margin) for the chart content.
Asciichart & styles(const std::vector< Style > &styles)
Set the styles (colors and decorations) for each data series.
Asciichart & show_legend(bool show)
Enable or disable the legend display.
Asciichart(const std::vector< std::vector< double >> &series)
Construct an Asciichart from multiple unnamed data series.
Asciichart & symbols(const std::map< std::string, std::string > &symbols)
Set custom symbols used to draw the chart.
static Foreground From(T color)
Create a Foreground color from a color value.
ANSI terminal color and decoration classes for styled output.
constexpr double kDoubleNegInfinity
Double value representing negative infinity.
constexpr double kDoubleNotANumber
Double value representing "Not a Number" (NaN).
constexpr double kDoubleInfinity
Double value representing positive infinity.
Styled text element for terminal output with ANSI color support.