Added support for new block types in Unicode 13 and 16.

This commit is contained in:
Teal Dulcet 2024-12-17 08:55:15 -08:00
parent ed5d06d08a
commit ba19fd98c0
7 changed files with 248 additions and 151 deletions

View File

@ -11,6 +11,7 @@ jobs:
name: Linux C++ name: Linux C++
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.cxx == 'clang++' && matrix.os == 'ubuntu-20.04' }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04] os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
@ -68,7 +69,7 @@ jobs:
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
python3 -m pip install ruff python3 -m pip install ruff
- name: Script - name: Script
run: ruff check --output-format=github --target-version py37 --select F,E4,E7,E9,W,I,D,UP,YTT,ANN,S,BLE,B,A,COM819,C4,T10,EM,EXE,ISC,ICN,G,PIE,PYI,Q,RSE,RET,SLF,SLOT,SIM,TID,TCH,ARG,PGH,PL,TRY,FLY,PERF,FURB,LOG,RUF --preview --ignore W191,D211,D213,D401,PLR09,PLR1702,PLR2004,FURB101,FURB167,RUF001,RUF002,RUF003,RUF023 . run: ruff check --output-format=github --target-version py37 --select F,E4,E7,E9,W,I,D,UP,YTT,ANN,S,BLE,B,A,COM819,C4,T10,EM,EXE,ISC,ICN,G,PIE,PYI,Q,RSE,RET,SLF,SLOT,SIM,TID,TC,ARG,PGH,PL,TRY,FLY,PERF,FURB,LOG,RUF --preview --ignore W191,D211,D213,D401,PLR09,PLR1702,PLR2004,FURB101,FURB167,RUF001,RUF002,RUF003,RUF023 .
continue-on-error: true continue-on-error: true
Python: Python:

View File

@ -544,7 +544,7 @@ Other compilers should work as well, but are not (yet) tested.
Run with: `./graphs` 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). If `height` is `0`, it will be set to the current height of the terminal (number of rows). If `width` is `0`, it will be set to the current width of the terminal (number of columns).
#### Output array as histogram #### Output array as histogram
@ -557,8 +557,8 @@ using namespace std;
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -588,8 +588,8 @@ using namespace std;
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -623,8 +623,8 @@ using namespace std;
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -654,8 +654,8 @@ using namespace std;
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -696,8 +696,8 @@ double afunction(double x)
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -719,8 +719,8 @@ using namespace std;
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -759,8 +759,8 @@ double function2(double x)
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -787,8 +787,8 @@ using namespace std;
int main() int main()
{ {
size_t height = 160; size_t height = 40;
size_t width = 160; size_t width = 80;
long double xmin = -20; long double xmin = -20;
long double xmax = 20; long double xmax = 20;
@ -875,9 +875,16 @@ Values:
1. `type_braille`: Braille (default) 1. `type_braille`: Braille (default)
![](images/type%20braille%20graph.png) ![](images/type%20braille%20graph.png)
2. `type_block`: Block 2. `type_block`: Block
3. `type_block_quadrant`: Block quadrant
![](images/type%20block%20graph.png) ![](images/type%20block%20graph.png)
4. `type_separated_block_quadrant`: Separated block quadrant
5. `type_block_sextant`: Block sextant
6. `type_separated_block_sextant`: Separated block sextant
7. `type_block_octant`: Block octant
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. The Braille and block octant types have the highest density of 2×4 pixels per character, while the two block sextant types use 2×3, the two block quadrant types use 2×2 and the block type uses 1×1. This option is only used for plots and graphs. Histograms use 1×8 pixels per character.
The block sextant type requires support for Unicode 13.0, while the separated block quadrant, separated block sextant and block octant types require support for Unicode 16.0.
#### Mark type #### Mark type

View File

@ -45,8 +45,8 @@ constexpr long double function5(long double x)
int main() int main()
{ {
const size_t height = 160; const size_t height = 40;
const size_t width = 160; const size_t width = 80;
const long double xmin = -20; const long double xmin = -20;
const long double xmax = 20; const long double xmax = 20;

View File

@ -77,24 +77,40 @@ namespace graphs
enum color_type const color_types[] = {color_default, color_black, color_red, color_green, color_yellow, color_blue, color_magenta, color_cyan, color_white, color_gray, color_bright_red, color_bright_green, color_bright_yellow, color_bright_blue, color_bright_magenta, color_bright_cyan, color_bright_white}; enum color_type const color_types[] = {color_default, color_black, color_red, color_green, color_yellow, color_blue, color_magenta, color_cyan, color_white, color_gray, color_bright_red, color_bright_green, color_bright_yellow, color_bright_blue, color_bright_magenta, color_bright_cyan, color_bright_white};
const char *const colors[] = {"\e[39m", "\e[30m", "\e[31m", "\e[32m", "\e[33m", "\e[34m", "\e[35m", "\e[36m", "\e[37m", "\e[90m", "\e[91m", "\e[92m", "\e[93m", "\e[94m", "\e[95m", "\e[96m", "\e[97m"}; const unsigned char colors[] = {39, 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97};
const char *const dots[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}; const char *const dots[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
const int dotvalues[][4] = {{0x1, 0x2, 0x4, 0x40}, {0x8, 0x10, 0x20, 0x80}}; const int dotvalues[][4] = {{0x1, 0x2, 0x4, 0x40}, {0x8, 0x10, 0x20, 0x80}};
const char *const blocks[] = {" ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}; const char *const blocks[] = {"\u00A0", ""};
const int blockvalues[][2] = {{4, 1}, {8, 2}};
const char *const bars[] = {" ", "", "", "", "", "", "", "", ""}; const char *const blocks_quadrant[] = {"\u00A0", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
const char *const separated_blocks_quadrant[] = {"\u00A0", "𜰡", "𜰢", "𜰣", "𜰤", "𜰥", "𜰦", "𜰧", "𜰨", "𜰩", "𜰪", "𜰫", "𜰬", "𜰭", "𜰮", "𜰯"};
const char *const blocks_sextant[] = {"\u00A0", "🬀", "🬁", "🬂", "🬃", "🬄", "🬅", "🬆", "🬇", "🬈", "🬉", "🬊", "🬋", "🬌", "🬍", "🬎", "🬏", "🬐", "🬑", "🬒", "🬓", "", "🬔", "🬕", "🬖", "🬗", "🬘", "🬙", "🬚", "🬛", "🬜", "🬝", "🬞", "🬟", "🬠", "🬡", "🬢", "🬣", "🬤", "🬥", "🬦", "🬧", "", "🬨", "🬩", "🬪", "🬫", "🬬", "🬭", "🬮", "🬯", "🬰", "🬱", "🬲", "🬳", "🬴", "🬵", "🬶", "🬷", "🬸", "🬹", "🬺", "🬻", ""};
const char *const separated_blocks_sextant[] = {"\u00A0", "𜹑", "𜹒", "𜹓", "𜹔", "𜹕", "𜹖", "𜹗", "𜹘", "𜹙", "𜹚", "𜹛", "𜹜", "𜹝", "𜹞", "𜹟", "𜹠", "𜹡", "𜹢", "𜹣", "𜹤", "𜹥", "𜹦", "𜹧", "𜹨", "𜹩", "𜹪", "𜹫", "𜹬", "𜹭", "𜹮", "𜹯", "𜹰", "𜹱", "𜹲", "𜹳", "𜹴", "𜹵", "𜹶", "𜹷", "𜹸", "𜹹", "𜹺", "𜹻", "𜹼", "𜹽", "𜹾", "𜹿", "𜺀", "𜺁", "𜺂", "𜺃", "𜺄", "𜺅", "𜺆", "𜺇", "𜺈", "𜺉", "𜺊", "𜺋", "𜺌", "𜺍", "𜺎", "𜺏"};
const char *const blocks_octant[] = {"\u00A0", "𜺨", "𜺫", "🮂", "𜴀", "", "𜴁", "𜴂", "𜴃", "𜴄", "", "𜴅", "𜴆", "𜴇", "𜴈", "", "𜴉", "𜴊", "𜴋", "𜴌", "🯦", "𜴍", "𜴎", "𜴏", "𜴐", "𜴑", "𜴒", "𜴓", "𜴔", "𜴕", "𜴖", "𜴗", "𜴘", "𜴙", "𜴚", "𜴛", "𜴜", "𜴝", "𜴞", "𜴟", "🯧", "𜴠", "𜴡", "𜴢", "𜴣", "𜴤", "𜴥", "𜴦", "𜴧", "𜴨", "𜴩", "𜴪", "𜴫", "𜴬", "𜴭", "𜴮", "𜴯", "𜴰", "𜴱", "𜴲", "𜴳", "𜴴", "𜴵", "🮅", "𜺣", "𜴶", "𜴷", "𜴸", "𜴹", "𜴺", "𜴻", "𜴼", "𜴽", "𜴾", "𜴿", "𜵀", "𜵁", "𜵂", "𜵃", "𜵄", "", "𜵅", "𜵆", "𜵇", "𜵈", "", "𜵉", "𜵊", "𜵋", "𜵌", "", "𜵍", "𜵎", "𜵏", "𜵐", "", "𜵑", "𜵒", "𜵓", "𜵔", "𜵕", "𜵖", "𜵗", "𜵘", "𜵙", "𜵚", "𜵛", "𜵜", "𜵝", "𜵞", "𜵟", "𜵠", "𜵡", "𜵢", "𜵣", "𜵤", "𜵥", "𜵦", "𜵧", "𜵨", "𜵩", "𜵪", "𜵫", "𜵬", "𜵭", "𜵮", "𜵯", "𜵰", "𜺠", "𜵱", "𜵲", "𜵳", "𜵴", "𜵵", "𜵶", "𜵷", "𜵸", "𜵹", "𜵺", "𜵻", "𜵼", "𜵽", "𜵾", "𜵿", "𜶀", "𜶁", "𜶂", "𜶃", "𜶄", "𜶅", "𜶆", "𜶇", "𜶈", "𜶉", "𜶊", "𜶋", "𜶌", "𜶍", "𜶎", "𜶏", "", "𜶐", "𜶑", "𜶒", "𜶓", "", "𜶔", "𜶕", "𜶖", "𜶗", "", "𜶘", "𜶙", "𜶚", "𜶛", "", "𜶜", "𜶝", "𜶞", "𜶟", "𜶠", "𜶡", "𜶢", "𜶣", "𜶤", "𜶥", "𜶦", "𜶧", "𜶨", "𜶩", "𜶪", "𜶫", "", "𜶬", "𜶭", "𜶮", "𜶯", "𜶰", "𜶱", "𜶲", "𜶳", "𜶴", "𜶵", "𜶶", "𜶷", "𜶸", "𜶹", "𜶺", "𜶻", "𜶼", "𜶽", "𜶾", "𜶿", "𜷀", "𜷁", "𜷂", "𜷃", "𜷄", "𜷅", "𜷆", "𜷇", "𜷈", "𜷉", "𜷊", "𜷋", "𜷌", "𜷍", "𜷎", "𜷏", "𜷐", "𜷑", "𜷒", "𜷓", "𜷔", "𜷕", "𜷖", "𜷗", "𜷘", "𜷙", "𜷚", "", "𜷛", "𜷜", "𜷝", "𜷞", "", "𜷟", "𜷠", "𜷡", "𜷢", "", "𜷣", "", "𜷤", "𜷥", ""};
const char *const bars[] = {"\u00A0", "", "", "", "", "", "", "", ""};
enum type_type enum type_type
{ {
type_braille, type_braille,
type_block, type_block,
type_block_quadrant,
type_separated_block_quadrant,
type_block_sextant,
type_separated_block_sextant,
type_block_octant,
type_histogram // Set automatically by the histogram() function type_histogram // Set automatically by the histogram() function
}; };
enum type_type const type_types[] = {type_braille, type_block /* , type_histogram */}; enum type_type const type_types[] = {type_braille, type_block, type_block_quadrant, type_separated_block_quadrant, type_block_sextant, type_separated_block_sextant, type_block_octant /* , type_histogram */};
const unsigned short densities[][2] = {{4, 2}, {1, 1}, {2, 2}, {2, 2}, {3, 2}, {3, 2}, {4, 2}, {8, 1}};
enum plot_type enum plot_type
{ {
@ -104,7 +120,7 @@ namespace graphs
enum plot_type const plot_types[] = {plot_scatter, plot_line}; enum plot_type const plot_types[] = {plot_scatter, plot_line};
const short marks[][8][2] = {{{0, 0}}, {{0, 1}, {-1, 0}, {0, 0}, {1, 0}, {0, -1}}, {{-1, 1}, {0, 1}, {1, 1}, {-1, 0}, {1, 0}, {-1, -1}, {0, -1}, {1, -1}}}; const vector<vector<pair<short, short>>> marks = {{{0, 0}}, {{0, 1}, {-1, 0}, {0, 0}, {1, 0}, {0, -1}}, {{-1, 1}, {0, 1}, {1, 1}, {-1, 0}, {1, 0}, {-1, -1}, {0, -1}, {1, -1}}};
enum mark_type enum mark_type
{ {
@ -378,8 +394,9 @@ namespace graphs
strm << number; strm << number;
} }
inline size_t outputlabel(const long double label, const units_type units, ostringstream &strm) inline size_t outputlabel(const long double label, const units_type units, string &str)
{ {
ostringstream strm;
strm.imbue(locale("")); strm.imbue(locale(""));
switch (units) switch (units)
@ -420,11 +437,21 @@ namespace graphs
break; break;
} }
const size_t length = strcol(strm.str()); str = strm.str();
const size_t length = strcol(str);
return length; return length;
} }
inline string outputcolor(const color_type color)
{
ostringstream strm;
strm << "\e[" << int(colors[color]) << "m";
return strm.str();
}
// Output graph // Output graph
inline int graph(const size_t height, const size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, const vector<vector<unsigned short>> &array, const options &aoptions) inline int graph(const size_t height, const size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, const vector<vector<unsigned short>> &array, const options &aoptions)
{ {
@ -437,7 +464,6 @@ namespace graphs
const bool axistick = aoptions.axistick; const bool axistick = aoptions.axistick;
const bool axisunitslabel = aoptions.axisunitslabel; const bool axisunitslabel = aoptions.axisunitslabel;
const type_type type = aoptions.type; const type_type type = aoptions.type;
const char *const title = aoptions.title;
if (!height) if (!height)
return 1; return 1;
@ -448,9 +474,7 @@ namespace graphs
struct winsize w; struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
const size_t ai = type == type_histogram ? 8 : type == type_block ? 2 const auto [ai, aj] = densities[type];
: 4;
const size_t aj = type == type_histogram ? 1 : 2;
const size_t aheight = height / ai; const size_t aheight = height / ai;
const size_t awidth = width / aj; const size_t awidth = width / aj;
@ -493,8 +517,8 @@ namespace graphs
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
if (title and title[0] != '\0') if (aoptions.title and aoptions.title[0] != '\0')
aoptions.ostr << wrap(title, awidth) << '\n'; aoptions.ostr << wrap(aoptions.title, awidth) << '\n';
const char *const *astyle = styles[aoptions.style]; const char *const *astyle = styles[aoptions.style];
@ -515,7 +539,7 @@ namespace graphs
const bool ayaxis = yaxis <= (height - ai) ? i <= yaxis and (i + ai) > yaxis : i < yaxis and (i + ai) >= yaxis; const bool ayaxis = yaxis <= (height - ai) ? i <= yaxis and (i + ai) > yaxis : i < yaxis and (i + ai) >= yaxis;
const bool yaxislabel = yaxis <= (height - ai) ? i <= (yaxis + ai) and (i + ai) > (yaxis + ai) : i < (yaxis - ai) and (i + ai) >= (yaxis - ai); const bool yaxislabel = yaxis <= (height - ai) ? i <= (yaxis + ai) and (i + ai) > (yaxis + ai) : i < (yaxis - ai) and (i + ai) >= (yaxis - ai);
ostringstream ylabelstrm; string ylabelstr;
size_t ylabellength = 0; size_t ylabellength = 0;
if (axis and axistick and axisunitslabel and yaxis >= 0 and yaxis <= height) if (axis and axistick and axisunitslabel and yaxis >= 0 and yaxis <= height)
@ -536,7 +560,7 @@ namespace graphs
if (output) if (output)
{ {
ylabellength = outputlabel(label, aoptions.yunits, ylabelstrm); ylabellength = outputlabel(label, aoptions.yunits, ylabelstr);
ylabellength *= aj; ylabellength *= aj;
} }
} }
@ -654,12 +678,12 @@ namespace graphs
{ {
output = false; output = false;
ostringstream astrm; string astr;
size_t length = outputlabel(label, aoptions.xunits, astrm); size_t length = outputlabel(label, aoptions.xunits, astr);
length *= aj; length *= aj;
if ((j >= xaxis or (j + length) < (ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0 ? xaxis - ai : xaxis)) and (j + length) < (width - aj) and (xaxis <= (width - aj) or j > aj)) if ((j >= xaxis or (j + length) < (ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0 ? xaxis - ai : xaxis)) and (j + length) < (width - aj) and (xaxis <= (width - aj) or j > aj))
{ {
strm << astrm.str(); strm << astr;
if (length > aj) if (length > aj)
j += length - aj; j += length - aj;
@ -678,7 +702,7 @@ namespace graphs
} }
else if (ylabellength and (xaxis < aj ? xaxislabel : j < (xaxis - ylabellength) and (j + aj) >= (xaxis - ylabellength)) and (yaxis >= ai or i < (height - ai)) and axistick and axisunitslabel) else if (ylabellength and (xaxis < aj ? xaxislabel : j < (xaxis - ylabellength) and (j + aj) >= (xaxis - ylabellength)) and (yaxis >= ai or i < (height - ai)) and axistick and axisunitslabel)
{ {
strm << ylabelstrm.str(); strm << ylabelstr;
output = true; output = true;
if (ylabellength > aj) if (ylabellength > aj)
j += ylabellength - aj; j += ylabellength - aj;
@ -697,15 +721,24 @@ namespace graphs
const unsigned short value = array[j + k][i + l]; const unsigned short value = array[j + k][i + l];
if (value) if (value)
{ {
if (type == type_histogram) switch (type)
{ {
case type_braille:
dot += dotvalues[k][l];
break;
case type_block:
case type_block_quadrant:
case type_separated_block_quadrant:
case type_block_sextant:
case type_separated_block_sextant:
case type_block_octant:
dot += 1 << (l * aj + k);
break;
case type_histogram:
if (!dot) if (!dot)
dot = (size(bars) - l) - 1; dot = (size(bars) - l) - 1;
break;
} }
else if (type == type_block)
dot += blockvalues[k][l];
else
dot += dotvalues[k][l];
} }
if (color) if (color)
{ {
@ -721,13 +754,38 @@ namespace graphs
--color; --color;
if (color) if (color)
strm << colors[color]; strm << outputcolor(color_type(color));
strm << (type == type_histogram ? bars[dot] : type == type_block ? blocks[dot] switch (type)
: dots[dot]); {
case type_braille:
strm << dots[dot];
break;
case type_block:
strm << blocks[dot];
break;
case type_block_quadrant:
strm << blocks_quadrant[dot];
break;
case type_separated_block_quadrant:
strm << separated_blocks_quadrant[dot];
break;
case type_block_sextant:
strm << blocks_sextant[dot];
break;
case type_separated_block_sextant:
strm << separated_blocks_sextant[dot];
break;
case type_block_octant:
strm << blocks_octant[dot];
break;
case type_histogram:
strm << bars[dot];
break;
}
if (color) if (color)
strm << colors[0]; strm << outputcolor(color_default);
} }
} }
@ -766,31 +824,30 @@ namespace graphs
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (!height) if (!height)
height = w.ws_row * 4; height = w.ws_row;
if (!width) if (!width)
width = w.ws_col * 2; width = w.ws_col;
if (aoptions.check) if (aoptions.check)
{ {
const size_t aheight = height / 4; if (height > w.ws_row)
const size_t awidth = width / 2;
if (aheight > w.ws_row)
{ {
cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; cerr << "The height of the graph (" << height << ") is greater then the height of the terminal (" << w.ws_row << ").\n";
return 1; return 1;
} }
if (awidth > w.ws_col) if (width > w.ws_col)
{ {
cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; cerr << "The width of the graph (" << width << ") is greater then the width of the terminal (" << w.ws_col << ").\n";
return 1; return 1;
} }
} }
height *= 2; const auto [ai, aj] = densities[type_histogram];
width /= 2;
height *= ai;
width *= aj;
if (!xmin and !xmax) if (!xmin and !xmax)
{ {
@ -890,31 +947,30 @@ namespace graphs
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (!height) if (!height)
height = w.ws_row * 4; height = w.ws_row;
if (!width) if (!width)
width = w.ws_col * 2; width = w.ws_col;
if (aoptions.check) if (aoptions.check)
{ {
const size_t aheight = height / 4; if (height > w.ws_row)
const size_t awidth = width / 2;
if (aheight > w.ws_row)
{ {
cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; cerr << "The height of the graph (" << height << ") is greater then the height of the terminal (" << w.ws_row << ").\n";
return 1; return 1;
} }
if (awidth > w.ws_col) if (width > w.ws_col)
{ {
cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; cerr << "The width of the graph (" << width << ") is greater then the width of the terminal (" << w.ws_col << ").\n";
return 1; return 1;
} }
} }
if (aoptions.type == type_block) const auto [ai, aj] = densities[aoptions.type];
height /= 2;
height *= ai;
width *= aj;
if (!xmin and !xmax) if (!xmin and !xmax)
{ {
@ -1029,31 +1085,30 @@ namespace graphs
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (!height) if (!height)
height = w.ws_row * 4; height = w.ws_row;
if (!width) if (!width)
width = w.ws_col * 2; width = w.ws_col;
if (aoptions.check) if (aoptions.check)
{ {
const size_t aheight = height / 4; if (height > w.ws_row)
const size_t awidth = width / 2;
if (aheight > w.ws_row)
{ {
cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; cerr << "The height of the graph (" << height << ") is greater then the height of the terminal (" << w.ws_row << ").\n";
return 1; return 1;
} }
if (awidth > w.ws_col) if (width > w.ws_col)
{ {
cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; cerr << "The width of the graph (" << width << ") is greater then the width of the terminal (" << w.ws_col << ").\n";
return 1; return 1;
} }
} }
if (aoptions.type == type_block) const auto [ai, aj] = densities[aoptions.type];
height /= 2;
height *= ai;
width *= aj;
if (xmin >= xmax) if (xmin >= xmax)
{ {

View File

@ -273,15 +273,15 @@ Complete versions of all of the examples below and more can be found in the [tes
Run with: `python3 -OO test.py`. Run with: `python3 -OO test.py`.
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). If `height` is `0`, it will be set to the current height of the terminal (number of rows). If `width` is `0`, it will be set to the current width of the terminal (number of columns).
#### Output array as histogram #### Output array as histogram
```py ```py
import graphs import graphs
height = 160 height = 40
width = 160 width = 80
xmin = -20 xmin = -20
xmax = 20 xmax = 20
@ -302,8 +302,8 @@ If `xmin` and `xmax` are both `0`, they will be set to the respective minimum an
```py ```py
import graphs import graphs
height = 160 height = 40
width = 160 width = 80
xmin = -20 xmin = -20
xmax = 20 xmax = 20
@ -329,8 +329,8 @@ import graphs
def afunction(x): def afunction(x):
return x + 1 return x + 1
height = 160 height = 40
width = 160 width = 80
xmin = -20 xmin = -20
xmax = 20 xmax = 20
@ -347,8 +347,8 @@ graphs.function(height, width, xmin, xmax, ymin, ymax, afunction)
```py ```py
import graphs import graphs
height = 160 height = 40
width = 160 width = 80
xmin = -20 xmin = -20
xmax = 20 xmax = 20
@ -373,8 +373,8 @@ def function1(x):
def function2(x): def function2(x):
return x ** 2 return x ** 2
height = 160 height = 40
width = 160 width = 80
xmin = -20 xmin = -20
xmax = 20 xmax = 20
@ -394,8 +394,8 @@ graphs.functions(height, width, xmin, xmax, ymin, ymax, functions)
```py ```py
import graphs import graphs
height = 160 height = 40
width = 160 width = 80
xmin = -20 xmin = -20
xmax = 20 xmax = 20
@ -474,9 +474,16 @@ Values:
1. `type_types.braille`: Braille (default) 1. `type_types.braille`: Braille (default)
![](../images/type%20braille%20graph.png) ![](../images/type%20braille%20graph.png)
2. `type_types.block`: Block 2. `type_types.block`: Block
3. `type_types.block_quadrant`: Block quadrant
![](../images/type%20block%20graph.png) ![](../images/type%20block%20graph.png)
4. `type_types.separated_block_quadrant`: Separated block quadrant
5. `type_types.block_sextant`: Block sextant
6. `type_types.separated_block_sextant`: Separated block sextant
7. `type_types.block_octant`: Block octant
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. The Braille and block octant types have the highest density of 2×4 pixels per character, while the two block sextant types use 2×3, the two block quadrant types use 2×2 and the block type uses 1×1. This option is only used for plots and graphs. Histograms use 1×8 pixels per character.
The block sextant type requires support for Unicode 13.0, while the separated block quadrant, separated block sextant and block octant types require support for Unicode 16.0.
#### Mark type #### Mark type

View File

@ -74,10 +74,7 @@ class color_types(IntEnum):
bright_white = auto() bright_white = auto()
colors = ("\033[39m", "\033[30m", "\033[31m", "\033[32m", "\033[33m", colors = (39, 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97)
"\033[34m", "\033[35m", "\033[36m", "\033[37m", "\033[90m",
"\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m",
"\033[96m", "\033[97m")
dots = ( dots = (
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
@ -100,19 +97,34 @@ dots = (
"") "")
dotvalues = ((0x1, 0x2, 0x4, 0x40), (0x8, 0x10, 0x20, 0x80)) dotvalues = ((0x1, 0x2, 0x4, 0x40), (0x8, 0x10, 0x20, 0x80))
blocks = (" ", "", "", "", "", "", "", blocks = ("\xA0", "")
"", "", "", "", "", "", "", "", "")
blockvalues = ((4, 1), (8, 2))
bars = (" ", "", "", "", "", "", "", "", "") blocks_quadrant = ("\xA0", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "")
separated_blocks_quadrant = ("\xA0", "𜰡", "𜰢", "𜰣", "𜰤", "𜰥", "𜰦", "𜰧", "𜰨", "𜰩", "𜰪", "𜰫", "𜰬", "𜰭", "𜰮", "𜰯")
blocks_sextant = ("\xA0", "🬀", "🬁", "🬂", "🬃", "🬄", "🬅", "🬆", "🬇", "🬈", "🬉", "🬊", "🬋", "🬌", "🬍", "🬎", "🬏", "🬐", "🬑", "🬒", "🬓", "", "🬔", "🬕", "🬖", "🬗", "🬘", "🬙", "🬚", "🬛", "🬜", "🬝", "🬞", "🬟", "🬠", "🬡", "🬢", "🬣", "🬤", "🬥", "🬦", "🬧", "", "🬨", "🬩", "🬪", "🬫", "🬬", "🬭", "🬮", "🬯", "🬰", "🬱", "🬲", "🬳", "🬴", "🬵", "🬶", "🬷", "🬸", "🬹", "🬺", "🬻", "")
separated_blocks_sextant = ("\xA0", "𜹑", "𜹒", "𜹓", "𜹔", "𜹕", "𜹖", "𜹗", "𜹘", "𜹙", "𜹚", "𜹛", "𜹜", "𜹝", "𜹞", "𜹟", "𜹠", "𜹡", "𜹢", "𜹣", "𜹤", "𜹥", "𜹦", "𜹧", "𜹨", "𜹩", "𜹪", "𜹫", "𜹬", "𜹭", "𜹮", "𜹯", "𜹰", "𜹱", "𜹲", "𜹳", "𜹴", "𜹵", "𜹶", "𜹷", "𜹸", "𜹹", "𜹺", "𜹻", "𜹼", "𜹽", "𜹾", "𜹿", "𜺀", "𜺁", "𜺂", "𜺃", "𜺄", "𜺅", "𜺆", "𜺇", "𜺈", "𜺉", "𜺊", "𜺋", "𜺌", "𜺍", "𜺎", "𜺏")
blocks_octant = ("\xA0", "𜺨", "𜺫", "🮂", "𜴀", "", "𜴁", "𜴂", "𜴃", "𜴄", "", "𜴅", "𜴆", "𜴇", "𜴈", "", "𜴉", "𜴊", "𜴋", "𜴌", "🯦", "𜴍", "𜴎", "𜴏", "𜴐", "𜴑", "𜴒", "𜴓", "𜴔", "𜴕", "𜴖", "𜴗", "𜴘", "𜴙", "𜴚", "𜴛", "𜴜", "𜴝", "𜴞", "𜴟", "🯧", "𜴠", "𜴡", "𜴢", "𜴣", "𜴤", "𜴥", "𜴦", "𜴧", "𜴨", "𜴩", "𜴪", "𜴫", "𜴬", "𜴭", "𜴮", "𜴯", "𜴰", "𜴱", "𜴲", "𜴳", "𜴴", "𜴵", "🮅", "𜺣", "𜴶", "𜴷", "𜴸", "𜴹", "𜴺", "𜴻", "𜴼", "𜴽", "𜴾", "𜴿", "𜵀", "𜵁", "𜵂", "𜵃", "𜵄", "", "𜵅", "𜵆", "𜵇", "𜵈", "", "𜵉", "𜵊", "𜵋", "𜵌", "", "𜵍", "𜵎", "𜵏", "𜵐", "", "𜵑", "𜵒", "𜵓", "𜵔", "𜵕", "𜵖", "𜵗", "𜵘", "𜵙", "𜵚", "𜵛", "𜵜", "𜵝", "𜵞", "𜵟", "𜵠", "𜵡", "𜵢", "𜵣", "𜵤", "𜵥", "𜵦", "𜵧", "𜵨", "𜵩", "𜵪", "𜵫", "𜵬", "𜵭", "𜵮", "𜵯", "𜵰", "𜺠", "𜵱", "𜵲", "𜵳", "𜵴", "𜵵", "𜵶", "𜵷", "𜵸", "𜵹", "𜵺", "𜵻", "𜵼", "𜵽", "𜵾", "𜵿", "𜶀", "𜶁", "𜶂", "𜶃", "𜶄", "𜶅", "𜶆", "𜶇", "𜶈", "𜶉", "𜶊", "𜶋", "𜶌", "𜶍", "𜶎", "𜶏", "", "𜶐", "𜶑", "𜶒", "𜶓", "", "𜶔", "𜶕", "𜶖", "𜶗", "", "𜶘", "𜶙", "𜶚", "𜶛", "", "𜶜", "𜶝", "𜶞", "𜶟", "𜶠", "𜶡", "𜶢", "𜶣", "𜶤", "𜶥", "𜶦", "𜶧", "𜶨", "𜶩", "𜶪", "𜶫", "", "𜶬", "𜶭", "𜶮", "𜶯", "𜶰", "𜶱", "𜶲", "𜶳", "𜶴", "𜶵", "𜶶", "𜶷", "𜶸", "𜶹", "𜶺", "𜶻", "𜶼", "𜶽", "𜶾", "𜶿", "𜷀", "𜷁", "𜷂", "𜷃", "𜷄", "𜷅", "𜷆", "𜷇", "𜷈", "𜷉", "𜷊", "𜷋", "𜷌", "𜷍", "𜷎", "𜷏", "𜷐", "𜷑", "𜷒", "𜷓", "𜷔", "𜷕", "𜷖", "𜷗", "𜷘", "𜷙", "𜷚", "", "𜷛", "𜷜", "𜷝", "𜷞", "", "𜷟", "𜷠", "𜷡", "𜷢", "", "𜷣", "", "𜷤", "𜷥", "")
bars = ("\xA0", "", "", "", "", "", "", "", "")
class type_types(IntEnum): class type_types(IntEnum):
braille = 0 braille = 0
block = auto() block = auto()
block_quadrant = auto()
separated_block_quadrant = auto()
block_sextant = auto()
separated_block_sextant = auto()
block_octant = auto()
histogram = auto() # Set automatically by the histogram() function histogram = auto() # Set automatically by the histogram() function
atype_types = (type_types.braille, type_types.block) atype_types = (type_types.braille, type_types.block, type_types.block_quadrant, type_types.separated_block_quadrant, type_types.block_sextant, type_types.separated_block_sextant, type_types.block_octant)
densities = ((4, 2), (1, 1), (2, 2), (2, 2), (3, 2), (3, 2), (4, 2), (8, 1))
marks = (((0, 0),), ((0, 1), (-1, 0), (0, 0), (1, 0), (0, -1)), marks = (((0, 0),), ((0, 1), (-1, 0), (0, 0), (1, 0), (0, -1)),
((-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1))) ((-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1)))
@ -298,6 +310,10 @@ def outputlabel(label: float, units: units_types) -> Tuple[int, str]:
return length, strm return length, strm
def outputcolor(color: color_types) -> str:
return f"\033[{colors[color]}m"
def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, array: List[List[int]], border: bool = False, axis: bool = True, axislabel: bool = True, axistick: bool = True, axisunitslabel: bool = True, xunits: units_types = units_types.fracts, yunits: units_types = units_types.fracts, atype: type_types = type_types.braille, style: style_types = style_types.light, title: Optional[str] = None, file: TextIO = sys.stdout, check: bool = True) -> int: def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, array: List[List[int]], border: bool = False, axis: bool = True, axislabel: bool = True, axistick: bool = True, axisunitslabel: bool = True, xunits: units_types = units_types.fracts, yunits: units_types = units_types.fracts, atype: type_types = type_types.braille, style: style_types = style_types.light, title: Optional[str] = None, file: TextIO = sys.stdout, check: bool = True) -> int:
"""Output graph.""" """Output graph."""
if not array: if not array:
@ -311,8 +327,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
w = shutil.get_terminal_size() w = shutil.get_terminal_size()
ai = 8 if atype == type_types.histogram else 2 if atype == type_types.block else 4 ai, aj = densities[atype]
aj = 1 if atype == type_types.histogram else 2
aheight = height // ai aheight = height // ai
awidth = width // aj awidth = width // aj
@ -485,13 +500,13 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
for l in range(min(ai, height - i)): for l in range(min(ai, height - i)):
value = array[j + k][i + l] value = array[j + k][i + l]
if value: if value:
if atype == type_types.histogram: if atype == type_types.braille:
dot += dotvalues[k][l]
elif atype in {type_types.block, type_types.block_quadrant, type_types.separated_block_quadrant, type_types.block_sextant, type_types.separated_block_sextant, type_types.block_octant}:
dot += 1 << (l * aj + k)
elif atype == type_types.histogram:
if not dot: if not dot:
dot = (len(bars) - l) - 1 dot = (len(bars) - l) - 1
elif atype == type_types.block:
dot += blockvalues[k][l]
else:
dot += dotvalues[k][l]
if color: if color:
if value and color != value: if value and color != value:
color = 1 color = 1
@ -502,12 +517,27 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
color -= 1 color -= 1
if color: if color:
strm += colors[color] strm += outputcolor(color)
strm += bars[dot] if atype == type_types.histogram else blocks[dot] if atype == type_types.block else dots[dot] if atype == type_types.braille:
strm += dots[dot]
elif atype == type_types.block:
strm += blocks[dot]
elif atype == type_types.block_quadrant:
strm += blocks_quadrant[dot]
elif atype == type_types.separated_block_quadrant:
strm += separated_blocks_quadrant[dot]
elif atype == type_types.block_sextant:
strm += blocks_sextant[dot]
elif atype == type_types.separated_block_sextant:
strm += separated_blocks_sextant[dot]
elif atype == type_types.block_octant:
strm += blocks_octant[dot]
elif atype == type_types.histogram:
strm += bars[dot]
if color: if color:
strm += colors[0] strm += outputcolor(color_types.default)
j += aj j += aj
@ -534,27 +564,26 @@ def histogram(height: int, width: int, xmin: float, xmax: float, ymin: float, ym
w = shutil.get_terminal_size() w = shutil.get_terminal_size()
if not height: if not height:
height = w.lines * 4 height = w.lines
if not width: if not width:
width = w.columns * 2 width = w.columns
if check: if check:
aheight = height // 4 if height > w.lines:
awidth = width // 2
if aheight > w.lines:
print( print(
f"The height of the graph ({aheight}) is greater then the height of the terminal ({w.lines}).", file=sys.stderr) f"The height of the graph ({height}) is greater then the height of the terminal ({w.lines}).", file=sys.stderr)
return 1 return 1
if awidth > w.columns: if width > w.columns:
print( print(
f"The width of the graph ({awidth}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr) f"The width of the graph ({width}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr)
return 1 return 1
height *= 2 ai, aj = densities[type_types.histogram]
width //= 2
height *= ai
width *= aj
if not xmin and not xmax: if not xmin and not xmax:
xmin = min(aarray) xmin = min(aarray)
@ -609,27 +638,26 @@ def plots(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
w = shutil.get_terminal_size() w = shutil.get_terminal_size()
if not height: if not height:
height = w.lines * 4 height = w.lines
if not width: if not width:
width = w.columns * 2 width = w.columns
if check: if check:
aheight = height // 4 if height > w.lines:
awidth = width // 2
if aheight > w.lines:
print( print(
f"The height of the graph ({aheight}) is greater then the height of the terminal ({w.lines}).", file=sys.stderr) f"The height of the graph ({height}) is greater then the height of the terminal ({w.lines}).", file=sys.stderr)
return 1 return 1
if awidth > w.columns: if width > w.columns:
print( print(
f"The width of the graph ({awidth}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr) f"The width of the graph ({width}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr)
return 1 return 1
if atype == type_types.block: ai, aj = densities[atype]
height //= 2
height *= ai
width *= aj
if not xmin and not xmax: if not xmin and not xmax:
xmin = min(x for aarray in aarrays for x, y in aarray) xmin = min(x for aarray in aarrays for x, y in aarray)
@ -689,27 +717,26 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym
w = shutil.get_terminal_size() w = shutil.get_terminal_size()
if not height: if not height:
height = w.lines * 4 height = w.lines
if not width: if not width:
width = w.columns * 2 width = w.columns
if check: if check:
aheight = height // 4 if height > w.lines:
awidth = width // 2
if aheight > w.lines:
print( print(
f"The height of the graph ({aheight}) is greater then the height of the terminal ({w.lines}).", file=sys.stderr) f"The height of the graph ({height}) is greater then the height of the terminal ({w.lines}).", file=sys.stderr)
return 1 return 1
if awidth > w.columns: if height > w.columns:
print( print(
f"The width of the graph ({awidth}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr) f"The width of the graph ({height}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr)
return 1 return 1
if atype == type_types.block: ai, aj = densities[atype]
height //= 2
height *= ai
width *= aj
if xmin >= xmax: if xmin >= xmax:
print("xmin must be less than xmax.", file=sys.stderr) print("xmin must be less than xmax.", file=sys.stderr)

View File

@ -107,8 +107,8 @@ for style in tables.style_types:
tables.functions(xmin, xmax, xstep, [ tables.functions(xmin, xmax, xstep, [
lambda x: 2 * x, lambda x: x ** 2], headerrow=True, style=style) lambda x: 2 * x, lambda x: x ** 2], headerrow=True, style=style)
height = 160 height = 40
width = 160 width = 80
xmin = -20 xmin = -20
xmax = 20 xmax = 20