diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcc1d69..2101b9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ jobs: name: Linux C++ runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.cxx == 'clang++' && matrix.os == 'ubuntu-20.04' }} strategy: matrix: 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 ruff - 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 Python: diff --git a/README.md b/README.md index fcbf5a2..6cc5c5a 100644 --- a/README.md +++ b/README.md @@ -544,7 +544,7 @@ 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). +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 @@ -557,8 +557,8 @@ using namespace std; int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -588,8 +588,8 @@ using namespace std; int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -623,8 +623,8 @@ using namespace std; int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -654,8 +654,8 @@ using namespace std; int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -696,8 +696,8 @@ double afunction(double x) int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -719,8 +719,8 @@ using namespace std; int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -759,8 +759,8 @@ double function2(double x) int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -787,8 +787,8 @@ using namespace std; int main() { - size_t height = 160; - size_t width = 160; + size_t height = 40; + size_t width = 80; long double xmin = -20; long double xmax = 20; @@ -874,10 +874,16 @@ Values: 1. `type_braille`: Braille (default) ![](images/type%20braille%20graph.png) -2. `type_block`: Block +2. `type_block_quadrant`: Block quadrant ![](images/type%20block%20graph.png) +3. `type_separated_block_quadrant`: Separated block quadrant +4. `type_block_sextant`: Block sextant +5. `type_separated_block_sextant`: Separated block sextant +6. `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 and the two block quadrant types use 2×2. 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 diff --git a/graphs.cpp b/graphs.cpp index 7e5d4ca..6328096 100644 --- a/graphs.cpp +++ b/graphs.cpp @@ -45,8 +45,8 @@ constexpr long double function5(long double x) int main() { - const size_t height = 160; - const size_t width = 160; + const size_t height = 40; + const size_t width = 80; const long double xmin = -20; const long double xmax = 20; diff --git a/graphs.hpp b/graphs.hpp index d32855d..cdc01d9 100644 --- a/graphs.hpp +++ b/graphs.hpp @@ -77,24 +77,37 @@ 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}; - 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 int dotvalues[][4] = {{0x1, 0x2, 0x4, 0x40}, {0x8, 0x10, 0x20, 0x80}}; - const char *const blocks[] = {" ", "▖", "▗", "▄", "▘", "▌", "▚", "▙", "▝", "▞", "▐", "▟", "▀", "▛", "▜", "█"}; - const int blockvalues[][2] = {{4, 1}, {8, 2}}; + const char *const blocks_quadrant[] = {" ", "▘", "▝", "▀", "▖", "▌", "▞", "▛", "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█"}; + + const char *const separated_blocks_quadrant[] = {" ", "𜰡", "𜰢", "𜰣", "𜰤", "𜰥", "𜰦", "𜰧", "𜰨", "𜰩", "𜰪", "𜰫", "𜰬", "𜰭", "𜰮", "𜰯"}; + + const char *const blocks_sextant[] = {" ", "🬀", "🬁", "🬂", "🬃", "🬄", "🬅", "🬆", "🬇", "🬈", "🬉", "🬊", "🬋", "🬌", "🬍", "🬎", "🬏", "🬐", "🬑", "🬒", "🬓", "▌", "🬔", "🬕", "🬖", "🬗", "🬘", "🬙", "🬚", "🬛", "🬜", "🬝", "🬞", "🬟", "🬠", "🬡", "🬢", "🬣", "🬤", "🬥", "🬦", "🬧", "▐", "🬨", "🬩", "🬪", "🬫", "🬬", "🬭", "🬮", "🬯", "🬰", "🬱", "🬲", "🬳", "🬴", "🬵", "🬶", "🬷", "🬸", "🬹", "🬺", "🬻", "█"}; + + const char *const separated_blocks_sextant[] = {" ", "𜹑", "𜹒", "𜹓", "𜹔", "𜹕", "𜹖", "𜹗", "𜹘", "𜹙", "𜹚", "𜹛", "𜹜", "𜹝", "𜹞", "𜹟", "𜹠", "𜹡", "𜹢", "𜹣", "𜹤", "𜹥", "𜹦", "𜹧", "𜹨", "𜹩", "𜹪", "𜹫", "𜹬", "𜹭", "𜹮", "𜹯", "𜹰", "𜹱", "𜹲", "𜹳", "𜹴", "𜹵", "𜹶", "𜹷", "𜹸", "𜹹", "𜹺", "𜹻", "𜹼", "𜹽", "𜹾", "𜹿", "𜺀", "𜺁", "𜺂", "𜺃", "𜺄", "𜺅", "𜺆", "𜺇", "𜺈", "𜺉", "𜺊", "𜺋", "𜺌", "𜺍", "𜺎", "𜺏"}; + + const char *const blocks_octant[] = {" ", "𜺨", "𜺫", "🮂", "𜴀", "▘", "𜴁", "𜴂", "𜴃", "𜴄", "▝", "𜴅", "𜴆", "𜴇", "𜴈", "▀", "𜴉", "𜴊", "𜴋", "𜴌", "🯦", "𜴍", "𜴎", "𜴏", "𜴐", "𜴑", "𜴒", "𜴓", "𜴔", "𜴕", "𜴖", "𜴗", "𜴘", "𜴙", "𜴚", "𜴛", "𜴜", "𜴝", "𜴞", "𜴟", "🯧", "𜴠", "𜴡", "𜴢", "𜴣", "𜴤", "𜴥", "𜴦", "𜴧", "𜴨", "𜴩", "𜴪", "𜴫", "𜴬", "𜴭", "𜴮", "𜴯", "𜴰", "𜴱", "𜴲", "𜴳", "𜴴", "𜴵", "🮅", "𜺣", "𜴶", "𜴷", "𜴸", "𜴹", "𜴺", "𜴻", "𜴼", "𜴽", "𜴾", "𜴿", "𜵀", "𜵁", "𜵂", "𜵃", "𜵄", "▖", "𜵅", "𜵆", "𜵇", "𜵈", "▌", "𜵉", "𜵊", "𜵋", "𜵌", "▞", "𜵍", "𜵎", "𜵏", "𜵐", "▛", "𜵑", "𜵒", "𜵓", "𜵔", "𜵕", "𜵖", "𜵗", "𜵘", "𜵙", "𜵚", "𜵛", "𜵜", "𜵝", "𜵞", "𜵟", "𜵠", "𜵡", "𜵢", "𜵣", "𜵤", "𜵥", "𜵦", "𜵧", "𜵨", "𜵩", "𜵪", "𜵫", "𜵬", "𜵭", "𜵮", "𜵯", "𜵰", "𜺠", "𜵱", "𜵲", "𜵳", "𜵴", "𜵵", "𜵶", "𜵷", "𜵸", "𜵹", "𜵺", "𜵻", "𜵼", "𜵽", "𜵾", "𜵿", "𜶀", "𜶁", "𜶂", "𜶃", "𜶄", "𜶅", "𜶆", "𜶇", "𜶈", "𜶉", "𜶊", "𜶋", "𜶌", "𜶍", "𜶎", "𜶏", "▗", "𜶐", "𜶑", "𜶒", "𜶓", "▚", "𜶔", "𜶕", "𜶖", "𜶗", "▐", "𜶘", "𜶙", "𜶚", "𜶛", "▜", "𜶜", "𜶝", "𜶞", "𜶟", "𜶠", "𜶡", "𜶢", "𜶣", "𜶤", "𜶥", "𜶦", "𜶧", "𜶨", "𜶩", "𜶪", "𜶫", "▂", "𜶬", "𜶭", "𜶮", "𜶯", "𜶰", "𜶱", "𜶲", "𜶳", "𜶴", "𜶵", "𜶶", "𜶷", "𜶸", "𜶹", "𜶺", "𜶻", "𜶼", "𜶽", "𜶾", "𜶿", "𜷀", "𜷁", "𜷂", "𜷃", "𜷄", "𜷅", "𜷆", "𜷇", "𜷈", "𜷉", "𜷊", "𜷋", "𜷌", "𜷍", "𜷎", "𜷏", "𜷐", "𜷑", "𜷒", "𜷓", "𜷔", "𜷕", "𜷖", "𜷗", "𜷘", "𜷙", "𜷚", "▄", "𜷛", "𜷜", "𜷝", "𜷞", "▙", "𜷟", "𜷠", "𜷡", "𜷢", "▟", "𜷣", "▆", "𜷤", "𜷥", "█"}; const char *const bars[] = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}; enum type_type { type_braille, - 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 }; - enum type_type const type_types[] = {type_braille, type_block /* , type_histogram */}; + enum type_type const type_types[] = {type_braille, 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}, {2, 2}, {2, 2}, {3, 2}, {3, 2}, {4, 2}, {8, 1}}; enum plot_type { @@ -104,7 +117,7 @@ namespace graphs 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>> 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 { @@ -378,8 +391,9 @@ namespace graphs 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("")); switch (units) @@ -420,11 +434,21 @@ namespace graphs break; } - const size_t length = strcol(strm.str()); + str = strm.str(); + const size_t length = strcol(str); return length; } + inline string outputcolor(const color_type color) + { + ostringstream strm; + + strm << "\e[" << int(colors[color]) << "m"; + + return strm.str(); + } + // 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> &array, const options &aoptions) { @@ -437,7 +461,6 @@ namespace graphs const bool axistick = aoptions.axistick; const bool axisunitslabel = aoptions.axisunitslabel; const type_type type = aoptions.type; - const char *const title = aoptions.title; if (!height) return 1; @@ -448,9 +471,7 @@ namespace graphs struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - const size_t ai = type == type_histogram ? 8 : type == type_block ? 2 - : 4; - const size_t aj = type == type_histogram ? 1 : 2; + const auto [ai, aj] = densities[type]; const size_t aheight = height / ai; const size_t awidth = width / aj; @@ -493,8 +514,8 @@ namespace graphs setlocale(LC_ALL, ""); - if (title and title[0] != '\0') - aoptions.ostr << wrap(title, awidth) << '\n'; + if (aoptions.title and aoptions.title[0] != '\0') + aoptions.ostr << wrap(aoptions.title, awidth) << '\n'; const char *const *astyle = styles[aoptions.style]; @@ -515,7 +536,7 @@ namespace graphs 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); - ostringstream ylabelstrm; + string ylabelstr; size_t ylabellength = 0; if (axis and axistick and axisunitslabel and yaxis >= 0 and yaxis <= height) @@ -536,7 +557,7 @@ namespace graphs if (output) { - ylabellength = outputlabel(label, aoptions.yunits, ylabelstrm); + ylabellength = outputlabel(label, aoptions.yunits, ylabelstr); ylabellength *= aj; } } @@ -654,12 +675,12 @@ namespace graphs { output = false; - ostringstream astrm; - size_t length = outputlabel(label, aoptions.xunits, astrm); + string astr; + size_t length = outputlabel(label, aoptions.xunits, astr); 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)) { - strm << astrm.str(); + strm << astr; if (length > aj) j += length - aj; @@ -678,7 +699,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) { - strm << ylabelstrm.str(); + strm << ylabelstr; output = true; if (ylabellength > aj) j += ylabellength - aj; @@ -697,15 +718,23 @@ namespace graphs const unsigned short value = array[j + k][i + l]; if (value) { - if (type == type_histogram) + switch (type) { + case type_braille: + dot += dotvalues[k][l]; + break; + 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) dot = (size(bars) - l) - 1; + break; } - else if (type == type_block) - dot += blockvalues[k][l]; - else - dot += dotvalues[k][l]; } if (color) { @@ -721,13 +750,35 @@ namespace graphs --color; if (color) - strm << colors[color]; + strm << outputcolor(color_type(color)); - strm << (type == type_histogram ? bars[dot] : type == type_block ? blocks[dot] - : dots[dot]); + switch (type) + { + case type_braille: + strm << dots[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) - strm << colors[0]; + strm << outputcolor(color_default); } } @@ -766,31 +817,30 @@ namespace graphs ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); if (!height) - height = w.ws_row * 4; + height = w.ws_row; if (!width) - width = w.ws_col * 2; + width = w.ws_col; if (aoptions.check) { - const size_t aheight = height / 4; - const size_t awidth = width / 2; - - if (aheight > w.ws_row) + if (height > 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; } - 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; } } - height *= 2; - width /= 2; + const auto [ai, aj] = densities[type_histogram]; + + height *= ai; + width *= aj; if (!xmin and !xmax) { @@ -890,31 +940,30 @@ namespace graphs ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); if (!height) - height = w.ws_row * 4; + height = w.ws_row; if (!width) - width = w.ws_col * 2; + width = w.ws_col; if (aoptions.check) { - const size_t aheight = height / 4; - const size_t awidth = width / 2; - - if (aheight > w.ws_row) + if (height > 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; } - 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; } } - if (aoptions.type == type_block) - height /= 2; + const auto [ai, aj] = densities[aoptions.type]; + + height *= ai; + width *= aj; if (!xmin and !xmax) { @@ -1029,31 +1078,30 @@ namespace graphs ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); if (!height) - height = w.ws_row * 4; + height = w.ws_row; if (!width) - width = w.ws_col * 2; + width = w.ws_col; if (aoptions.check) { - const size_t aheight = height / 4; - const size_t awidth = width / 2; - - if (aheight > w.ws_row) + if (height > 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; } - 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; } } - if (aoptions.type == type_block) - height /= 2; + const auto [ai, aj] = densities[aoptions.type]; + + height *= ai; + width *= aj; if (xmin >= xmax) { diff --git a/python/README.md b/python/README.md index 9e193ed..b940813 100644 --- a/python/README.md +++ b/python/README.md @@ -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`. -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 ```py import graphs -height = 160 -width = 160 +height = 40 +width = 80 xmin = -20 xmax = 20 @@ -302,8 +302,8 @@ If `xmin` and `xmax` are both `0`, they will be set to the respective minimum an ```py import graphs -height = 160 -width = 160 +height = 40 +width = 80 xmin = -20 xmax = 20 @@ -329,8 +329,8 @@ import graphs def afunction(x): return x + 1 -height = 160 -width = 160 +height = 40 +width = 80 xmin = -20 xmax = 20 @@ -347,8 +347,8 @@ graphs.function(height, width, xmin, xmax, ymin, ymax, afunction) ```py import graphs -height = 160 -width = 160 +height = 40 +width = 80 xmin = -20 xmax = 20 @@ -373,8 +373,8 @@ def function1(x): def function2(x): return x ** 2 -height = 160 -width = 160 +height = 40 +width = 80 xmin = -20 xmax = 20 @@ -394,8 +394,8 @@ graphs.functions(height, width, xmin, xmax, ymin, ymax, functions) ```py import graphs -height = 160 -width = 160 +height = 40 +width = 80 xmin = -20 xmax = 20 @@ -473,10 +473,16 @@ Values: 1. `type_types.braille`: Braille (default) ![](../images/type%20braille%20graph.png) -2. `type_types.block`: Block +2. `type_types.block_quadrant`: Block quadrant ![](../images/type%20block%20graph.png) +3. `type_types.separated_block_quadrant`: Separated block quadrant +4. `type_types.block_sextant`: Block sextant +5. `type_types.separated_block_sextant`: Separated block sextant +6. `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 and the two block quadrant types use 2×2. 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 diff --git a/python/graphs.py b/python/graphs.py index 6f94d1c..07cb153 100644 --- a/python/graphs.py +++ b/python/graphs.py @@ -74,10 +74,7 @@ class color_types(IntEnum): bright_white = auto() -colors = ("\033[39m", "\033[30m", "\033[31m", "\033[32m", "\033[33m", - "\033[34m", "\033[35m", "\033[36m", "\033[37m", "\033[90m", - "\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m", - "\033[96m", "\033[97m") +colors = (39, 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97) dots = ( "⠀", "⠁", "⠂", "⠃", "⠄", "⠅", "⠆", "⠇", "⠈", "⠉", "⠊", "⠋", "⠌", "⠍", "⠎", @@ -100,19 +97,31 @@ class color_types(IntEnum): "⣿") dotvalues = ((0x1, 0x2, 0x4, 0x40), (0x8, 0x10, 0x20, 0x80)) -blocks = (" ", "▖", "▗", "▄", "▘", "▌", "▚", - "▙", "▝", "▞", "▐", "▟", "▀", "▛", "▜", "█") -blockvalues = ((4, 1), (8, 2)) +blocks_quadrant = (" ", "▘", "▝", "▀", "▖", "▌", "▞", "▛", "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█") + +separated_blocks_quadrant = (" ", "𜰡", "𜰢", "𜰣", "𜰤", "𜰥", "𜰦", "𜰧", "𜰨", "𜰩", "𜰪", "𜰫", "𜰬", "𜰭", "𜰮", "𜰯") + +blocks_sextant = (" ", "🬀", "🬁", "🬂", "🬃", "🬄", "🬅", "🬆", "🬇", "🬈", "🬉", "🬊", "🬋", "🬌", "🬍", "🬎", "🬏", "🬐", "🬑", "🬒", "🬓", "▌", "🬔", "🬕", "🬖", "🬗", "🬘", "🬙", "🬚", "🬛", "🬜", "🬝", "🬞", "🬟", "🬠", "🬡", "🬢", "🬣", "🬤", "🬥", "🬦", "🬧", "▐", "🬨", "🬩", "🬪", "🬫", "🬬", "🬭", "🬮", "🬯", "🬰", "🬱", "🬲", "🬳", "🬴", "🬵", "🬶", "🬷", "🬸", "🬹", "🬺", "🬻", "█") + +separated_blocks_sextant = (" ", "𜹑", "𜹒", "𜹓", "𜹔", "𜹕", "𜹖", "𜹗", "𜹘", "𜹙", "𜹚", "𜹛", "𜹜", "𜹝", "𜹞", "𜹟", "𜹠", "𜹡", "𜹢", "𜹣", "𜹤", "𜹥", "𜹦", "𜹧", "𜹨", "𜹩", "𜹪", "𜹫", "𜹬", "𜹭", "𜹮", "𜹯", "𜹰", "𜹱", "𜹲", "𜹳", "𜹴", "𜹵", "𜹶", "𜹷", "𜹸", "𜹹", "𜹺", "𜹻", "𜹼", "𜹽", "𜹾", "𜹿", "𜺀", "𜺁", "𜺂", "𜺃", "𜺄", "𜺅", "𜺆", "𜺇", "𜺈", "𜺉", "𜺊", "𜺋", "𜺌", "𜺍", "𜺎", "𜺏") + +blocks_octant = (" ", "𜺨", "𜺫", "🮂", "𜴀", "▘", "𜴁", "𜴂", "𜴃", "𜴄", "▝", "𜴅", "𜴆", "𜴇", "𜴈", "▀", "𜴉", "𜴊", "𜴋", "𜴌", "🯦", "𜴍", "𜴎", "𜴏", "𜴐", "𜴑", "𜴒", "𜴓", "𜴔", "𜴕", "𜴖", "𜴗", "𜴘", "𜴙", "𜴚", "𜴛", "𜴜", "𜴝", "𜴞", "𜴟", "🯧", "𜴠", "𜴡", "𜴢", "𜴣", "𜴤", "𜴥", "𜴦", "𜴧", "𜴨", "𜴩", "𜴪", "𜴫", "𜴬", "𜴭", "𜴮", "𜴯", "𜴰", "𜴱", "𜴲", "𜴳", "𜴴", "𜴵", "🮅", "𜺣", "𜴶", "𜴷", "𜴸", "𜴹", "𜴺", "𜴻", "𜴼", "𜴽", "𜴾", "𜴿", "𜵀", "𜵁", "𜵂", "𜵃", "𜵄", "▖", "𜵅", "𜵆", "𜵇", "𜵈", "▌", "𜵉", "𜵊", "𜵋", "𜵌", "▞", "𜵍", "𜵎", "𜵏", "𜵐", "▛", "𜵑", "𜵒", "𜵓", "𜵔", "𜵕", "𜵖", "𜵗", "𜵘", "𜵙", "𜵚", "𜵛", "𜵜", "𜵝", "𜵞", "𜵟", "𜵠", "𜵡", "𜵢", "𜵣", "𜵤", "𜵥", "𜵦", "𜵧", "𜵨", "𜵩", "𜵪", "𜵫", "𜵬", "𜵭", "𜵮", "𜵯", "𜵰", "𜺠", "𜵱", "𜵲", "𜵳", "𜵴", "𜵵", "𜵶", "𜵷", "𜵸", "𜵹", "𜵺", "𜵻", "𜵼", "𜵽", "𜵾", "𜵿", "𜶀", "𜶁", "𜶂", "𜶃", "𜶄", "𜶅", "𜶆", "𜶇", "𜶈", "𜶉", "𜶊", "𜶋", "𜶌", "𜶍", "𜶎", "𜶏", "▗", "𜶐", "𜶑", "𜶒", "𜶓", "▚", "𜶔", "𜶕", "𜶖", "𜶗", "▐", "𜶘", "𜶙", "𜶚", "𜶛", "▜", "𜶜", "𜶝", "𜶞", "𜶟", "𜶠", "𜶡", "𜶢", "𜶣", "𜶤", "𜶥", "𜶦", "𜶧", "𜶨", "𜶩", "𜶪", "𜶫", "▂", "𜶬", "𜶭", "𜶮", "𜶯", "𜶰", "𜶱", "𜶲", "𜶳", "𜶴", "𜶵", "𜶶", "𜶷", "𜶸", "𜶹", "𜶺", "𜶻", "𜶼", "𜶽", "𜶾", "𜶿", "𜷀", "𜷁", "𜷂", "𜷃", "𜷄", "𜷅", "𜷆", "𜷇", "𜷈", "𜷉", "𜷊", "𜷋", "𜷌", "𜷍", "𜷎", "𜷏", "𜷐", "𜷑", "𜷒", "𜷓", "𜷔", "𜷕", "𜷖", "𜷗", "𜷘", "𜷙", "𜷚", "▄", "𜷛", "𜷜", "𜷝", "𜷞", "▙", "𜷟", "𜷠", "𜷡", "𜷢", "▟", "𜷣", "▆", "𜷤", "𜷥", "█") bars = (" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█") class type_types(IntEnum): braille = 0 - 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 -atype_types = (type_types.braille, type_types.block) +atype_types = (type_types.braille, 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), (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)), ((-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1))) @@ -298,6 +307,10 @@ def outputlabel(label: float, units: units_types) -> Tuple[int, str]: 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: """Output graph.""" if not array: @@ -311,8 +324,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: w = shutil.get_terminal_size() - ai = 8 if atype == type_types.histogram else 2 if atype == type_types.block else 4 - aj = 1 if atype == type_types.histogram else 2 + ai, aj = densities[atype] aheight = height // ai awidth = width // aj @@ -485,13 +497,13 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: for l in range(min(ai, height - i)): value = array[j + k][i + l] if value: - if atype == type_types.histogram: + if atype == type_types.braille: + dot += dotvalues[k][l] + elif atype in {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: dot = (len(bars) - l) - 1 - elif atype == type_types.block: - dot += blockvalues[k][l] - else: - dot += dotvalues[k][l] if color: if value and color != value: color = 1 @@ -502,12 +514,25 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: color -= 1 if color: - strm += colors[color] - - strm += bars[dot] if atype == type_types.histogram else blocks[dot] if atype == type_types.block else dots[dot] + strm += outputcolor(color) + + if atype == type_types.braille: + strm += dots[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: - strm += colors[0] + strm += outputcolor(color_types.default) j += aj @@ -534,27 +559,26 @@ def histogram(height: int, width: int, xmin: float, xmax: float, ymin: float, ym w = shutil.get_terminal_size() if not height: - height = w.lines * 4 + height = w.lines if not width: - width = w.columns * 2 + width = w.columns if check: - aheight = height // 4 - awidth = width // 2 - - if aheight > w.lines: + if height > w.lines: 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 - if awidth > w.columns: + if width > w.columns: 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 - height *= 2 - width //= 2 + ai, aj = densities[type_types.histogram] + + height *= ai + width *= aj if not xmin and not xmax: xmin = min(aarray) @@ -609,27 +633,26 @@ def plots(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: w = shutil.get_terminal_size() if not height: - height = w.lines * 4 + height = w.lines if not width: - width = w.columns * 2 + width = w.columns if check: - aheight = height // 4 - awidth = width // 2 - - if aheight > w.lines: + if height > w.lines: 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 - if awidth > w.columns: + if width > w.columns: 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 - if atype == type_types.block: - height //= 2 + ai, aj = densities[atype] + + height *= ai + width *= aj if not xmin and not xmax: xmin = min(x for aarray in aarrays for x, y in aarray) @@ -689,27 +712,26 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym w = shutil.get_terminal_size() if not height: - height = w.lines * 4 + height = w.lines if not width: - width = w.columns * 2 + width = w.columns if check: - aheight = height // 4 - awidth = width // 2 - - if aheight > w.lines: + if height > w.lines: 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 - if awidth > w.columns: + if height > w.columns: 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 - if atype == type_types.block: - height //= 2 + ai, aj = densities[atype] + + height *= ai + width *= aj if xmin >= xmax: print("xmin must be less than xmax.", file=sys.stderr) diff --git a/python/test.py b/python/test.py index 5825cf5..64c58df 100644 --- a/python/test.py +++ b/python/test.py @@ -107,8 +107,8 @@ def function2(x: float) -> float: tables.functions(xmin, xmax, xstep, [ lambda x: 2 * x, lambda x: x ** 2], headerrow=True, style=style) -height = 160 -width = 160 +height = 40 +width = 80 xmin = -20 xmax = 20