52 uint64_t basechan = 0;
64calculate_gradient_vector(
ncplot* p,
unsigned pixelp){
65 const int dimy = ncplane_dim_y(p->
ncp);
66 const unsigned states = dimy * (pixelp ? ncplane_pile(p->
ncp)->cellpxy : 1);
86typedef struct nc##X##plot { \
92static int redraw_pixelplot_##T(nc##X##plot* ncp){ \
93 if(calculate_gradient_vector(&ncp->plot, 1)){ \
96 const int scale = ncplane_pile_const(ncp->plot.ncp)->cellpxx; \
97 ncplane_erase(ncp->plot.ncp); \
98 unsigned dimy, dimx; \
99 ncplane_dim_yx(ncp->plot.ncp, &dimy, &dimx); \
100 const unsigned scaleddim = dimx * scale; \
102 const size_t states = ncplane_pile_const(ncp->plot.ncp)->cellpxy; \
107 if(ncp->plot.exponentiali){ \
108 if(ncp->maxy > ncp->miny){ \
109 interval = pow(ncp->maxy - ncp->miny, (double)1 / (dimy * states)); \
115 interval = ncp->maxy < ncp->miny ? 0 : (ncp->maxy - ncp->miny) / ((double)dimy * states); \
117 const int startx = ncp->plot.labelaxisd ? NCPREFIXCOLUMNS : 0; \
120 const unsigned finalx = (ncp->plot.slotcount < scaleddim - 1 - (startx * scale) ? \
121 startx + (ncp->plot.slotcount / scale) - 1 : dimx - 1); \
122 ncplane_set_styles(ncp->plot.ncp, ncp->plot.legendstyle); \
123 if(ncp->plot.labelaxisd){ \
125 for(unsigned y = 0 ; y < dimy ; ++y){ \
126 ncplane_set_channels(ncp->plot.ncp, ncp->plot.channels[y * states]); \
127 char buf[NCPREFIXSTRLEN + 1]; \
128 if(ncp->plot.exponentiali){ \
130 ncqprefix(ncp->maxy * 100, 100, buf, 0); \
132 ncqprefix(pow(interval, (y + 1) * states) * 100, 100, buf, 0); \
135 ncqprefix((ncp->maxy - interval * states * (dimy - y - 1)) * 100, 100, buf, 0); \
137 if(y == dimy - 1 && strlen(ncp->plot.title)){ \
138 ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, 0, "%*.*s %s", \
139 NCPREFIXSTRLEN, NCPREFIXSTRLEN, buf, ncp->plot.title); \
141 ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, 0, "%*.*s", \
142 NCPREFIXSTRLEN, NCPREFIXSTRLEN, buf); \
145 }else if(strlen(ncp->plot.title)){ \
146 ncplane_set_channels(ncp->plot.ncp, ncp->plot.channels[(dimy - 1) * states]); \
147 ncplane_printf_yx(ncp->plot.ncp, 0, NCPREFIXCOLUMNS - strlen(ncp->plot.title), "%s", ncp->plot.title); \
149 ncplane_set_styles(ncp->plot.ncp, NCSTYLE_NONE); \
150 if((int)finalx < startx){ \
156 uint32_t* pixels = malloc(dimy * dimx * states * scale * sizeof(*pixels)); \
157 if(pixels == NULL){ \
161 memset(pixels, 0, dimy * dimx * states * scale * sizeof(*pixels)); \
163 T* gvals = malloc(sizeof(*gvals) * scale); \
168 int idx = ncp->plot.slotstart; \
171 for(int x = finalx ; x >= startx ; --x){ \
173 for(int i = scale - 1 ; i >= 0 ; --i){ \
174 gvals[i] = ncp->slots[idx]; \
175 if(gvals[i] < ncp->miny){ \
176 gvals[i] = ncp->miny; \
178 if(gvals[i] > ncp->maxy){ \
179 gvals[i] = ncp->maxy; \
183 idx = ncp->plot.slotcount - 1; \
189 T intervalbase = ncp->miny; \
190 bool done = !ncp->plot.bset->fill; \
191 for(unsigned y = 0 ; y < dimy ; ++y){ \
195 for(int i = 0 ; i < scale ; ++i){ \
197 if(intervalbase < gvals[i]){ \
198 if(ncp->plot.exponentiali){ \
200 double scaled = log(gvals[i] - ncp->miny) / log(interval); \
201 double sival = intervalbase ? log(intervalbase) / log(interval) : 0; \
202 egcidx = scaled - sival; \
204 egcidx = (gvals[i] - intervalbase) / interval; \
206 if(egcidx >= states){ \
214 for(size_t yy = 0 ; yy < egcidx ; ++yy){ \
215 int poff = x * scale + i + (((dimy - 1 - y) * states + (states - 1 - yy)) * dimx * scale); \
216 uint32_t color = ncchannels_fg_rgb(ncp->plot.channels[y * states + yy]); \
217 ncpixel_set_a(&color, 0xff); \
218 pixels[poff] = color; \
224 if(ncp->plot.exponentiali){ \
225 intervalbase = ncp->miny + pow(interval, (y + 1) * states - 1); \
227 intervalbase += (states * interval); \
231 if(ncp->plot.printsample){ \
232 ncplane_set_styles(ncp->plot.ncp, ncp->plot.legendstyle); \
233 ncplane_set_channels(ncp->plot.ncp, ncp->plot.maxchannels); \
237 ncplane_printf_aligned(ncp->plot.ncp, 0, NCALIGN_RIGHT, "%" PRIu64, (uint64_t)ncp->slots[idx]); \
239 ncplane_home(ncp->plot.ncp); \
240 struct ncvisual* ncv = ncvisual_from_rgba(pixels, dimy * states, dimx * scale * 4, dimx * scale); \
246 struct ncvisual_options vopts = { \
247 .n = ncp->plot.pixelp, \
248 .blitter = NCBLIT_PIXEL, \
249 .flags = NCVISUAL_OPTION_NODEGRADE, \
251 if(ncvisual_blit(ncplane_notcurses(ncp->plot.ncp), ncv, &vopts) == NULL){ \
252 ncvisual_destroy(ncv); \
255 ncvisual_destroy(ncv); \
259static int redraw_plot_##T(nc##X##plot* ncp){ \
260 if(ncp->plot.bset->geom == NCBLIT_PIXEL){ \
261 return redraw_pixelplot_##T(ncp); \
263 if(calculate_gradient_vector(&ncp->plot, 0)){ \
266 ncplane_erase(ncp->plot.ncp); \
267 const unsigned scale = ncp->plot.bset->width; \
268 unsigned dimy, dimx; \
269 ncplane_dim_yx(ncp->plot.ncp, &dimy, &dimx); \
270 const unsigned scaleddim = dimx * scale; \
272 const size_t states = ncp->plot.bset->height + 1; \
277 if(ncp->plot.exponentiali){ \
278 if(ncp->maxy > ncp->miny){ \
279 interval = pow(ncp->maxy - ncp->miny, (double)1 / (dimy * states)); \
285 interval = ncp->maxy < ncp->miny ? 0 : (ncp->maxy - ncp->miny) / ((double)dimy * states); \
287 const int startx = ncp->plot.labelaxisd ? NCPREFIXCOLUMNS : 0; \
290 const unsigned finalx = (ncp->plot.slotcount < scaleddim - 1 - (startx * scale) ? \
291 startx + (ncp->plot.slotcount / scale) - 1 : dimx - 1); \
292 ncplane_set_styles(ncp->plot.ncp, ncp->plot.legendstyle); \
293 if(ncp->plot.labelaxisd){ \
295 for(unsigned y = 0 ; y < dimy ; ++y){ \
296 ncplane_set_channels(ncp->plot.ncp, ncp->plot.channels[y]); \
297 char buf[NCPREFIXSTRLEN + 1]; \
298 if(ncp->plot.exponentiali){ \
300 ncqprefix(ncp->maxy * 100, 100, buf, 0); \
302 ncqprefix(pow(interval, (y + 1) * states) * 100, 100, buf, 0); \
305 ncqprefix((ncp->maxy - interval * states * (dimy - y - 1)) * 100, 100, buf, 0); \
307 if(y == dimy - 1 && strlen(ncp->plot.title)){ \
308 ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, NCPREFIXCOLUMNS - strlen(buf), "%s %s", buf, ncp->plot.title); \
310 ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, NCPREFIXCOLUMNS - strlen(buf), "%s", buf); \
313 }else if(strlen(ncp->plot.title)){ \
314 ncplane_set_channels(ncp->plot.ncp, ncp->plot.channels[dimy - 1]); \
315 ncplane_printf_yx(ncp->plot.ncp, 0, NCPREFIXCOLUMNS - strlen(ncp->plot.title), "%s", ncp->plot.title); \
317 ncplane_set_styles(ncp->plot.ncp, NCSTYLE_NONE); \
318 if((int)finalx < startx){ \
324 int idx = ncp->plot.slotstart; \
325 for(int x = finalx ; x >= startx ; --x){ \
330 for(int i = scale - 1 ; i >= 0 ; --i){ \
331 gvals[i] = ncp->slots[idx]; \
332 if(gvals[i] < ncp->miny){ \
333 gvals[i] = ncp->miny; \
335 if(gvals[i] > ncp->maxy){ \
336 gvals[i] = ncp->maxy; \
340 idx = ncp->plot.slotcount - 1; \
346 T intervalbase = ncp->miny; \
347 const wchar_t* egc = ncp->plot.bset->plotegcs; \
348 bool done = !ncp->plot.bset->fill; \
349 for(unsigned y = 0 ; y < dimy ; ++y){ \
350 ncplane_set_channels(ncp->plot.ncp, ncp->plot.channels[y]); \
351 size_t egcidx = 0, sumidx = 0; \
355 for(unsigned i = 0 ; i < scale ; ++i){ \
357 if(intervalbase < gvals[i]){ \
358 if(ncp->plot.exponentiali){ \
360 double scaled = log(gvals[i] - ncp->miny) / log(interval); \
361 double sival = intervalbase ? log(intervalbase) / log(interval) : 0; \
362 egcidx = scaled - sival; \
364 egcidx = (gvals[i] - intervalbase) / interval; \
366 if(egcidx >= states){ \
367 egcidx = states - 1; \
383 uint64_t chan = ncp->plot.channels[y]; \
384 if(notcurses_canutf8(ncplane_notcurses(ncp->plot.ncp))){ \
385 char utf8[MB_LEN_MAX + 1]; \
386 int bytes = wctomb(utf8, egc[sumidx]); \
390 utf8[bytes] = '\0'; \
391 nccell* c = ncplane_cell_ref_yx(ncp->plot.ncp, dimy - y - 1, x); \
392 cell_set_bchannel(c, ncchannels_bchannel(chan)); \
393 cell_set_fchannel(c, ncchannels_fchannel(chan)); \
394 nccell_set_styles(c, NCSTYLE_NONE); \
395 if(pool_blit_direct(&ncp->plot.ncp->pool, c, utf8, bytes, 1) <= 0){ \
399 const uint64_t swapbg = ncchannels_bchannel(chan); \
400 const uint64_t swapfg = ncchannels_fchannel(chan); \
401 ncchannels_set_bchannel(&chan, swapfg); \
402 ncchannels_set_fchannel(&chan, swapbg); \
403 ncplane_set_channels(ncp->plot.ncp, chan); \
404 if(ncplane_putchar_yx(ncp->plot.ncp, dimy - y - 1, x, ' ') <= 0){ \
407 ncchannels_set_bchannel(&chan, swapbg); \
408 ncchannels_set_fchannel(&chan, swapfg); \
409 ncplane_set_channels(ncp->plot.ncp, chan); \
415 if(ncp->plot.exponentiali){ \
416 intervalbase = ncp->miny + pow(interval, (y + 1) * states - 1); \
418 intervalbase += (states * interval); \
422 if(ncp->plot.printsample){ \
423 ncplane_set_styles(ncp->plot.ncp, ncp->plot.legendstyle); \
424 ncplane_set_channels(ncp->plot.ncp, ncp->plot.maxchannels); \
425 ncplane_printf_aligned(ncp->plot.ncp, 0, NCALIGN_RIGHT, "%" PRIu64, (uint64_t)ncp->slots[idx]); \
427 ncplane_home(ncp->plot.ncp); \
431static const struct blitset* \
432create_##T(nc##X##plot* ncpp, ncplane* n, const ncplot_options* opts, \
433 const T miny, const T maxy, const T trueminy, const T truemaxy){ \
435 ncpp->plot.ncp = n; \
436 if(ncplane_set_widget(ncpp->plot.ncp, ncpp, (void(*)(void*))nc##X##plot_destroy)){ \
439 ncplot_options zeroed = {0}; \
443 if(opts->flags >= (NCPLOT_OPTION_PRINTSAMPLE << 1u)){ \
444 logwarn("provided unsupported flags %016" PRIx64, opts->flags); \
447 if(miny == maxy && miny){ \
450 if(opts->rangex < 0){ \
451 logerror("error: supplied negative independent range %d", opts->rangex); \
455 logerror("error: supplied maxy < miny"); \
459 if(opts->flags & NCPLOT_OPTION_DETECTMAXONLY && (miny != maxy)){ \
460 logerror("supplied DETECTMAXONLY without domain detection"); \
463 const notcurses* notc = ncplane_notcurses(n); \
464 ncblitter_e blitfxn = opts ? opts->gridtype : NCBLIT_DEFAULT; \
465 if(blitfxn == NCBLIT_DEFAULT){ \
466 blitfxn = ncplot_defblitter(notc); \
468 bool degrade_blitter = !(opts && (opts->flags & NCPLOT_OPTION_NODEGRADE)); \
469 const struct blitset* bset = lookup_blitset(¬c->tcache, blitfxn, degrade_blitter); \
473 unsigned sdimy, sdimx; \
474 ncplane_dim_yx(n, &sdimy, &sdimx); \
478 unsigned dimx = sdimx; \
479 ncpp->plot.title = strdup(opts->title ? opts->title : ""); \
480 ncpp->plot.rangex = opts->rangex; \
483 const unsigned scaleddim = dimx * (bset->geom == NCBLIT_PIXEL ? ncplane_pile_const(n)->cellpxx : bset->width); \
484 const unsigned scaledprefixlen = NCPREFIXCOLUMNS * (bset->geom == NCBLIT_PIXEL ? ncplane_pile_const(n)->cellpxx : bset->width); \
485 if((ncpp->plot.slotcount = ncpp->plot.rangex) == 0){ \
486 ncpp->plot.slotcount = scaleddim; \
488 if(dimx < ncpp->plot.rangex){ \
489 ncpp->plot.slotcount = scaleddim; \
491 ncpp->plot.legendstyle = opts->legendstyle; \
492 if( (ncpp->plot.labelaxisd = opts->flags & NCPLOT_OPTION_LABELTICKSD) ){ \
493 if(ncpp->plot.slotcount + scaledprefixlen > scaleddim){ \
494 if(scaleddim > scaledprefixlen){ \
495 ncpp->plot.slotcount = scaleddim - scaledprefixlen; \
499 size_t slotsize = sizeof(*ncpp->slots) * ncpp->plot.slotcount; \
500 ncpp->slots = malloc(slotsize); \
501 if(ncpp->slots == NULL){ \
504 memset(ncpp->slots, 0, slotsize); \
505 ncpp->plot.maxchannels = opts->maxchannels; \
506 ncpp->plot.minchannels = opts->minchannels; \
507 ncpp->plot.bset = bset; \
510 ncpp->plot.vertical_indep = opts->flags & NCPLOT_OPTION_VERTICALI; \
511 ncpp->plot.exponentiali = opts->flags & NCPLOT_OPTION_EXPONENTIALD; \
512 ncpp->plot.detectonlymax = opts->flags & NCPLOT_OPTION_DETECTMAXONLY; \
513 ncpp->plot.printsample = opts->flags & NCPLOT_OPTION_PRINTSAMPLE; \
514 if( (ncpp->plot.detectdomain = (miny == maxy)) ){ \
515 ncpp->maxy = trueminy; \
516 if(!ncpp->plot.detectonlymax){ \
517 ncpp->miny = truemaxy; \
520 ncpp->plot.slotstart = 0; \
521 ncpp->plot.slotx = 0; \
522 ncpp->plot.chancount = 0; \
523 ncpp->plot.channels = NULL; \
524 if(bset->geom == NCBLIT_PIXEL){ \
525 if(create_pixelp(&ncpp->plot, n)){ \
529 redraw_plot_##T(ncpp); \
537int window_slide_##T(nc##X##plot* ncp, int64_t x){ \
538 if(x <= ncp->plot.slotx){ \
541 int64_t xdiff = x - ncp->plot.slotx; \
542 ncp->plot.slotx = x; \
543 if(xdiff >= ncp->plot.slotcount){ \
544 memset(ncp->slots, 0, sizeof(*ncp->slots) * ncp->plot.slotcount); \
545 ncp->plot.slotstart = 0; \
551 int slotsreset = ncp->plot.slotcount - ncp->plot.slotstart - 1; \
552 if(slotsreset > xdiff){ \
553 slotsreset = xdiff; \
556 memset(ncp->slots + ncp->plot.slotstart + 1, 0, slotsreset * sizeof(*ncp->slots)); \
558 ncp->plot.slotstart = (ncp->plot.slotstart + xdiff) % ncp->plot.slotcount; \
559 xdiff -= slotsreset; \
561 memset(ncp->slots, 0, xdiff * sizeof(*ncp->slots)); \
566static int update_domain_##T(nc##X##plot* ncp, uint64_t x); \
567static void update_sample_##T(nc##X##plot* ncp, int64_t x, T y, bool reset); \
573int add_sample_##T(nc##X##plot* ncpp, int64_t x, T y){ \
574 if(x < ncpp->plot.slotx - (ncpp->plot.slotcount - 1)){ \
577 if(y == 0 && x <= ncpp->plot.slotx){ \
580 if(window_slide_##T(ncpp, x)){ \
583 update_sample_##T(ncpp, x, y, false); \
584 if(update_domain_##T(ncpp, x)){ \
587 return redraw_plot_##T(ncpp); \
589int sample_##T(const nc##X##plot* ncp, int64_t x, T* y){ \
590 if(x < ncp->plot.slotx - (ncp->plot.slotcount - 1)){ \
592 }else if(x > ncp->plot.slotx){ \
595 *y = ncp->slots[x % ncp->plot.slotcount]; \
605 if(ncplane_set_widget(
n->ncp,
NULL,
NULL) == 0){
616 const uint64_t val = ncp->slots[
x % ncp->plot.slotcount];
617 if(ncp->plot.detectdomain){
621 if(!ncp->plot.detectonlymax){
628 if(val > ncp->maxy || val < ncp->miny){
635 const double val = ncp->slots[
x % ncp->plot.slotcount];
636 if(ncp->plot.detectdomain){
640 if(!ncp->plot.detectonlymax){
647 if(val > ncp->maxy || val < ncp->miny){
655update_sample_uint64_t(ncuplot* ncp, int64_t
x, uint64_t
y,
bool reset){
656 const int64_t diff = ncp->plot.slotx -
x;
657 const int idx = (ncp->plot.slotstart + ncp->plot.slotcount - diff) % ncp->plot.slotcount;
661 ncp->slots[
idx] +=
y;
667update_sample_double(ncdplot* ncp, int64_t
x,
double y,
bool reset){
668 const int64_t diff = ncp->plot.slotx -
x;
669 const int idx = (ncp->plot.slotstart + ncp->plot.slotcount - diff) % ncp->plot.slotcount;
673 ncp->slots[
idx] +=
y;
679 ncuplot* ret = malloc(
sizeof(*ret));
684 memset(ret, 0,
sizeof(*ret));
685 const struct blitset* bset = create_uint64_t(ret,
n,
opts, miny, maxy, 0, UINT64_MAX);
698 return add_sample_uint64_t(
n,
x,
y);
702 if(window_slide_uint64_t(
n,
x)){
705 update_sample_uint64_t(
n,
x,
y,
true);
709 return redraw_plot_uint64_t(
n);
714 ncplot_destroy(&
n->plot);
722 ncdplot* ret = malloc(
sizeof(*ret));
727 memset(ret, 0,
sizeof(*ret));
728 const struct blitset* bset = create_double(ret,
n,
opts, miny, maxy, -DBL_MAX, DBL_MAX);
741 return add_sample_double(
n,
x,
y);
745 if(window_slide_double(
n,
x)){
748 update_sample_double(
n,
x,
y,
true);
752 return redraw_plot_double(
n);
756 return sample_uint64_t(
n,
x,
y);
760 return sample_double(
n,
x,
y);
765 ncplot_destroy(&
n->plot);
ncplane * ncplane_dup(const ncplane *n, void *opaque)
int ncplane_destroy(ncplane *ncp)
int ncplane_set_base(ncplane *ncp, const char *egc, uint16_t stylemask, uint64_t channels)
ncplane * ncplane_reparent(ncplane *n, ncplane *newparent)
int ncplane_set_name(ncplane *n, const char *name)
int ncplane_move_below(ncplane *restrict n, ncplane *restrict below)
const struct ncplane_options * opts
#define NCALPHA_TRANSPARENT
int ncdplot_add_sample(ncdplot *n, uint64_t x, double y)
int update_domain_uint64_t(ncuplot *ncp, uint64_t x)
ncplane * ncuplot_plane(ncuplot *n)
void ncuplot_destroy(ncuplot *n)
ncplane * ncdplot_plane(ncdplot *n)
ncuplot * ncuplot_create(ncplane *n, const ncplot_options *opts, uint64_t miny, uint64_t maxy)
int ncdplot_set_sample(ncdplot *n, uint64_t x, double y)
void ncdplot_destroy(ncdplot *n)
int ncuplot_set_sample(ncuplot *n, uint64_t x, uint64_t y)
int ncuplot_add_sample(ncuplot *n, uint64_t x, uint64_t y)
ncdplot * ncdplot_create(ncplane *n, const ncplot_options *opts, double miny, double maxy)
int ncdplot_sample(const ncdplot *n, uint64_t x, double *y)
int ncuplot_sample(const ncuplot *n, uint64_t x, uint64_t *y)
int update_domain_double(ncdplot *ncp, uint64_t x)
const struct blitset * bset