86 { \
87 T* slots; \
88 T miny, maxy; \
90} nc##X##plot; \
91\
92static int redraw_pixelplot_##T(nc##X##plot* ncp){ \
93 if(calculate_gradient_vector(&ncp->plot, 1)){ \
94 return -1; \
95 } \
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; \
101 \
102 const size_t states = ncplane_pile_const(ncp->plot.ncp)->cellpxy; \
103
104
105 \
106 double interval; \
107 if(ncp->plot.exponentiali){ \
108 if(ncp->maxy > ncp->miny){ \
109 interval = pow(ncp->maxy - ncp->miny, (double)1 / (dimy * states)); \
110 \
111 }else{ \
112 interval = 0; \
113 } \
114 }else{ \
115 interval = ncp->maxy < ncp->miny ? 0 : (ncp->maxy - ncp->miny) / ((double)dimy * states); \
116 } \
118
119 \
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){ \
124 \
125 for(
unsigned y = 0 ;
y < dimy ; ++
y){ \
126 ncplane_set_channels(ncp->plot.ncp, ncp->plot.channels[
y * states]); \
128 if(ncp->plot.exponentiali){ \
130 ncqprefix(ncp->maxy * 100, 100, buf, 0); \
131 }else{ \
132 ncqprefix(pow(interval, (
y + 1) * states) * 100, 100, buf, 0); \
133 } \
134 }else{ \
135 ncqprefix((ncp->maxy - interval * states * (dimy -
y - 1)) * 100, 100, buf, 0); \
136 } \
137 if(
y == dimy - 1 && strlen(ncp->plot.title)){ \
138 ncplane_printf_yx(ncp->plot.ncp, dimy -
y - 1, 0,
"%*.*s %s", \
140 }else{ \
141 ncplane_printf_yx(ncp->plot.ncp, dimy -
y - 1, 0,
"%*.*s", \
143 } \
144 } \
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); \
148 } \
150 if((int)finalx < startx){ \
151 return 0; \
152 } \
153 if(!interval){ \
154 interval = 1; \
155 } \
156 uint32_t* pixels = malloc(dimy * dimx * states * scale * sizeof(*pixels)); \
157 if(pixels ==
NULL){ \
158 return -1; \
159 } \
160 \
161 memset(pixels, 0, dimy * dimx * states * scale * sizeof(*pixels)); \
162 \
163 T* gvals = malloc(sizeof(*gvals) * scale); \
165 free(pixels); \
166 return -1; \
167 } \
168 int idx = ncp->plot.slotstart; \
169
170 \
171 for(
int x = finalx ;
x >= startx ; --
x){ \
172 \
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; \
177 } \
178 if(gvals[i] > ncp->maxy){ \
179 gvals[i] = ncp->maxy; \
180 } \
181 \
183 idx = ncp->plot.slotcount - 1; \
184 } \
185 } \
186
187
188 \
189 T intervalbase = ncp->miny; \
190 bool done = !ncp->plot.bset->fill; \
191 for(
unsigned y = 0 ;
y < dimy ; ++
y){ \
192
193
194 \
195 for(int i = 0 ; i < scale ; ++i){ \
196 size_t egcidx; \
197 if(intervalbase < gvals[i]){ \
198 if(ncp->plot.exponentiali){ \
199 \
200 double scaled = log(gvals[i] - ncp->miny) / log(interval); \
201 double sival = intervalbase ? log(intervalbase) / log(interval) : 0; \
202 egcidx = scaled - sival; \
203 }else{ \
204 egcidx = (gvals[i] - intervalbase) / interval; \
205 } \
206 if(egcidx >= states){ \
207 egcidx = states; \
208 done = false; \
209 } \
210 }else{ \
211 egcidx = 0; \
212 } \
213\
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; \
219 } \
220 } \
221 if(done){ \
222 break; \
223 } \
224 if(ncp->plot.exponentiali){ \
225 intervalbase = ncp->miny + pow(interval, (
y + 1) * states - 1); \
226 }else{ \
227 intervalbase += (states * interval); \
228 } \
229 } \
230 } \
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); \
234 \
235
236 \
237 ncplane_printf_aligned(ncp->plot.ncp, 0,
NCALIGN_RIGHT,
"%" PRIu64, (uint64_t)ncp->slots[
idx]); \
238 } \
239 ncplane_home(ncp->plot.ncp); \
241 free(pixels); \
242 free(gvals); \
244 return -1; \
245 } \
247 .n = ncp->plot.pixelp, \
250 }; \
252 ncvisual_destroy(
ncv); \
253 return -1; \
254 } \
255 ncvisual_destroy(
ncv); \
256 return 0; \
257} \
258\
259static int redraw_plot_##T(nc##X##plot* ncp){ \
261 return redraw_pixelplot_##T(ncp); \
262 } \
263 if(calculate_gradient_vector(&ncp->plot, 0)){ \
264 return -1; \
265 } \
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; \
271 \
272 const size_t states = ncp->plot.bset->height + 1; \
273
274
275 \
276 double interval; \
277 if(ncp->plot.exponentiali){ \
278 if(ncp->maxy > ncp->miny){ \
279 interval = pow(ncp->maxy - ncp->miny, (double)1 / (dimy * states)); \
280 \
281 }else{ \
282 interval = 0; \
283 } \
284 }else{ \
285 interval = ncp->maxy < ncp->miny ? 0 : (ncp->maxy - ncp->miny) / ((double)dimy * states); \
286 } \
288
289 \
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){ \
294 \
295 for(
unsigned y = 0 ;
y < dimy ; ++
y){ \
296 ncplane_set_channels(ncp->plot.ncp, ncp->plot.channels[
y]); \
298 if(ncp->plot.exponentiali){ \
300 ncqprefix(ncp->maxy * 100, 100, buf, 0); \
301 }else{ \
302 ncqprefix(pow(interval, (
y + 1) * states) * 100, 100, buf, 0); \
303 } \
304 }else{ \
305 ncqprefix((ncp->maxy - interval * states * (dimy -
y - 1)) * 100, 100, buf, 0); \
306 } \
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); \
309 }else{ \
310 ncplane_printf_yx(ncp->plot.ncp, dimy -
y - 1,
NCPREFIXCOLUMNS - strlen(buf),
"%s", buf); \
311 } \
312 } \
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); \
316 } \
318 if((int)finalx < startx){ \
319 return 0; \
320 } \
321 if(!interval){ \
322 interval = 1; \
323 } \
324 int idx = ncp->plot.slotstart; \
325 for(
int x = finalx ;
x >= startx ; --
x){ \
326
327 \
329 \
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; \
334 } \
335 if(gvals[i] > ncp->maxy){ \
336 gvals[i] = ncp->maxy; \
337 } \
338 \
340 idx = ncp->plot.slotcount - 1; \
341 } \
342 } \
343
344
345 \
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; \
352
353
354 \
355 for(unsigned i = 0 ; i < scale ; ++i){ \
356 sumidx *= states; \
357 if(intervalbase < gvals[i]){ \
358 if(ncp->plot.exponentiali){ \
359 \
360 double scaled = log(gvals[i] - ncp->miny) / log(interval); \
361 double sival = intervalbase ? log(intervalbase) / log(interval) : 0; \
362 egcidx = scaled - sival; \
363 }else{ \
364 egcidx = (gvals[i] - intervalbase) / interval; \
365 } \
366 if(egcidx >= states){ \
367 egcidx = states - 1; \
368 done = false; \
369 } \
370 sumidx += egcidx; \
371 }else{ \
372 egcidx = 0; \
373 } \
374 \
375 } \
376
377
378
379
380 \
381 \
382 if(sumidx){ \
383 uint64_t chan = ncp->plot.channels[
y]; \
385 char utf8[MB_LEN_MAX + 1]; \
386 int bytes = wctomb(utf8,
egc[sumidx]); \
387 if(bytes < 0){ \
388 return -1; \
389 } \
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)); \
395 if(pool_blit_direct(&ncp->plot.ncp->pool,
c, utf8, bytes, 1) <= 0){ \
396 return -1; \
397 } \
398 }else{ \
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){ \
405 return -1; \
406 } \
407 ncchannels_set_bchannel(&chan, swapbg); \
408 ncchannels_set_fchannel(&chan, swapfg); \
409 ncplane_set_channels(ncp->plot.ncp, chan); \
410 } \
411 } \
412 if(done){ \
413 break; \
414 } \
415 if(ncp->plot.exponentiali){ \
416 intervalbase = ncp->miny + pow(interval, (
y + 1) * states - 1); \
417 }else{ \
418 intervalbase += (states * interval); \
419 } \
420 } \
421 } \
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]); \
426 } \
427 ncplane_home(ncp->plot.ncp); \
428 return 0; \
429} \
430\
433 const T miny, const T maxy, const T trueminy, const T truemaxy){ \
434 \
435 ncpp->plot.ncp =
n; \
436 if(ncplane_set_widget(
ncpp->plot.ncp,
ncpp, (
void(*)(
void*))nc##X##plot_destroy)){ \
438 } \
442 } \
444 logwarn(
"provided unsupported flags %016" PRIx64,
opts->
flags); \
445 } \
446 \
447 if(miny == maxy && miny){ \
449 } \
450 if(
opts->rangex < 0){ \
451 logerror(
"error: supplied negative independent range %d",
opts->rangex); \
453 } \
454 if(maxy < miny){ \
455 logerror("error: supplied maxy < miny"); \
457 } \
458 \
460 logerror("supplied DETECTMAXONLY without domain detection"); \
462 } \
466 blitfxn = ncplot_defblitter(notc); \
467 } \
472 } \
473 unsigned sdimy, sdimx; \
474 ncplane_dim_yx(
n, &sdimy, &sdimx); \
475 if(sdimx <= 0){ \
477 } \
478 unsigned dimx = sdimx; \
479 ncpp->plot.title = strdup(
opts->title ?
opts->title :
""); \
481
482 \
483 const unsigned scaleddim = dimx * (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; \
487 } \
488 if(dimx < ncpp->plot.rangex){ \
489 ncpp->plot.slotcount = scaleddim; \
490 } \
491 ncpp->plot.legendstyle =
opts->legendstyle; \
493 if(
ncpp->plot.slotcount + scaledprefixlen > scaleddim){ \
494 if(scaleddim > scaledprefixlen){ \
495 ncpp->plot.slotcount = scaleddim - scaledprefixlen; \
496 } \
497 } \
498 } \
499 size_t slotsize =
sizeof(*
ncpp->slots) *
ncpp->plot.slotcount; \
500 ncpp->slots = malloc(slotsize); \
503 } \
504 memset(
ncpp->slots, 0, slotsize); \
505 ncpp->plot.maxchannels =
opts->maxchannels; \
506 ncpp->plot.minchannels =
opts->minchannels; \
507 ncpp->plot.bset = bset; \
514 if( (
ncpp->plot.detectdomain = (miny == maxy)) ){ \
515 ncpp->maxy = trueminy; \
516 if(!
ncpp->plot.detectonlymax){ \
517 ncpp->miny = truemaxy; \
518 } \
519 } \
520 ncpp->plot.slotstart = 0; \
521 ncpp->plot.slotx = 0; \
522 ncpp->plot.chancount = 0; \
525 if(create_pixelp(&
ncpp->plot,
n)){ \
527 } \
528 } \
529 redraw_plot_##T(
ncpp); \
530 return bset; \
531} \
532
533
534
535
536 \
537int window_slide_##T(nc##X##plot* ncp, int64_t
x){ \
538 if(x <= ncp->plot.slotx){ \
539 return 0; \
540 } \
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; \
546 return 0; \
547 } \
548
549
550 \
551 int slotsreset = ncp->plot.slotcount - ncp->plot.slotstart - 1; \
552 if(slotsreset > xdiff){ \
553 slotsreset = xdiff; \
554 } \
555 if(slotsreset){ \
556 memset(ncp->slots + ncp->plot.slotstart + 1, 0, slotsreset * sizeof(*ncp->slots)); \
557 } \
558 ncp->plot.slotstart = (ncp->plot.slotstart + xdiff) % ncp->plot.slotcount; \
559 xdiff -= slotsreset; \
560 if(xdiff){ \
561 memset(ncp->slots, 0, xdiff * sizeof(*ncp->slots)); \
562 } \
563 return 0; \
564} \
565\
566static int update_domain_##T(nc##X##plot* ncp, uint64_t
x); \
const struct blitset * lookup_blitset(const tinfo *tcache, ncblitter_e setid, bool may_degrade)
notcurses * ncplane_notcurses(const ncplane *n)
const struct ncplane_options struct ncvisual * ncv
const struct ncplane_options struct ncvisual struct ncvisual_options * vopts
const struct ncplane_options * opts
#define NCPLOT_OPTION_EXPONENTIALD
#define NCPLOT_OPTION_NODEGRADE
#define NCPLOT_OPTION_VERTICALI
#define NCPLOT_OPTION_PRINTSAMPLE
#define NCPLOT_OPTION_LABELTICKSD
#define NCVISUAL_OPTION_NODEGRADE
#define NCPLOT_OPTION_DETECTMAXONLY
ncvisual * ncvisual_from_rgba(const void *rgba, int rows, int rowstride, int cols)
ncplane * ncvisual_blit(notcurses *nc, ncvisual *ncv, const struct ncvisual_options *vopts)