diff --git a/common/utils/T/tracer/gui/gui.h b/common/utils/T/tracer/gui/gui.h index 43b955f91a566a1f039f6c67741d9026a6ad6036..0fb96db325798e77de38bee3af2fa9b1e62f9e7d 100644 --- a/common/utils/T/tracer/gui/gui.h +++ b/common/utils/T/tracer/gui/gui.h @@ -14,6 +14,10 @@ typedef void widget; #define DEFAULT_FONT 0 +/* tic type for XY plot */ +#define XY_PLOT_DEFAULT_TICK 0 +#define XY_PLOT_SCROLL_TICK 1 + /* key modifiers */ #define KEY_SHIFT (1<<0) #define KEY_CONTROL (1<<1) @@ -54,6 +58,7 @@ void xy_plot_set_points(gui *gui, widget *this, int plot, int npoints, float *x, float *y); void xy_plot_get_dimensions(gui *gui, widget *this, int *width, int *height); void xy_plot_set_title(gui *gui, widget *this, char *label); +void xy_plot_set_tick_type(gui *gui, widget *this, int type); void textlist_add(gui *gui, widget *this, const char *text, int position, int color); diff --git a/common/utils/T/tracer/gui/gui_defs.h b/common/utils/T/tracer/gui/gui_defs.h index 9cb94dc98a4f1104082d38b85619d1d055fa1fb0..88b8121ec9591b461d59ba1ae7c87f9deaa79d39 100644 --- a/common/utils/T/tracer/gui/gui_defs.h +++ b/common/utils/T/tracer/gui/gui_defs.h @@ -117,6 +117,7 @@ struct xy_plot_widget { int wanted_height; struct xy_plot_plot *plots; int nplots; + int tick_type; }; struct timeline_subline { diff --git a/common/utils/T/tracer/gui/xy_plot.c b/common/utils/T/tracer/gui/xy_plot.c index 3baac92c04615dc61ab9c8327103abf26bd58d0c..4115f92103adca50bad18cd022dce6cbbc6b2680 100644 --- a/common/utils/T/tracer/gui/xy_plot.c +++ b/common/utils/T/tracer/gui/xy_plot.c @@ -1,11 +1,20 @@ #include "gui.h" #include "gui_defs.h" #include "x.h" +#include "../utils.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> +#if 0 +/* this version behaves differently when you resize the XY plot. Say + * you increase the size. The old (smaller) view is put at the center + * of the new view. Everything inside keeps the same size/aspect ratio. + * The other version below resizes the old view so that it fully occupies + * the new view. It may introduce aspect ratio changes, but usage seems + * to suggest it's a better behaviour. + */ static void paint(gui *_gui, widget *_this) { struct gui *g = _gui; @@ -155,6 +164,184 @@ static void paint(gui *_gui, widget *_this) x_plot_points(g->x, g->xwin, this->plots[n].color); } } +#endif + +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)); + x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR, + this->common.x + this->vrule_width - vwidth - 2, + this->common.y + FLIP(y)-this->label_height/2+this->label_baseline, + 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) { @@ -191,6 +378,7 @@ widget *new_xy_plot(gui *_gui, int width, int height, char *label, w->ymax = 1; w->plots = NULL; w->nplots = 0; + w->tick_type = XY_PLOT_DEFAULT_TICK; w->common.paint = paint; w->common.hints = hints; @@ -309,3 +497,17 @@ void xy_plot_set_title(gui *_gui, widget *_this, char *label) 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); +}