xy_plot.c 9.33 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "gui.h"
#include "gui_defs.h"
#include "x.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

static void paint(gui *_gui, widget *_this)
{
  struct gui *g = _gui;
  struct xy_plot_widget *this = _this;
  int wanted_plot_width, allocated_plot_width;
  int wanted_plot_height, 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;
  float center;
Cedric Roux's avatar
Cedric Roux committed
23
  int i;
24
  int n;
25

26
27
# define FLIP(v) (-(v) + allocated_plot_height-1)

Cedric Roux's avatar
Cedric Roux committed
28
  LOGD("PAINT xy plot xywh %d %d %d %d\n", this->common.x, this->common.y, this->common.width, this->common.height);
29
30
31

//x_draw_rectangle(g->x, g->xwin, 1, this->common.x, this->common.y, this->common.width, this->common.height);

32
33
34
35
36
  wanted_plot_width = this->wanted_width;
  allocated_plot_width = this->common.width - this->vrule_width;
  wanted_plot_height = this->wanted_height;
  allocated_plot_height = this->common.height - this->label_height * 2;

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  /* 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) / wanted_plot_width;
  ticdist = 100;
  tic = floor(log10(ticdist * pxsize));
  ticstep = powf(10, tic);
  center = (this->xmax + this->xmin) / 2;
  allocated_xmin = center - ((this->xmax - this->xmin) *
                             allocated_plot_width / wanted_plot_width) / 2;
  allocated_xmax = center + ((this->xmax - this->xmin) *
                             allocated_plot_width / wanted_plot_width) / 2;
  /* adjust tic if too tight */
Cedric Roux's avatar
Cedric Roux committed
56
  LOGD("pre x ticstep %g\n", ticstep);
57
58
59
60
61
  while (1) {
    if (ticstep / (allocated_xmax - allocated_xmin)
                * (allocated_plot_width - 1) > 40) break;
    ticstep *= 2;
  }
Cedric Roux's avatar
Cedric Roux committed
62
63
  LOGD("post x ticstep %g\n", ticstep);
  LOGD("xmin/max %g %g width wanted allocated %d %d alloc xmin/max %g %g ticstep %g\n", this->xmin, this->xmax, wanted_plot_width, allocated_plot_width, allocated_xmin, allocated_xmax, ticstep);
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  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)
 */
    char v[64];
    int vwidth, dummy;
    float x = (k * ticstep - allocated_xmin) /
              (allocated_xmax - allocated_xmin) *
              (allocated_plot_width - 1);
    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);
Cedric Roux's avatar
Cedric Roux committed
82
83
    x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy);
    x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
84
        this->common.x + this->vrule_width + x - vwidth/2,
Cedric Roux's avatar
Cedric Roux committed
85
86
        this->common.y + this->common.height - this->label_height * 2 +
            this->label_baseline,
87
        v);
Cedric Roux's avatar
Cedric Roux committed
88
    LOGD("tic k %d val %g x %g\n", k, k * ticstep, x);
89
90
91
92
93
94
95
96
97
98
99
100
101
  }

  /* vertical tics */
  pxsize = (this->ymax - this->ymin) / wanted_plot_height;
  ticdist = 30;
  tic = floor(log10(ticdist * pxsize));
  ticstep = powf(10, tic);
  center = (this->ymax + this->ymin) / 2;
  allocated_ymin = center - ((this->ymax - this->ymin) *
                             allocated_plot_height / wanted_plot_height) / 2;
  allocated_ymax = center + ((this->ymax - this->ymin) *
                             allocated_plot_height / wanted_plot_height) / 2;
  /* adjust tic if too tight */
Cedric Roux's avatar
Cedric Roux committed
102
  LOGD("pre y ticstep %g\n", ticstep);
103
104
105
106
107
  while (1) {
    if (ticstep / (allocated_ymax - allocated_ymin)
                * (allocated_plot_height - 1) > 20) break;
    ticstep *= 2;
  }
Cedric Roux's avatar
Cedric Roux committed
108
109
  LOGD("post y ticstep %g\n", ticstep);
  LOGD("ymin/max %g %g height wanted allocated %d %d alloc ymin/max %g %g ticstep %g\n", this->ymin, this->ymax, wanted_plot_height, allocated_plot_height, allocated_ymin, allocated_ymax, ticstep);
110
111
112
113
114
115
116
117
118
  kmin = ceil(allocated_ymin / ticstep);
  kmax = floor(allocated_ymax / ticstep);
  for (k = kmin; k <= kmax; k++) {
    char v[64];
    int vwidth, dummy;
    float y = (k * ticstep - allocated_ymin) /
              (allocated_ymax - allocated_ymin) *
              (allocated_plot_height - 1);
    sprintf(v, "%g", k * ticstep);
Cedric Roux's avatar
Cedric Roux committed
119
    x_text_get_dimensions(g->x, DEFAULT_FONT, v, &vwidth, &dummy, &dummy);
120
121
    x_draw_line(g->x, g->xwin, FOREGROUND_COLOR,
        this->common.x + this->vrule_width,
122
        this->common.y + FLIP(y),
123
        this->common.x + this->vrule_width + 5,
124
        this->common.y + FLIP(y));
Cedric Roux's avatar
Cedric Roux committed
125
    x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
126
        this->common.x + this->vrule_width - vwidth - 2,
127
        this->common.y + FLIP(y) - this->label_height/2+this->label_baseline,
128
129
130
131
        v);
  }

  /* label at bottom, in the middle */
Cedric Roux's avatar
Cedric Roux committed
132
  x_draw_string(g->x, g->xwin, DEFAULT_FONT, FOREGROUND_COLOR,
133
134
135
136
      this->common.x + (this->common.width - this->label_width) / 2,
      this->common.y + this->common.height - this->label_height
          + this->label_baseline,
      this->label);
Cedric Roux's avatar
Cedric Roux committed
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  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);
Cedric Roux's avatar
Cedric Roux committed
156
  }
157
158
159
160
161
162
163
}

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 */
Cedric Roux's avatar
Cedric Roux committed
164
  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);
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
}

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") */
Cedric Roux's avatar
Cedric Roux committed
180
181
  x_text_get_dimensions(g->x, DEFAULT_FONT, label,
      &w->label_width, &w->label_height, &w->label_baseline);
Cedric Roux's avatar
Cedric Roux committed
182
  LOGD("XY PLOT label wh %d %d\n", w->label_width, w->label_height);
183
184
185
186
187
188
189
190
191

  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;
192
193
  w->plots = NULL;
  w->nplots = 0;
194
195
196
197
198
199
200
201

  w->common.paint = paint;
  w->common.hints = hints;

  gunlock(g);

  return w;
}
Cedric Roux's avatar
Cedric Roux committed
202
203
204
205
206

/*************************************************************************/
/*                           public functions                            */
/*************************************************************************/

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
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;
}

Cedric Roux's avatar
Cedric Roux committed
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
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);
}

250
void xy_plot_set_points(gui *_gui, widget *_this, int plot,
Cedric Roux's avatar
Cedric Roux committed
251
252
253
254
255
256
257
    int npoints, float *x, float *y)
{
  struct gui *g = _gui;
  struct xy_plot_widget *this = _this;

  glock(g);

258
259
260
  if (npoints != this->plots[plot].npoints) {
    free(this->plots[plot].x);
    free(this->plots[plot].y);
261
    this->plots[plot].x = calloc(npoints, sizeof(float));
Cedric Roux's avatar
Cedric Roux committed
262
    if (this->plots[plot].x == NULL) abort();
263
    this->plots[plot].y = calloc(npoints, sizeof(float));
Cedric Roux's avatar
Cedric Roux committed
264
    if (this->plots[plot].y == NULL) abort();
265
    this->plots[plot].npoints = npoints;
Cedric Roux's avatar
Cedric Roux committed
266
267
  }

268
269
  memcpy(this->plots[plot].x, x, npoints * sizeof(float));
  memcpy(this->plots[plot].y, y, npoints * sizeof(float));
Cedric Roux's avatar
Cedric Roux committed
270
271
272
273
274

  send_event(g, DIRTY, this->common.id);

  gunlock(g);
}
275
276
277
278
279
280
281
282

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);

283
284
285
286
287
288
289
  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;
  }
290
291
292

  gunlock(g);
}