From 124004c87cc00e0a1336569d926597bb9d643dd7 Mon Sep 17 00:00:00 2001 From: nick black Date: Fri, 14 Feb 2020 05:14:04 -0500 Subject: [PATCH] add nplane_polyfill_yx() #340 --- README.md | 9 ++++ debian/libnotcurses1.symbols | 1 + doc/man/man3/notcurses_lines.3.md | 2 + include/notcurses.h | 6 +++ python/setup.py | 1 - python/src/notcurses/build_notcurses.py | 1 + src/lib/notcurses.c | 66 ++++++++++++++++++++++++- tests/fade.cpp | 1 - tests/fills.cpp | 59 ++++++++++++++++++++++ tools/setup.py.in | 1 - 10 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 tests/fills.cpp diff --git a/README.md b/README.md index e6d09f60c..6db4a3b3d 100644 --- a/README.md +++ b/README.md @@ -1129,6 +1129,15 @@ ncplane_double_box_sized(struct ncplane* n, uint32_t attr, uint64_t channels, } ``` +Similarly, areas can be filled with a cell. + +```c +// The specified coordinate must not currently have a glyph, or it is an error. +// Otherwise, that coordinate, and all cardinally-connected glyphless cells, +// will have 'c' written to them. +int ncplane_polyfill_yx(struct ncplane* n, int y, int x, const cell* c); +``` + My 14 year-old self would never forgive me if we didn't have sweet palette fades. ```c diff --git a/debian/libnotcurses1.symbols b/debian/libnotcurses1.symbols index 308a4ab48..7c9a38136 100644 --- a/debian/libnotcurses1.symbols +++ b/debian/libnotcurses1.symbols @@ -8,6 +8,7 @@ libnotcurses.so.1 libnotcurses1 #MINVER# ncblit_rgba@Base 1.1.8 ncdirect_bg@Base 1.1.8 ncdirect_bg_rgb8@Base 1.1.8 + ncdirect_clear@Base 1.1.9 ncdirect_fg@Base 1.1.8 ncdirect_fg_rgb8@Base 1.1.8 ncdirect_stop@Base 1.1.8 diff --git a/doc/man/man3/notcurses_lines.3.md b/doc/man/man3/notcurses_lines.3.md index 83677fa4f..813f53b83 100644 --- a/doc/man/man3/notcurses_lines.3.md +++ b/doc/man/man3/notcurses_lines.3.md @@ -57,6 +57,8 @@ ncplane_box_sized(struct ncplane* n, const cell* ul, const cell* ur, **static inline int ncplane_double_box_sized(struct ncplane* n, uint32_t attr, uint64_t channels, int ylen, int xlen, unsigned ctlword);** +**int ncplane_polyfill_yx(struct ncplane* n, int y, int x, const cell* c);** + # DESCRIPTION diff --git a/include/notcurses.h b/include/notcurses.h index 5a6fb82f1..7a0762d42 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -940,6 +940,12 @@ ncplane_perimeter(struct ncplane* n, const cell* ul, const cell* ur, return ncplane_box_sized(n, ul, ur, ll, lr, hline, vline, dimy, dimx, ctlword); } +// Starting at the specified coordinate, if it has no glyph, 'c' is copied into +// it. We do the same to all cardinally-connected glyphless cells, filling in +// everything behind a boundary. Returns the number of cells polyfilled. An +// invalid initial y, x is an error. +API int ncplane_polyfill_yx(struct ncplane* n, int y, int x, const cell* c); + // Erase every cell in the ncplane, resetting all attributes to normal, all // colors to the default color, and all cells to undrawn. All cells associated // with this ncplane is invalidated, and must not be used after the call, diff --git a/python/setup.py b/python/setup.py index 610adb2c5..7bec66140 100644 --- a/python/setup.py +++ b/python/setup.py @@ -9,7 +9,6 @@ setup( version="1.1.8", packages=['notcurses'], package_dir={'': 'src'}, - py_modules=['notcurses'], author="Nick Black", author_email="nickblack@linux.com", description="Blingful TUI construction library (python bindings)", diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index f4c2d40f0..e28730827 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -352,6 +352,7 @@ struct nctablet* ncreel_prev(struct ncreel* pr); int ncreel_destroy(struct ncreel* pr); void* nctablet_userptr(struct nctablet* t); struct ncplane* nctablet_ncplane(struct nctablet* t); +int ncplane_polyfill_yx(struct ncplane* n, int y, int x, const cell* c); """) if __name__ == "__main__": diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 72c1f970a..37b08e674 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -191,14 +191,21 @@ int ncplane_at_cursor(ncplane* n, cell* c){ return cell_duplicate(n, c, &n->fb[nfbcellidx(n, n->y, n->x)]); } -int ncplane_at_yx(ncplane* n, int y, int x, cell* c){ +static inline int +ncplane_at_yx_locked(ncplane* n, int y, int x, cell* c){ int ret = -1; - pthread_mutex_lock(&n->nc->lock); if(y < n->leny && x < n->lenx){ if(y >= 0 && x >= 0){ ret = cell_duplicate(n, c, &n->fb[nfbcellidx(n, y, x)]); } } + return ret; +} + +int ncplane_at_yx(ncplane* n, int y, int x, cell* c){ + int ret = -1; + pthread_mutex_lock(&n->nc->lock); + ret = ncplane_at_yx_locked(n, y, x, c); pthread_mutex_unlock(&n->nc->lock); return ret; } @@ -1934,3 +1941,58 @@ void ncplane_greyscale(ncplane *n){ } ncplane_unlock(n); } + +// if this is not polyfillable cell, we return 0. if it is, we attempt to fill +// it, then recurse out. return -1 on error, or number of cells filled on +// success. so a return of 0 means there's no work to be done here, and N means +// we did some work here, filling everything we could reach. out-of-plane is 0. +static int +ncplane_polyfill_locked(ncplane* n, int y, int x, const cell* c){ +fprintf(stderr, "%d %d\n", y, x); + if(y >= n->leny || x >= n->lenx){ + return 0; // not fillable + } + if(y < 0 || x < 0){ + return 0; // not fillable + } +fprintf(stderr, "%d %d\n", y, x); + cell* cur = &n->fb[nfbcellidx(n, y, x)]; + if(cur->gcluster){ + return 0; // glyph, not polyfillable + } +fprintf(stderr, "%c\n", c->gcluster); + if(cell_duplicate(n, cur, c) < 0){ + return -1; + } + int r, ret = 1; + if((r = ncplane_polyfill_locked(n, y - 1, x, c)) < 0){ + return -1; + } + ret += r; + if((r = ncplane_polyfill_locked(n, y + 1, x, c)) < 0){ + return -1; + } + ret += r; + if((r = ncplane_polyfill_locked(n, y, x - 1, c)) < 0){ + return -1; + } + ret += r; + if((r = ncplane_polyfill_locked(n, y, x + 1, c)) < 0){ + return -1; + } + ret += r; + return ret; +} + +// at the initial step only, invalid y, x is an error, so explicitly check. +int ncplane_polyfill_yx(ncplane* n, int y, int x, const cell* c){ + int ret = -1; + ncplane_lock(n); + if(y < n->leny && x < n->lenx){ + if(y >= 0 && x >= 0){ + ret = ncplane_polyfill_locked(n, y, x, c); + } + } + ncplane_unlock(n); + return ret; +} diff --git a/tests/fade.cpp b/tests/fade.cpp index 6a9c811a3..fe1df4593 100644 --- a/tests/fade.cpp +++ b/tests/fade.cpp @@ -52,7 +52,6 @@ TEST_CASE("Fade") { } } - SUBCASE("FadeOut") { CHECK(0 == notcurses_render(nc_)); struct timespec ts; diff --git a/tests/fills.cpp b/tests/fills.cpp new file mode 100644 index 000000000..48c71d49c --- /dev/null +++ b/tests/fills.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include "internal.h" +#include "main.h" + +TEST_CASE("Fills") { + if(getenv("TERM") == nullptr){ + return; + } + notcurses_options nopts{}; + nopts.inhibit_alternate_screen = true; + nopts.suppress_banner = true; + FILE* outfp_ = fopen("/dev/tty", "wb"); + REQUIRE(outfp_); + struct notcurses* nc_ = notcurses_init(&nopts, outfp_); + REQUIRE(nc_); + struct ncplane* n_ = notcurses_stdplane(nc_); + REQUIRE(n_); + + // trying to polyfill an invalid cell ought be an error + SUBCASE("PolyfillOffplane") { + int dimx, dimy; + ncplane_dim_yx(n_, &dimy, &dimx); + cell c = CELL_SIMPLE_INITIALIZER('+'); + CHECK(0 > ncplane_polyfill_yx(n_, dimy, 0, &c)); + CHECK(0 > ncplane_polyfill_yx(n_, 0, dimx, &c)); + CHECK(0 > ncplane_polyfill_yx(n_, 0, -1, &c)); + CHECK(0 > ncplane_polyfill_yx(n_, -1, 0, &c)); + } + + SUBCASE("PolyfillEmptyPlane") { + cell c = CELL_SIMPLE_INITIALIZER('+'); + struct ncplane* pfn = ncplane_new(nc_, 4, 4, 0, 0, nullptr); + REQUIRE(nullptr != pfn); + CHECK(16 == ncplane_polyfill_yx(pfn, 0, 0, &c)); + CHECK(0 == notcurses_render(nc_)); + CHECK(0 == ncplane_destroy(pfn)); + } + + SUBCASE("PolyfillWalledPlane") { + cell c = CELL_SIMPLE_INITIALIZER('+'); + struct ncplane* pfn = ncplane_new(nc_, 4, 4, 0, 0, nullptr); + REQUIRE(nullptr != pfn); + CHECK(0 < ncplane_putc_yx(pfn, 0, 1, &c)); + CHECK(0 < ncplane_putc_yx(pfn, 1, 1, &c)); + CHECK(0 < ncplane_putc_yx(pfn, 1, 0, &c)); + // Trying to fill the origin ought fill exactly one cell + CHECK(1 == ncplane_polyfill_yx(pfn, 0, 0, &c)); + // Beyond the origin, we ought fill 12 + CHECK(12 == ncplane_polyfill_yx(pfn, 2, 2, &c)); + CHECK(0 == notcurses_render(nc_)); + CHECK(0 == ncplane_destroy(pfn)); + } + + CHECK(0 == notcurses_stop(nc_)); + CHECK(0 == fclose(outfp_)); + +} diff --git a/tools/setup.py.in b/tools/setup.py.in index b8c133b3e..9b08d936b 100644 --- a/tools/setup.py.in +++ b/tools/setup.py.in @@ -9,7 +9,6 @@ setup( version="${PROJECT_VERSION}", packages=['notcurses'], package_dir={ '': '${CMAKE_CURRENT_BINARY_DIR}/python/src' }, - py_modules=['notcurses'], author="Nick Black", author_email="nickblack@linux.com", description="Blingful TUI construction library (python bindings)",