#include "gui.h" #include "gui_defs.h" #include "x.h" #include "../utils.h" #include #include #include #include static void paint(gui *_gui, widget *_this) { struct gui *g = _gui; struct xy_plot_widget *this = _this; int allocated_plot_width; int allocated_plot_height; float pxsize; float ticdist; float tic; float ticstep; int k, kmin, kmax; float allocated_xmin, allocated_xmax; float allocated_ymin, allocated_ymax; int i; int n; char v[64]; int vwidth, dummy; # define FLIP(v) (-(v) + allocated_plot_height-1) LOGD("PAINT xy plot xywh %d %d %d %d\n", this->common.x, this->common.y, this->common.width, this->common.height); //x_draw_rectangle(g->x, g->xwin, 1, this->common.x, this->common.y, this->common.width, this->common.height); allocated_plot_width = this->common.width - this->vrule_width; allocated_plot_height = this->common.height - this->label_height * 2; if (allocated_plot_width <= 1 || allocated_plot_height <= 1) { WARN("PAINT xy plot: width (%d) or height (%d) is wrong, not painting\n", this->common.width, this->common.height); return; } /* plot zone */ /* TODO: refine height - height of hrule text may be != from label */ x_draw_rectangle(g->x, g->xwin, 1, this->common.x + this->vrule_width, this->common.y, this->common.width - this->vrule_width -1, /* -1 to see right border */ this->common.height - this->label_height * 2); /* horizontal tics */ pxsize = (this->xmax - this->xmin) / allocated_plot_width; ticdist = 100; tic = floor(log10(ticdist * pxsize)); ticstep = powf(10, tic); allocated_xmin = this->xmin; allocated_xmax = this->xmax; /* adjust tic if too tight */ LOGD("pre x ticstep %g\n", ticstep); while (1) { if (ticstep / (allocated_xmax - allocated_xmin) * (allocated_plot_width - 1) > 40) break; ticstep *= 2; } LOGD("post x ticstep %g\n", ticstep); LOGD("xmin/max %g %g width allocated %d alloc xmin/max %g %g ticstep %g\n", this->xmin, this->xmax, allocated_plot_width, allocated_xmin, allocated_xmax, ticstep); kmin = ceil(allocated_xmin / ticstep); kmax = floor(allocated_xmax / ticstep); for (k = kmin; k <= kmax; k++) { /* (k * ticstep - allocated_xmin) / (allocated_max - allocated_xmin) = (x - 0) / (allocated_plot_width-1 - 0) */ int x = (k * ticstep - allocated_xmin) / (allocated_xmax - allocated_xmin) * (allocated_plot_width - 1); int px; x_draw_line(g->x, g->xwin, FOREGROUND_COLOR, this->common.x + this->vrule_width + x, this->common.y + this->common.height - this->label_height * 2, this->common.x + this->vrule_width + x, this->common.y + this->common.height - this->label_height * 2 - 5); sprintf(v, "%g", k * ticstep); x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy); /* do not draw after the widget ('width-1' for if we use 'width' * it is still off by 1 pixel for whatever reason) */ px = this->vrule_width + x - vwidth/2; if (px + vwidth > this->common.width-1) px = this->common.width-1 - vwidth; x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR, this->common.x + px, this->common.y + this->common.height - this->label_height * 2 + this->label_baseline, v); LOGD("tic k %d val %g x %d\n", k, k * ticstep, x); } /* vertical tics */ allocated_ymin = this->ymin; allocated_ymax = this->ymax; switch (this->tick_type) { case XY_PLOT_DEFAULT_TICK: pxsize = (this->ymax - this->ymin) / allocated_plot_height; ticdist = 30; tic = floor(log10(ticdist * pxsize)); ticstep = powf(10, tic); /* adjust tic if too tight */ LOGD("pre y ticstep %g\n", ticstep); while (1) { if (ticstep / (allocated_ymax - allocated_ymin) * (allocated_plot_height - 1) > 20) break; ticstep *= 2; } LOGD("post y ticstep %g\n", ticstep); LOGD("ymin/max %g %g height allocated %d alloc " "ymin/max %g %g ticstep %g\n", this->ymin, this->ymax, allocated_plot_height, allocated_ymin, allocated_ymax, ticstep); kmin = ceil(allocated_ymin / ticstep); kmax = floor(allocated_ymax / ticstep); for (k = kmin; k <= kmax; k++) { int y = (k * ticstep - allocated_ymin) / (allocated_ymax - allocated_ymin) * (allocated_plot_height - 1); sprintf(v, "%g", k * ticstep); x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy); x_draw_line(g->x, g->xwin, FOREGROUND_COLOR, this->common.x + this->vrule_width, this->common.y + FLIP(y), this->common.x + this->vrule_width + 5, this->common.y + FLIP(y)); /* do not print out of the widget (take care of top) */ y = FLIP(y)-this->label_height/2+this->label_baseline; if (y - this->label_baseline < 0) y = this->label_baseline; x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR, this->common.x + this->vrule_width - vwidth - 2, this->common.y + y, v); } break; case XY_PLOT_SCROLL_TICK: /* print only max value, formatted using 'bps' for readability */ bps(v, this->ymax, ""); x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy); x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR, this->common.x + this->vrule_width - vwidth - 2, this->common.y + +this->label_baseline, v); /* vertically divide into 5 */ for (k = 1; k < 5; k++) { int y = round((k * (allocated_plot_height - 1)) / 5.); x_draw_line(g->x, g->xwin, FOREGROUND_COLOR, this->common.x + this->vrule_width, this->common.y + y, this->common.x + this->vrule_width + 5, this->common.y + y); } break; } /* swich (this->tick_type) */ /* label at bottom, in the middle */ x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR, this->common.x + (this->common.width - this->label_width) / 2, this->common.y + this->common.height - this->label_height + this->label_baseline, this->label); for (n = 0; n < this->nplots; n++) { /* points */ float ax, bx, ay, by; ax = (allocated_plot_width-1) / (allocated_xmax - allocated_xmin); bx = -ax * allocated_xmin; ay = (allocated_plot_height-1) / (allocated_ymax - allocated_ymin); by = -ay * allocated_ymin; for (i = 0; i < this->plots[n].npoints; i++) { int x, y; x = ax * this->plots[n].x[i] + bx; y = ay * this->plots[n].y[i] + by; if (x >= 0 && x < allocated_plot_width && y >= 0 && y < allocated_plot_height) x_add_point(g->x, this->common.x + this->vrule_width + x, this->common.y + FLIP(y)); } x_plot_points(g->x, g->xwin, this->plots[n].color); } } static void hints(gui *_gui, widget *_w, int *width, int *height) { struct xy_plot_widget *w = _w; *width = w->wanted_width + w->vrule_width; *height = w->wanted_height + w->label_height * 2; /* TODO: refine */ LOGD("HINTS xy plot wh %d %d (vrule_width %d) (wanted wh %d %d)\n", *width, *height, w->vrule_width, w->wanted_width, w->wanted_height); } widget *new_xy_plot(gui *_gui, int width, int height, char *label, int vruler_width) { struct gui *g = _gui; struct xy_plot_widget *w; glock(g); w = new_widget(g, XY_PLOT, sizeof(struct xy_plot_widget)); w->label = strdup(label); if (w->label == NULL) OOM; /* TODO: be sure calling X there is valid wrt "global model" (we are * not in the "gui thread") */ x_text_get_dimensions(g->x, DEFAULT_FONT, label, &w->label_width, &w->label_height, &w->label_baseline); LOGD("XY PLOT label wh %d %d\n", w->label_width, w->label_height); w->wanted_width = width; w->wanted_height = height; w->vrule_width = vruler_width; w->xmin = -1; w->xmax = 1; w->ymin = -1; w->ymax = 1; w->plots = NULL; w->nplots = 0; w->tick_type = XY_PLOT_DEFAULT_TICK; w->common.paint = paint; w->common.hints = hints; gunlock(g); return w; } /*************************************************************************/ /* public functions */ /*************************************************************************/ int xy_plot_new_plot(gui *_gui, widget *_this, int color) { int ret; struct gui *g = _gui; struct xy_plot_widget *this = _this; glock(g); ret = this->nplots; this->nplots++; this->plots = realloc(this->plots, this->nplots * sizeof(struct xy_plot_plot)); if (this->plots == NULL) abort(); this->plots[ret].x = NULL; this->plots[ret].y = NULL; this->plots[ret].npoints = 0; this->plots[ret].color = color; gunlock(g); return ret; } void xy_plot_set_range(gui *_gui, widget *_this, float xmin, float xmax, float ymin, float ymax) { struct gui *g = _gui; struct xy_plot_widget *this = _this; glock(g); this->xmin = xmin; this->xmax = xmax; this->ymin = ymin; this->ymax = ymax; send_event(g, DIRTY, this->common.id); gunlock(g); } void xy_plot_get_range(gui *_gui, widget *_this, float *xmin, float *xmax, float *ymin, float *ymax) { struct gui *g = _gui; struct xy_plot_widget *this = _this; glock(g); *xmin = this->xmin; *xmax = this->xmax; *ymin = this->ymin; *ymax = this->ymax; gunlock(g); } void xy_plot_set_points(gui *_gui, widget *_this, int plot, int npoints, float *x, float *y) { struct gui *g = _gui; struct xy_plot_widget *this = _this; glock(g); if (npoints != this->plots[plot].npoints) { free(this->plots[plot].x); free(this->plots[plot].y); this->plots[plot].x = calloc(npoints, sizeof(float)); if (this->plots[plot].x == NULL) abort(); this->plots[plot].y = calloc(npoints, sizeof(float)); if (this->plots[plot].y == NULL) abort(); this->plots[plot].npoints = npoints; } memcpy(this->plots[plot].x, x, npoints * sizeof(float)); memcpy(this->plots[plot].y, y, npoints * sizeof(float)); send_event(g, DIRTY, this->common.id); gunlock(g); } void xy_plot_get_dimensions(gui *_gui, widget *_this, int *width, int *height) { struct gui *g = _gui; struct xy_plot_widget *this = _this; glock(g); if (this->common.width == 0 || this->common.height == 0) { *width = this->wanted_width; *height = this->wanted_height; } else { *width = this->common.width - this->vrule_width; *height = this->common.height - this->label_height * 2; } gunlock(g); } void xy_plot_set_title(gui *_gui, widget *_this, char *label) { struct gui *g = _gui; struct xy_plot_widget *this = _this; glock(g); free(this->label); this->label = strdup(label); if (this->label == NULL) OOM; /* TODO: be sure calling X there is valid wrt "global model" (we are * not in the "gui thread") */ x_text_get_dimensions(g->x, DEFAULT_FONT, label, &this->label_width, &this->label_height, &this->label_baseline); send_event(g, REPACK, this->common.id); gunlock(g); } void xy_plot_set_tick_type(gui *_gui, widget *_this, int type) { struct gui *g = _gui; struct xy_plot_widget *this = _this; glock(g); this->tick_type = type; send_event(g, DIRTY, this->common.id); gunlock(g); }