Commit 83004b05 authored by Cedric Roux's avatar Cedric Roux Committed by Raymond Knopp

T: some work on XY plot

- change paint function:
  when the XY plot is resized we scale. Previously
  the last range was put in the middle of the new range
  (say when we increase the size).
  We may get aspect ratio changes if the resize is not identical
  vertically and horizontally, but I think this behaviour is more
  'natural'.
- fix a bug:
  the last horizontal tick label was printed to far on the right,
  out of the bouding box of the XY plot. This is not totally fixed
  in the case the label is larger than the XY plot. Now the part
  out of the bounding box will be printed on the left. No big deal,
  make the plot big enough. (Before, even if big enough you had a
  problem.)
- add a new vertical tick display, to be used for throughput mostly.
  See in enb.c the difference between 'input signal' and throughput
  plots (those throughput plots will come in later commits).
parent 940190cd
...@@ -14,6 +14,10 @@ typedef void widget; ...@@ -14,6 +14,10 @@ typedef void widget;
#define DEFAULT_FONT 0 #define DEFAULT_FONT 0
/* tic type for XY plot */
#define XY_PLOT_DEFAULT_TICK 0
#define XY_PLOT_SCROLL_TICK 1
/* key modifiers */ /* key modifiers */
#define KEY_SHIFT (1<<0) #define KEY_SHIFT (1<<0)
#define KEY_CONTROL (1<<1) #define KEY_CONTROL (1<<1)
...@@ -54,6 +58,7 @@ void xy_plot_set_points(gui *gui, widget *this, ...@@ -54,6 +58,7 @@ void xy_plot_set_points(gui *gui, widget *this,
int plot, int npoints, float *x, float *y); int plot, int npoints, float *x, float *y);
void xy_plot_get_dimensions(gui *gui, widget *this, int *width, int *height); 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_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, void textlist_add(gui *gui, widget *this, const char *text, int position,
int color); int color);
......
...@@ -117,6 +117,7 @@ struct xy_plot_widget { ...@@ -117,6 +117,7 @@ struct xy_plot_widget {
int wanted_height; int wanted_height;
struct xy_plot_plot *plots; struct xy_plot_plot *plots;
int nplots; int nplots;
int tick_type;
}; };
struct timeline_subline { struct timeline_subline {
......
#include "gui.h" #include "gui.h"
#include "gui_defs.h" #include "gui_defs.h"
#include "x.h" #include "x.h"
#include "../utils.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.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) static void paint(gui *_gui, widget *_this)
{ {
struct gui *g = _gui; struct gui *g = _gui;
...@@ -155,6 +164,184 @@ static void paint(gui *_gui, widget *_this) ...@@ -155,6 +164,184 @@ static void paint(gui *_gui, widget *_this)
x_plot_points(g->x, g->xwin, this->plots[n].color); 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) 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, ...@@ -191,6 +378,7 @@ widget *new_xy_plot(gui *_gui, int width, int height, char *label,
w->ymax = 1; w->ymax = 1;
w->plots = NULL; w->plots = NULL;
w->nplots = 0; w->nplots = 0;
w->tick_type = XY_PLOT_DEFAULT_TICK;
w->common.paint = paint; w->common.paint = paint;
w->common.hints = hints; w->common.hints = hints;
...@@ -309,3 +497,17 @@ void xy_plot_set_title(gui *_gui, widget *_this, char *label) ...@@ -309,3 +497,17 @@ void xy_plot_set_title(gui *_gui, widget *_this, char *label)
gunlock(g); 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment