1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
// Draw a graph!
// Guaranteed to get you either an 'A' or an 'F'.
//
// Save this file as UTF-8. Then compile with one of:
//
// cl /EHsc /W4 /Ox /std:c++17 /utf-8 a.cpp
// clang++ -Wall -pedantic-errors -O3 -std=c++17 a.cpp -o a
// g++ -Wall -pedantic-errors -O3 -std=c++17 a.cpp
//
// Takes a single, optional argument specifying the maximum number
// of lines (terminated by newline) to output. Defaults to 25.
//
// Takes the values from standard input, as many as you give it,
// assuming the following:
//
// ∀x ∈ [0, 1000)
// 9 + 4|xs| < console_width
//
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// The isatty() function will tell us whether standard input is piped or not.
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#define isatty _isatty
#else
#include <unistd.h>
#endif
int main( int argc, char** argv )
{
// Windows needs a little help...
#ifdef _WIN32
SetConsoleOutputCP( CP_UTF8 );
#endif
// Maximum screen height
int max_lines = (argc == 2) ? std::stoi( argv[1] ) : 25;
// Get the values to be graphed from standard input
std::vector <double> xs;
{
if (isatty( 0 )) std::cout << "xs? "; // Ask the user, if necessary
double x;
while (std::cin >> x)
xs.emplace_back( x );
}
// Calculate the vertical dimensions of the graph,
// fitting it to the available space as necessary.
double domain = *std::max_element( xs.begin(), xs.end() );
double divisor = (domain < (max_lines - 3)) ? 1 : domain / (max_lines - 3);
int nlines = (domain < (max_lines - 3)) ? (int)domain : (max_lines - 3);
// Output Glyphs (this is the UTF-8 required part)
const char* none_bar = "────";
const char* half_bar = "─▄▄▄";
const char* full_bar = "─███";
const char* x_axis = "─▀▀▀";
const char* x_axis_0 = "────";
const char* x_axis_cap = "─ ";
const char* y_axis = "├";
const char* line_cap = "─ ";
const char* origin = "└";
const char* digits[] = { "₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉" };
// Draw the graph
// (draw everything above the X-axis)
std::cout << std::fixed << "\n";
for (int n = 0; n < nlines; n++)
{
double y2 = (nlines - n) * divisor;
double y1 = y2 - divisor / 4;
std::cout << std::setw( 6 ) << std::setprecision( 2 ) << y2 << " " << y_axis;
for (auto x : xs)
{
if (x > y2) std::cout << full_bar;
else if (x > y1) std::cout << half_bar;
else std::cout << none_bar;
}
std::cout << line_cap << "\n";
}
// (draw the X-axis)
std::cout << " 0.00 " << origin;
for (auto x : xs)
{
if (x > divisor / 4) std::cout << x_axis;
else std::cout << x_axis_0;
}
std::cout << x_axis_cap << "\n";
// (draw labels under the X-axis)
std::cout << std::string( 8, ' ' );
for (int n = 0; n < xs.size(); n++)
{
auto s = std::to_string( n );
std::string d;
for (auto& c : s) d += digits[ c - '0' ];
std::cout << std::setw( 3 ) << "x" << d;
}
std::cout << "\n";
}
|