mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
plots: always start from the right #457
This commit is contained in:
parent
cfdd8c4c5c
commit
b04a37c433
@ -27,7 +27,7 @@ typedef struct ncplot_options {
|
||||
// number of "pixels" per row x column
|
||||
ncgridgeom_e gridtype;
|
||||
// independent variable is a contiguous range
|
||||
uint64_t rangex;
|
||||
int rangex;
|
||||
// dependent min and max. set both equal to 0 to
|
||||
// use domain autodiscovery.
|
||||
uint64_t miny, maxy;
|
||||
|
@ -2557,7 +2557,7 @@ typedef struct ncplot_options {
|
||||
// of keys. for a time range, say the previous hour sampled with second
|
||||
// resolution, the independent variable would be the range [0..3600): 3600.
|
||||
// if rangex is 0, it is dynamically set to the number of columns.
|
||||
uint64_t rangex;
|
||||
int rangex;
|
||||
uint64_t miny, maxy; // y axis min and max. for autodiscovery, set them equal.
|
||||
bool labelaxisd; // generate labels for the dependent axis
|
||||
bool exponentialy; // is y-axis exponential? (not yet implemented)
|
||||
|
@ -193,7 +193,6 @@ void Tick(ncpp::NotCurses* nc, uint64_t sec) {
|
||||
if(!nc->render()){
|
||||
throw std::runtime_error("error rendering");
|
||||
}
|
||||
mtx.unlock();
|
||||
}
|
||||
|
||||
void Ticker(ncpp::NotCurses* nc) {
|
||||
|
@ -154,24 +154,27 @@ typedef struct ncplot {
|
||||
ncplane* ncp;
|
||||
uint64_t maxchannel;
|
||||
uint64_t minchannel;
|
||||
bool vertical_indep;
|
||||
bool vertical_indep; // not yet implemented FIXME
|
||||
ncgridgeom_e gridtype;
|
||||
// requested number of slots. 0 for automatically setting the number of slots
|
||||
// to span the horizontal area.
|
||||
uint64_t rangex;
|
||||
// to span the horizontal area. if there are more slots than there are
|
||||
// columns, we prefer showing more recent slots to less recent. if there are
|
||||
// fewer slots than there are columns, they prefer the left side.
|
||||
int rangex;
|
||||
// domain minimum and maximum. if detectdomain is true, these are
|
||||
// progressively enlarged/shrunk to fit the sample set. if not, samples
|
||||
// outside these bounds are counted, but the displayed range covers only this.
|
||||
uint64_t miny, maxy;
|
||||
// circular buffer, with the oldest element at slotstart, and slotcount
|
||||
// elements. slotcount is max(columns, rangex).
|
||||
int64_t* slots;
|
||||
unsigned slotstart; // slot index corresponding to slotx
|
||||
uint64_t slotx; // x value corresponding to slots[slotstart]
|
||||
unsigned slotcount;
|
||||
bool labelaxisd; // label dependent axis
|
||||
bool exponentialy;
|
||||
bool detectdomain;
|
||||
// sloutcount-element circular buffer of samples. the newest one (rightmost)
|
||||
// is at slots[slotstart]; they get older as you go back (and around).
|
||||
// elements. slotcount is max(columns, rangex), less label room.
|
||||
uint64_t* slots;
|
||||
int slotcount;
|
||||
int slotstart; // index of most recently-written slot
|
||||
int64_t slotx; // x value corresponding to slots[slotstart] (newest x)
|
||||
bool labelaxisd; // label dependent axis (consumes PREFIXSTRLEN columns)
|
||||
bool exponentialy; // not yet implemented FIXME
|
||||
bool detectdomain; // is domain detection in effect (stretch the domain)?
|
||||
} ncplot;
|
||||
|
||||
typedef struct ncmenu {
|
||||
|
128
src/lib/plot.c
128
src/lib/plot.c
@ -20,8 +20,10 @@ redraw_plot(ncplot* n){
|
||||
const size_t states = wcslen(geomdata[n->gridtype].egcs);
|
||||
// FIXME can we not rid ourselves of this meddlesome double?
|
||||
double interval = n->maxy < n->miny ? 0 : (n->maxy - n->miny) / ((double)dimy * states);
|
||||
int idx = n->slotstart;
|
||||
const int startx = n->labelaxisd ? PREFIXSTRLEN : 0;
|
||||
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 < dimx - 1 - startx ? startx + n->slotcount - 1 : dimx - 1);
|
||||
if(n->labelaxisd){
|
||||
// show the *top* of each interval range
|
||||
for(int y = 0 ; y < dimy ; ++y){
|
||||
@ -30,39 +32,41 @@ redraw_plot(ncplot* n){
|
||||
ncplane_putstr_yx(ncplot_plane(n), dimy - y - 1, PREFIXSTRLEN - strlen(buf), buf);
|
||||
}
|
||||
}
|
||||
if(interval){
|
||||
for(uint64_t x = startx ; x < n->slotcount + startx ; ++x){
|
||||
if(x >= (unsigned)dimx){
|
||||
// exit on pathologically narrow planes, or sampleless draws
|
||||
if(finalx < startx || !interval){
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
for(int y = 0 ; y < dimy ; ++y){
|
||||
// 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;
|
||||
}
|
||||
uint64_t gval = n->slots[idx]; // clip the value at the limits of the graph
|
||||
if(gval < n->miny){
|
||||
gval = n->miny;
|
||||
size_t egcidx = (gval - intervalbase) / interval;
|
||||
if(egcidx >= states){
|
||||
egcidx = states - 1;
|
||||
}
|
||||
if(gval > n->maxy){
|
||||
gval = n->maxy;
|
||||
if(ncplane_putwc_yx(ncplot_plane(n), dimy - y - 1, x, geomdata[n->gridtype].egcs[egcidx]) <= 0){
|
||||
return -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;
|
||||
for(int y = 0 ; y < dimy ; ++y){
|
||||
// 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;
|
||||
}
|
||||
size_t egcidx = (gval - intervalbase) / interval;
|
||||
if(egcidx >= states){
|
||||
egcidx = states - 1;
|
||||
}
|
||||
if(ncplane_putwc_yx(ncplot_plane(n), dimy - y - 1, x, geomdata[n->gridtype].egcs[egcidx]) <= 0){
|
||||
return -1;
|
||||
}
|
||||
intervalbase += (states * interval);
|
||||
}
|
||||
idx = (idx + 1) % n->slotcount;
|
||||
intervalbase += (states * interval);
|
||||
}
|
||||
if(--idx < 0){
|
||||
idx = n->slotcount - 1;
|
||||
}
|
||||
}
|
||||
if(ncplane_cursor_move_yx(ncplot_plane(n), 0, 0)){
|
||||
@ -80,6 +84,9 @@ ncplot* ncplot_create(ncplane* n, const ncplot_options* opts){
|
||||
if(opts->miny == opts->maxy && opts->miny){
|
||||
return NULL;
|
||||
}
|
||||
if(opts->rangex < 0){
|
||||
return NULL;
|
||||
}
|
||||
if(opts->maxy < opts->miny){
|
||||
return NULL;
|
||||
}
|
||||
@ -91,7 +98,7 @@ ncplot* ncplot_create(ncplane* n, const ncplot_options* opts){
|
||||
if(sdimx <= 0){
|
||||
return NULL;
|
||||
}
|
||||
uint64_t dimx = sdimx;
|
||||
int dimx = sdimx;
|
||||
ncplot* ret = malloc(sizeof(*ret));
|
||||
if(ret){
|
||||
ret->rangex = opts->rangex;
|
||||
@ -159,43 +166,48 @@ update_domain(ncplot* n, uint64_t x){
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if x is less than the window, return -1
|
||||
// if x is less than the window, return -1, as the sample will be thrown away.
|
||||
// if the x is within the current window, find the proper slot and update it.
|
||||
// otherwise, the x is the newest sample. if it is obsoletes all existing slots,
|
||||
// reset them, and write the new sample anywhere. otherwise, write it to the
|
||||
// proper slot based on the current newest slot.
|
||||
static inline int
|
||||
window_slide(ncplot* n, uint64_t x){
|
||||
if(x < n->slotx){ // x is behind window, won't be counted
|
||||
window_slide(ncplot* n, int64_t x){
|
||||
if(x < n->slotx - (n->slotcount - 1)){ // x is behind window, won't be counted
|
||||
return -1;
|
||||
}else if(x < n->slotx + n->slotcount){ // x is within window, do nothing
|
||||
}else if(x <= n->slotx){ // x is within window, do nothing
|
||||
return 0;
|
||||
} // x is beyond window; we might be keeping some, might not
|
||||
uint64_t newslotx = x - n->slotcount + 1; // the new value of slotx
|
||||
uint64_t slotdiff = newslotx - n->slotx; // the raw amount we're advancing
|
||||
if(slotdiff > n->slotcount){
|
||||
slotdiff = n->slotcount;
|
||||
} // slotdiff is the number of slots to reset, and amount to advance slotstart
|
||||
n->slotx = newslotx;
|
||||
// number to reset on the right of the circular buffer. min of (available at
|
||||
// current or to right, slotdiff)
|
||||
uint64_t slotsreset = n->slotcount - n->slotstart;
|
||||
if(slotsreset > slotdiff){
|
||||
slotsreset = slotdiff;
|
||||
} // x is newest; we might be keeping some, might not
|
||||
int64_t xdiff = x - n->slotx; // the raw amount we're advancing
|
||||
n->slotx = x;
|
||||
if(xdiff >= n->slotcount){ // we're throwing away all old samples, write to 0
|
||||
memset(n->slots, 0, sizeof(*n->slots) * n->slotcount);
|
||||
n->slotstart = 0;
|
||||
return 0;
|
||||
}
|
||||
// we're throwing away only xdiff slots, which is less than n->slotcount.
|
||||
// first, we'll try to clear to the right...number to reset on the right of
|
||||
// the circular buffer. min of (available at current or to right, xdiff)
|
||||
int slotsreset = n->slotcount - n->slotstart - 1;
|
||||
if(slotsreset > xdiff){
|
||||
slotsreset = xdiff;
|
||||
}
|
||||
if(slotsreset){
|
||||
memset(n->slots + n->slotstart, 0, slotsreset * sizeof(*n->slots));
|
||||
n->slotstart += slotsreset;
|
||||
n->slotstart %= n->slotcount;
|
||||
slotdiff -= slotsreset;
|
||||
memset(n->slots + n->slotstart + 1, 0, slotsreset * sizeof(*n->slots));
|
||||
}
|
||||
if(slotdiff){
|
||||
memset(n->slots, 0, slotdiff * sizeof(*n->slots));
|
||||
n->slotstart = slotdiff - 1;
|
||||
n->slotstart = (n->slotstart + xdiff) % n->slotcount;
|
||||
xdiff -= slotsreset;
|
||||
if(xdiff){ // throw away some at the beginning
|
||||
memset(n->slots, 0, xdiff * sizeof(*n->slots));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x must be within n's window
|
||||
// x must be within n's window at this point
|
||||
static inline void
|
||||
update_sample(ncplot* n, uint64_t x, uint64_t y, bool reset){
|
||||
uint64_t idx = x % n->slotcount;
|
||||
update_sample(ncplot* n, int64_t x, uint64_t y, bool reset){
|
||||
const int64_t diff = n->slotx - x; // amount behind
|
||||
const int idx = (n->slotstart + n->slotcount - diff) % n->slotcount;
|
||||
if(reset){
|
||||
n->slots[idx] = y;
|
||||
}else{
|
||||
|
Loading…
x
Reference in New Issue
Block a user