quadrant/braille step plots #461

pull/500/head
nick black 5 years ago committed by Nick Black
parent 6ec9b2d250
commit 0e47db3979

@ -219,7 +219,7 @@ int main(void){
popts.minchannel = popts.maxchannel = 0;
channels_set_fg_rgb(&popts.minchannel, 0x40, 0x50, 0xb0);
channels_set_fg_rgb(&popts.maxchannel, 0x40, 0xff, 0xd0);
popts.gridtype = static_cast<ncgridgeom_e>(NCPLOT_2x1);
popts.gridtype = static_cast<ncgridgeom_e>(NCPLOT_2x2);
plot = ncplot_create(pplane, &popts);
if(!plot){
return EXIT_FAILURE;

@ -298,6 +298,10 @@ typedef struct notcurses {
ncstats stats; // some statistics across the lifetime of the notcurses ctx
ncstats stashstats; // cumulative stats, unaffected by notcurses_reset_stats()
int truecols; // true number of columns in the physical rendering area.
// used only to see if output motion takes us to the next
// line thanks to terminal action alone.
int colors; // number of colors terminfo reported usable for this screen
char* cup; // move cursor
char* cuf; // move n cells right

@ -549,6 +549,7 @@ query_rgb(void){
static int
interrogate_terminfo(notcurses* nc, const notcurses_options* opts, int* dimy, int* dimx){
update_term_dimensions(nc->ttyfd, dimy, dimx);
nc->truecols = *dimx;
char* shortname_term = termname();
char* longname_term = longname();
if(!opts->suppress_banner){

@ -3,31 +3,38 @@
static const struct {
ncgridgeom_e geom;
int width;
int height;
// the EGCs which form the various levels of a given geometry. if the geometry
// is wide, things are arranged with the rightmost side increasing most
// quickly, i.e. it can be indexed as height arrays of 1 + height glyphs. i.e.
// the first five braille EGCs are all 0 on the left, [0..4] on the right.
const wchar_t* egcs;
bool fill;
} geomdata[] = {
{ .geom = NCPLOT_1x1, .width = 1, .egcs = L"", },
{ .geom = NCPLOT_2x1, .width = 1, .egcs = L" ▄█", },
{ .geom = NCPLOT_1x1x4, .width = 1, .egcs = L" ▒░▓█", },
{ .geom = NCPLOT_2x2, .width = 2, .egcs = L" ▗▐ ▖▄▟▌▙█", },
{ .geom = NCPLOT_4x1, .width = 1, .egcs = L" ▂▄▆█", },
{ .geom = NCPLOT_4x2, .width = 2, .egcs = L" ⡀⡄⡆⡇⢀⣀⣄⣆⣇⢠⣠⣤⣦⣧⢰⣰⣴⣶⣷⢸⣸⣼⣾⣿", },
{ .geom = NCPLOT_8x1, .width = 1, .egcs = L" ▁▂▃▄▅▆▇█", },
{ .geom = NCPLOT_1x1, .width = 1, .height = 2, .egcs = L"", .fill = false, },
{ .geom = NCPLOT_2x1, .width = 1, .height = 3, .egcs = L" ▄█", .fill = false, },
{ .geom = NCPLOT_1x1x4, .width = 1, .height = 5, .egcs = L" ▒░▓█", .fill = false, },
{ .geom = NCPLOT_2x2, .width = 2, .height = 3, .egcs = L" ▗▐▖▄▟▌▙█", .fill = false, },
{ .geom = NCPLOT_4x1, .width = 1, .height = 5, .egcs = L" ▂▄▆█", .fill = false, },
{ .geom = NCPLOT_4x2, .width = 2, .height = 5, .egcs = L"⡀⡄⡆⡇⢀⣀⣄⣆⣇⢠⣠⣤⣦⣧⢰⣰⣴⣶⣷⢸⣸⣼⣾⣿", .fill = true, },
{ .geom = NCPLOT_8x1, .width = 1, .height = 9, .egcs = L" ▁▂▃▄▅▆▇█", .fill = false, },
};
static int
redraw_plot(ncplot* n){
ncplane_erase(ncplot_plane(n));
const int scale = geomdata[n->gridtype].width;
int dimy, dimx;
ncplane_dim_yx(ncplot_plane(n), &dimy, &dimx);
const int scaleddim = dimx * geomdata[n->gridtype].width;
const int scaleddim = dimx * scale;
// each transition is worth this much change in value
const size_t states = wcslen(geomdata[n->gridtype].egcs);
const size_t states = geomdata[n->gridtype].height;
// FIXME can we not rid ourselves of this meddlesome double?
double interval = n->maxy < n->miny ? 0 : (n->maxy - n->miny) / ((double)dimy * states);
const int startx = n->labelaxisd ? PREFIXSTRLEN : 0; // plot cols begin here
// if we want fewer slots than there are available columns, our final column
// will be other than the plane's final column. most recent x goes here.
const int finalx = (n->slotcount < scaleddim - 1 - (startx * geomdata[n->gridtype].width) ? startx + (n->slotcount / geomdata[n->gridtype].width) - 1 : dimx - 1);
const int finalx = (n->slotcount < scaleddim - 1 - (startx * scale) ? startx + (n->slotcount / scale) - 1 : dimx - 1);
if(n->labelaxisd){
// show the *top* of each interval range
for(int y = 0 ; y < dimy ; ++y){
@ -36,42 +43,62 @@ redraw_plot(ncplot* n){
ncplane_putstr_yx(ncplot_plane(n), dimy - y - 1, PREFIXSTRLEN - strlen(buf), buf);
}
}
// exit on pathologically narrow planes, or sampleless draws
if(finalx < startx || !interval){
if(finalx < startx){ // exit on pathologically narrow planes
return 0;
}
if(!interval){
interval = 1;
}
#define MAXWIDTH 2
int idx = n->slotstart; // idx holds the real slot index; we move backwards
for(int x = finalx ; x >= startx ; --x){
uint64_t gval = n->slots[idx]; // clip the value at the limits of the graph
if(gval < n->miny){
gval = n->miny;
}
if(gval > n->maxy){
gval = n->maxy;
uint64_t gvals[MAXWIDTH];
// load it retaining the same ordering we have in the actual array
for(int i = scale - 1 ; i >= 0 ; --i){
gvals[i] = n->slots[idx]; // clip the value at the limits of the graph
if(gvals[i] < n->miny){
gvals[i] = n->miny;
}
if(gvals[i] > n->maxy){
gvals[i] = n->maxy;
}
// FIXME if there are an odd number, only go up through the valid ones...
if(--idx < 0){
idx = n->slotcount - 1;
}
}
// starting from the least-significant row, progress in the more significant
// direction, drawing egcs from the grid specification, aborting early if
// we can't draw anything in a given cell.
double intervalbase = n->miny;
const wchar_t* egc = geomdata[n->gridtype].egcs;
for(int y = 0 ; y < dimy ; ++y){
size_t egcidx, sumidx = 0;
// if we've got at least one interval's worth on the number of positions
// times the number of intervals per position plus the starting offset,
// we're going to print *something*
if(intervalbase >= gval){
break;
bool done = !geomdata[n->gridtype].fill;
for(int i = 0 ; i < scale ; ++i){
sumidx *= states;
if(intervalbase < gvals[i]){
egcidx = (gvals[i] - intervalbase) / interval;
if(egcidx >= states){
egcidx = states - 1;
}
done = false;
sumidx += egcidx;
}else{
egcidx = 0;
}
}
size_t egcidx = (gval - intervalbase) / interval;
if(egcidx >= states){
egcidx = states - 1;
if(done){
break;
}
if(ncplane_putwc_yx(ncplot_plane(n), dimy - y - 1, x, geomdata[n->gridtype].egcs[egcidx]) <= 0){
if(ncplane_putwc_yx(ncplot_plane(n), dimy - y - 1, x, egc[sumidx]) <= 0){
return -1;
}
intervalbase += (states * interval);
}
if((idx -= geomdata[n->gridtype].width) < 0){
idx = n->slotcount - 1;
}
}
if(ncplane_cursor_move_yx(ncplot_plane(n), 0, 0)){
return -1;

@ -20,6 +20,7 @@ int notcurses_resize(notcurses* n, int* restrict rows, int* restrict cols){
if(update_term_dimensions(n->ttyfd, rows, cols)){
return -1;
}
n->truecols = *cols;
*rows -= n->margin_t + n->margin_b;
if(*rows <= 0){
*rows = 1;
@ -807,30 +808,26 @@ update_palette(notcurses* nc, FILE* out){
// textronix lacks cup; fake it with horiz+vert moves)
static inline int
stage_cursor(notcurses* nc, FILE* out, int y, int x){
int ret;
if(nc->rstate.y == y){
if(nc->rstate.x == x){
return 0;
}else if(nc->rstate.x < x){
if(nc->rstate.x == x - 1){
int ret = 0;
if(nc->rstate.y == y){ // only need move x
const int xdiff = x - nc->rstate.x;
if(xdiff > 0){
if(xdiff == 1){
ret = term_emit("cuf1", tiparm(nc->cuf1), out, false);
}else{
ret = term_emit("cuf", tiparm(nc->cuf, x - nc->rstate.x), out, false);
}
}else{
if(nc->rstate.x == x + 1){
ret = term_emit("cub1", tiparm(nc->cub1), out, false);
}else{
ret = term_emit("cub", tiparm(nc->cub, nc->rstate.x - x), out, false);
ret = term_emit("cuf", tiparm(nc->cuf, xdiff), out, false);
}
nc->rstate.x = x;
return ret;
}else if(xdiff == 0){
return 0; // no move needed
}
}else{
if((ret = term_emit("cup", tiparm(nc->cup, y, x), out, false)) >= 0){
nc->rstate.y = y;
}
// cub1/cub tend to be destructive in my experiments :/
}
if(ret >= 0){
ret = term_emit("cup", tiparm(nc->cup, y, x), out, false);
if(ret == 0){
nc->rstate.x = x;
nc->rstate.y = y;
}
return ret;
}
@ -861,10 +858,6 @@ notcurses_rasterize(notcurses* nc, const struct crender* rvec){
update_palette(nc, out);
for(y = nc->stdscr->absy ; y < nc->stdscr->leny + nc->stdscr->absy ; ++y){
const int innery = y - nc->stdscr->absy;
// how many characters have we elided? it's not worthwhile to invoke a
// cursor movement with cup if we only elided one or two. set to INT_MAX
// whenever we're on a new line. leave room to avoid overflow.
int needmove = INT_MAX - nc->stdscr->lenx;
for(x = nc->stdscr->absx ; x < nc->stdscr->lenx + nc->stdscr->absx ; ++x){
const int innerx = x - nc->stdscr->absx;
const size_t damageidx = innery * nc->lfdimx + innerx;
@ -879,10 +872,8 @@ notcurses_rasterize(notcurses* nc, const struct crender* rvec){
// no need to emit a cell; what we rendered appears to already be
// here. no updates are performed to elision state nor lastframe.
++nc->stats.cellelisions;
++needmove;
if(cell_wide_left_p(srccell)){
++needmove;
++nc->stats.cellelisions;
++x;
}
}else{
++nc->stats.cellemissions;
@ -992,14 +983,21 @@ fprintf(stderr, "RAST %u [%c] to %d/%d\n", srccell->gcluster, srccell->gcluster,
fprintf(stderr, "RAST %u [%s] to %d/%d\n", srccell->gcluster, egcpool_extended_gcluster(&nc->pool, srccell), y, x);
}*/
if(term_putc(out, &nc->pool, srccell) == 0){
nc->rstate.x += 1 + cell_wide_left_p(srccell);
++nc->rstate.x;
if(cell_wide_left_p(srccell)){
++nc->rstate.x;
++x;
}
// if the terminal's own motion carried us down to the next line,
// we need update our concept of the cursor's true y
/*if(nc->rstate.x >= nc->truecols){
++nc->rstate.y; // FIXME not if on last line, right?
nc->rstate.x = 0;
}*/
}else{
ret = -1;
}
}
if(cell_wide_left_p(srccell)){
++x;
}
//fprintf(stderr, "damageidx: %ld\n", damageidx);
}
}

Loading…
Cancel
Save