Console Table and Graph/Plot Libraries
Copyright Β© 2018 Teal Dulcet
These header only libraries use box-drawing, Braille, fraction and other Unicode characters and terminal colors and formatting to output tables and graphs/plots to the console. All the tables and graphs are created with a single (one) function call and they do not require any special data structures.
See the python directory for Python ports of the libraries.
For command-line tools using these respective libraries, see the Tables and Graphs CLI.
β€οΈ Please visit tealdulcet.com to support these libraries and my other software development.
Requires support for C++17. See the tables.hpp file for full usage information.
Complete versions of all of the examples below and more can be found in the tables.cpp file.
Compile with:
- GCC:
g++ -std=c++17 -Wall -g -O3 tables.cpp -o tables
- Clang:
clang++ -std=c++17 -Wall -g -O3 tables.cpp -o tables
Other compilers should work as well, but are not (yet) tested.
Run with: ./tables
#include "tables.hpp"
using namespace std;
int main()
{
size_t rows = 5;
size_t columns = 5;
char ***array;
// Allocate and set array
tables::options aoptions = {.headerrow = true, .headercolumn = true};
tables::array(rows, columns, array, nullptr, nullptr, aoptions);
// Deallocate array
return 0;
}
#include "tables.hpp"
using namespace std;
int main()
{
size_t rows = 5;
size_t columns = 5;
vector<vector<string>> array(rows, vector<string>(columns));
// Set array
string *headerrow = nullptr;
string *headercolumn = nullptr;
tables::options aoptions = {.headerrow = true, .headercolumn = true};
tables::array(array, headerrow, headercolumn, aoptions);
return 0;
}
Table cells can contain Unicode characters and formatted text with ANSI escape sequences, but not newlines and tabs.
#include "tables.hpp"
using namespace std;
int main()
{
size_t rows = 4;
size_t columns = 4;
const char* headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"};
const char* headercolumn[] = {"Header column 2", "Header column 3", "Header column 4", "Header column 5"};
char ***array;
// Allocate and set array
tables::options aoptions = {.headerrow = true, .headercolumn = true};
tables::array(rows, columns, array, headerrow, headercolumn, aoptions);
// Deallocate array
return 0;
}
#include "tables.hpp"
using namespace std;
int main()
{
size_t rows = 5;
size_t columns = 5;
string headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"};
string headercolumn[] = {"Header column 2", "Header column 3", "Header column 4", "Header column 5"};
vector<vector<string>> array(rows, vector<string>(columns));
// Set array
tables::options aoptions = {.headerrow = true, .headercolumn = true};
tables::array(array, headerrow, headercolumn, aoptions);
return 0;
}
Output same as example above.
#include "tables.hpp"
using namespace std;
int main()
{
size_t rows = 5;
size_t columns = 5;
double **array; // array can be any data type
// Allocate and set array
tables::array(rows, columns, array);
// Deallocate array
return 0;
}
#include "tables.hpp"
using namespace std;
int main()
{
size_t rows = 5;
size_t columns = 5;
vector<vector<double>> array(rows, vector<double>(columns)); // array can be any data type
// Set array
tables::array(array);
return 0;
}
#include <algorithm>
#include "tables.hpp"
using namespace std;
int dimensions; // Number of columns
int sortdimension; // Column to sort by
template <typename T>
bool compare(const T &a, const T &b)
{
if (a[sortdimension] == b[sortdimension])
for (int i = 0; i < dimensions; ++i)
if (sortdimension != i and a[i] != b[i])
return a[i] < b[i];
return a[sortdimension] < b[sortdimension];
}
int main()
{
size_t rows = 5;
size_t columns = 5;
int **array; // array can be any data type
// Allocate and set array
dimensions = columns;
sortdimension = 0;
sort(array, array + rows, compare<int *>);
tables::array(rows, columns, array);
// Deallocate array
return 0;
}
#include <algorithm>
#include "tables.hpp"
using namespace std;
int sortdimension; // Column to sort by
template <typename T>
bool compare(const T &a, const T &b)
{
if (a[sortdimension] == b[sortdimension])
for (int i = 0; i < tables::size(a); ++i)
if (sortdimension != i and a[i] != b[i])
return a[i] < b[i];
return a[sortdimension] < b[sortdimension];
}
int main()
{
size_t rows = 5;
size_t columns = 5;
vector<vector<int>> array(rows, vector<int>(columns)); // array can be any data type
// Set array
sortdimension = 0;
sort(array.begin(), array.end(), compare<vector<int>>);
tables::array(array);
return 0;
}
#include "tables.hpp"
using namespace std;
double afunction(double x)
{
return x + 1;
}
int main()
{
double xmin = -10;
double xmax = 10;
double xstep = 0.5;
tables::options aoptions = {.headerrow = true};
tables::function(xmin, xmax, xstep, afunction, aoptions);
return 0;
}
#include "tables.hpp"
using namespace std;
int main()
{
double xmin = -10;
double xmax = 10;
double xstep = 0.5;
function<double(double)> afunction = [](auto x)
{ return x + 1; };
tables::options aoptions = {.headerrow = true};
tables::function(xmin, xmax, xstep, afunction, aoptions);
return 0;
}
#include <cmath>
#include "tables.hpp"
using namespace std;
double function1(double x)
{
return 2 * x;
}
double function2(double x)
{
return pow(x, 2);
}
int main()
{
double xmin = -10;
double xmax = 10;
double xstep = 0.5;
size_t numfunctions = 2;
// Function parameter and return value can be any data type, as long as they are the same
function<double(double)> functions[] = {function1, function2};
tables::options aoptions = {.headerrow = true};
tables::functions(xmin, xmax, xstep, numfunctions, functions, aoptions);
return 0;
}
#include <cmath>
#include "tables.hpp"
using namespace std;
int main()
{
double xmin = -10;
double xmax = 10;
double xstep = 0.5;
size_t numfunctions = 2;
// Function parameter and return value can be any data type, as long as they are the same
function<double(double)> functions[] = {[](auto x)
{ return 2 * x; },
[](auto x)
{ return pow(x, 2); }};
tables::options aoptions = {.headerrow = true};
tables::functions(xmin, xmax, xstep, numfunctions, functions, aoptions);
return 0;
}
Option: headerrow
Default value: false
Header rows are bolded, centered and have a border.
Option: headercolumn
Default value: false
Header columns are bolded, centered and have a border.
Option: tableborder
Default value: true
Option: cellborder
Default value: false
Option: padding
Default value: 1
Option: alignment
Values:
nullptr
left
(default)right
internal
(integer and floating-point types only)
Option: boolalpha
Default value: false
Option: title
Default value: nullptr
The title is output at the top of the table. It is word wrapped based on the current width of the terminal, using this solution. Handles newlines, tabs and Unicode characters.
Option: style
Values:
style_ASCII
: ASCIIstyle_basic
: Basicstyle_light
: Light (default)style_heavy
: Heavystyle_double
: Doublestyle_arc
: Light Arcstyle_light_dashed
: Light Dashedstyle_heavy_dashed
: Heavy Dashed
Option: check
Default value: true
Check that the width of the table is not greater then the width of the terminal.
- C++ Text Table (must specify every cell individually in their data structure, limited options, no Unicode support, no header row or column support)
- Cpp Console Table (must specify every cell individually in their data structure, no Unicode support, no header row or column support)
- ConsoleTable (requires C++11, must specify entire row at once in their data structure, no header column support)
Requires support for C++17. See the graphs.hpp file for full usage information.
Complete versions of all of the examples below and more can be found in the graphs.cpp file.
Compile with:
- GCC:
g++ -std=c++17 -Wall -g -O3 graphs.cpp -o graphs
- Clang:
clang++ -std=c++17 -Wall -g -O3 graphs.cpp -o graphs
Other compilers should work as well, but are not (yet) tested.
Run with: ./graphs
If height
is 0
, it will be set to the current height of the terminal (number of rows times four). If width
is 0
, it will be set to the current width of the terminal (number of columns times two).
#include "graphs.hpp"
using namespace std;
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
size_t rows = 100;
double *array; // array can be any data type
// Allocate and set array
graphs::histogram(height, width, xmin, xmax, ymin, ymax, rows, array);
// Deallocate array
return 0;
}
#include "graphs.hpp"
using namespace std;
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
size_t rows = 100;
vector<double> array(rows); // array can be any data type
// Set array
graphs::histogram(height, width, xmin, xmax, ymin, ymax, array);
return 0;
}
If xmin
and xmax
are both 0
, they will be set to the respective minimum and maximum values of x in the array. If ymin
and ymax
are both 0
, they will be set to the respective minimum and maximum values of y in the resulting histogram.
#include "graphs.hpp"
using namespace std;
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
size_t rows = 10;
double **array; // array can be any data type, but must have exactly two columns
// Allocate and set array
graphs::plot(height, width, xmin, xmax, ymin, ymax, rows, array);
// Deallocate array
return 0;
}
#include "graphs.hpp"
using namespace std;
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
size_t rows = 10;
vector<vector<double>> array(rows, vector<double>(2)); // array can be any data type, but must have exactly two columns
// Set array
graphs::plot(height, width, xmin, xmax, ymin, ymax, array);
return 0;
}
If xmin
and xmax
are both 0
, they will be set to the respective minimum and maximum values of x in the array. If ymin
and ymax
are both 0
, they will be set to the respective minimum and maximum values of y in the array.
Use graphs::plots()
to plot multiple arrays, which can be of different sizes.
#include "graphs.hpp"
using namespace std;
double afunction(double x)
{
return x + 1;
}
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
graphs::function(height, width, xmin, xmax, ymin, ymax, afunction);
return 0;
}
#include "graphs.hpp"
using namespace std;
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
function<double(double)> afunction = [](auto x)
{ return x + 1; };
graphs::function(height, width, xmin, xmax, ymin, ymax, afunction);
return 0;
}
#include "graphs.hpp"
using namespace std;
double function1(double x)
{
return 2 * x;
}
double function2(double x)
{
return pow(x, 2);
}
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
size_t numfunctions = 2;
// Function parameter and return value can be any data type, as long as they are the same
function<double(double)> functions[] = {function1, function2};
graphs::functions(height, width, xmin, xmax, ymin, ymax, numfunctions, functions);
return 0;
}
#include "graphs.hpp"
using namespace std;
int main()
{
size_t height = 160;
size_t width = 160;
long double xmin = -20;
long double xmax = 20;
long double ymin = -20;
long double ymax = 20;
size_t numfunctions = 2;
// Function parameter and return value can be any data type, as long as they are the same
function<double(double)> functions[] = {[](auto x)
{ return 2 * x; },
[](auto x)
{ return pow(x, 2); }};
graphs::functions(height, width, xmin, xmax, ymin, ymax, numfunctions, functions);
return 0;
}
Option: border
Default value: false
Option: axis
Default value: true
Option: axislabel
Default value: true
Requires axis
to be true
.
Option: axistick
Default value: true
Requires axis
to be true
.
Option: axisunitslabel
Default value: true
Requires axis
and axistick
to be true
.
Option: xunits
Values:
units_number
: Locale number formatunits_scale_none
: Locale number format with full precisionunits_scale_SI
: Auto-scale to the SI standardunits_scale_IEC
: Auto-scale to the IEC standardunits_scale_IEC_I
: Auto-scale to the IEC standardunits_fracts
: Locale number format, but convert fractions and mathematical constants to Unicode characters (default)units_percent
: Percentage formatunits_date
: Locale date formatunits_time
: Locale time formatunits_monetary
: Locale monetary/currency format
Formats 2-5 are similar to the respective --to
options with the numfmt command from GNU Coreutils, but with more precision.
Option: yunits
Values: Same as above.
Option: type
Values:
The Braille type has the highest resolution of 2Γ4 pixels per character, while the block type uses 2Γ2. This option is only used for plots and graphs. Histograms use 1Γ8 pixels per character.
Option: mark
Values:
The dot mark type uses a single pixel per mark, the plus uses five pixels and the square uses eight pixels. This option is only used for plots and graphs.
Option: title
Default value: nullptr
The title is output at the top of the graph. It is word wrapped based on the current width of the terminal, using this solution. Handles newlines, tabs and Unicode characters.
Option: style
Values:
style_ASCII
: ASCIIstyle_basic
: Basicstyle_light
: Light (default)style_heavy
: Heavystyle_double
: Doublestyle_arc
: Light Arcstyle_light_dashed
: Light Dashedstyle_heavy_dashed
: Heavy Dashed
Option: color
Values:
color_default
: System defaultcolor_black
: Blackcolor_red
: Red (default)color_green
: Greencolor_yellow
: Yellowcolor_blue
: Bluecolor_magenta
: Magentacolor_cyan
: Cyancolor_white
: Whitecolor_gray
: Graycolor_bright_red
: Bright Redcolor_bright_green
: Bright Greencolor_bright_yellow
: Bright Yellowcolor_bright_blue
: Bright Bluecolor_bright_magenta
: Bright Magentacolor_bright_cyan
: Bright Cyancolor_bright_white
: Bright White
See here for examples of the colors.
This option is only used when plotting a single array and when graphing a single function. When plotting multiple arrays or graphing multiple functions, colors 2 - 16 are used inorder. The system default color is used where the plots cross.
Option: check
Default value: true
Check that the width and height of the graph are not greater then the respective width and height of the terminal.
- C++ terminal plotting library (requires C++14, based on UnicodePlots.jl, no documentation and very difficult to use, although supports animations)
Pull requests welcome! Ideas for contributions:
Both:
- Add more options
- Add options to word wrap and truncate long text in table cells
- Add option to center text in table cells
- Add more examples
- Improve the performance
- Handle newlines and tabs in the tables
- Handle formatted text in the table and graph/plot titles
- Support more graph/plot colors
- Support 256 and 24-bit color
- Support combining/blending colors when functions cross
- Update the
-t, --table
options of column command from util-linux to use the Table library - Port to other languages (C, Java, Rust, etc.)
C++:
- Update the CI to test with more compilers
- Support tables with the
wchar_t
,char16_t
andchar32_t
C data types and thewstring
,u16string
andu32string
C++ data types.