1 /*
2 * Copyright (c) 2005-2010 Hypertriton, Inc. <http://hypertriton.com/>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include <agar/config/enable_gui.h>
27 #ifdef ENABLE_GUI
28
29 /*
30 * Graphical plotter widget.
31 */
32
33 #include <agar/core/core.h>
34 #include <agar/gui/widget.h>
35 #include <agar/gui/hsvpal.h>
36 #include <agar/gui/table.h>
37 #include <agar/gui/notebook.h>
38 #include <agar/gui/separator.h>
39 #include <agar/gui/radio.h>
40 #include <agar/gui/numerical.h>
41 #include <agar/gui/primitive.h>
42
43 #include <agar/math/m.h>
44 #include <agar/math/m_plotter.h>
45 #include <agar/math/m_gui.h>
46
47 #include <stdarg.h>
48 #include <string.h>
49
50 M_Plotter *
M_PlotterNew(void * parent,Uint flags)51 M_PlotterNew(void *parent, Uint flags)
52 {
53 M_Plotter *pl;
54
55 pl = Malloc(sizeof(M_Plotter));
56 AG_ObjectInit(pl, &mPlotterClass);
57 pl->flags |= flags;
58
59 if (flags & M_PLOTTER_HFILL) WIDGET(pl)->flags |= AG_WIDGET_HFILL;
60 if (flags & M_PLOTTER_VFILL) WIDGET(pl)->flags |= AG_WIDGET_VFILL;
61
62 AG_ObjectAttach(parent, pl);
63 return (pl);
64 }
65
66 static void
KeyDown(AG_Event * event)67 KeyDown(AG_Event *event)
68 {
69 M_Plotter *ptr = AG_SELF();
70 int keysym = AG_INT(1);
71
72 switch (keysym) {
73 case AG_KEY_0:
74 case AG_KEY_1:
75 ptr->yScale = 1.0;
76 AG_Redraw(ptr);
77 break;
78 case AG_KEY_EQUALS:
79 ptr->yScale += 0.125;
80 AG_Redraw(ptr);
81 break;
82 case AG_KEY_MINUS:
83 ptr->yScale -= 0.125;
84 AG_Redraw(ptr);
85 break;
86 }
87 if (ptr->yScale <= 0.125) { ptr->yScale = 0.125; }
88 }
89
90 static __inline__ int
MouseOverPlotItem(M_Plotter * ptr,M_Plot * pl,int x,int y)91 MouseOverPlotItem(M_Plotter *ptr, M_Plot *pl, int x, int y)
92 {
93 AG_Surface *lbl;
94
95 if (pl->label < 0) { return (0); }
96 lbl = WSURFACE(ptr,pl->label);
97 return (x >= pl->xLabel && x <= (pl->xLabel + lbl->w) &&
98 y >= pl->yLabel && y <= (pl->yLabel + lbl->h));
99 }
100
101 static void
MouseMotion(AG_Event * event)102 MouseMotion(AG_Event *event)
103 {
104 M_Plotter *ptr = AG_SELF();
105 int x = AG_INT(1);
106 int y = AG_INT(2);
107 int dy = AG_INT(4);
108 int state = AG_INT(5);
109 M_Plot *pl;
110
111 if (!AG_WidgetRelativeArea(ptr, x, y))
112 return;
113
114 TAILQ_FOREACH(pl, &ptr->plots, plots) {
115 if (pl->flags & M_PLOT_SELECTED &&
116 state & AG_MOUSE_LEFT) {
117 pl->yOffs += dy;
118 AG_Redraw(ptr);
119 }
120 if (MouseOverPlotItem(ptr, pl, x, y)) {
121 if (!(pl->flags & M_PLOT_MOUSEOVER)) {
122 pl->flags |= M_PLOT_MOUSEOVER;
123 AG_Redraw(ptr);
124 }
125 } else {
126 if (pl->flags & M_PLOT_MOUSEOVER) {
127 pl->flags &= ~M_PLOT_MOUSEOVER;
128 AG_Redraw(ptr);
129 }
130 }
131 }
132 }
133
134 #if 0
135 static void
136 MouseButtonUp(AG_Event *event)
137 {
138 M_Plotter *ptr = AG_SELF();
139 int button = AG_INT(1);
140 int x = AG_INT(2);
141 int y = AG_INT(3);
142 M_Plot *pl;
143
144 switch (button) {
145 case AG_MOUSE_LEFT:
146 TAILQ_FOREACH(pl, &ptr->plots, plots) {
147 pl->flags &= ~M_PLOT_DRAGGING;
148 }
149 break;
150 }
151 }
152 #endif
153
154 void
M_PlotUpdateLabel(M_Plot * pl)155 M_PlotUpdateLabel(M_Plot *pl)
156 {
157 M_Plotter *ptr = pl->plotter;
158
159 if (pl->label >= 0) {
160 AG_WidgetUnmapSurface(ptr, pl->label);
161 }
162 AG_TextFont(ptr->font);
163 AG_TextColor(pl->color);
164 pl->label = (pl->label_txt == NULL) ? -1 :
165 AG_WidgetMapSurface(ptr, AG_TextRender(pl->label_txt));
166 AG_Redraw(ptr);
167 }
168
169 static void
UpdateLabel(AG_Event * event)170 UpdateLabel(AG_Event *event)
171 {
172 M_PlotUpdateLabel(AG_PTR(1));
173 }
174
175 static void
UpdatePlotTbl(AG_Event * event)176 UpdatePlotTbl(AG_Event *event)
177 {
178 AG_Table *tbl = AG_SELF();
179 M_Plot *pl = AG_PTR(1);
180 Uint i, j;
181
182 AG_TableBegin(tbl);
183 for (i = 0; i < pl->n; i++) {
184 if (pl->type == M_PLOT_VECTORS) {
185 M_Vector *v = pl->data.v[i];
186 for (j = 0; j < v->m; j++) {
187 AG_TableAddRow(tbl, "%u[%u]:%f", i, j,
188 M_VecGetElement(v,j));
189 }
190 } else {
191 AG_TableAddRow(tbl, "%u:%f", pl->data.r[i]);
192 }
193 }
194 AG_TableEnd(tbl);
195 }
196
197 AG_Window *
M_PlotSettings(M_Plot * pl)198 M_PlotSettings(M_Plot *pl)
199 {
200 AG_Window *win;
201 AG_Notebook *nb;
202 AG_NotebookTab *ntab;
203 const char *type_names[] = {
204 N_("Points"),
205 N_("Linear interpolation"),
206 N_("Cubic spline interpolation"),
207 NULL
208 };
209
210 if ((win = AG_WindowNewNamed(0, "plotter%p", pl)) == NULL) {
211 return (NULL);
212 }
213 AG_WindowSetCaption(win, _("Plot: <%s>"), pl->label_txt);
214 AG_WindowSetPosition(win, AG_WINDOW_MIDDLE_LEFT, 0);
215
216 nb = AG_NotebookNew(win, AG_NOTEBOOK_EXPAND);
217 ntab = AG_NotebookAdd(nb, _("Trace"), AG_BOX_VERT);
218 {
219 AG_RadioNewUint(ntab, 0, type_names, (void *)&pl->type);
220 M_NumericalNewReal(ntab, 0, NULL, _("X-scale: "), &pl->xScale);
221 M_NumericalNewReal(ntab, 0, NULL, _("Y-scale: "), &pl->yScale);
222 AG_SeparatorNew(ntab, AG_SEPARATOR_HORIZ);
223 AG_NumericalNewInt(ntab, 0, "px", _("X-offset: "), &pl->xOffs);
224 AG_NumericalNewInt(ntab, 0, "px", _("Y-offset: "), &pl->yOffs);
225 }
226 ntab = AG_NotebookAdd(nb, _("Color"), AG_BOX_VERT);
227 {
228 AG_HSVPal *pal;
229
230 pal = AG_HSVPalNew(ntab, AG_HSVPAL_EXPAND);
231 AG_BindUint8(pal, "RGBAv", (Uint8 *)&pl->color);
232 AG_SetEvent(pal, "h-changed", UpdateLabel, "%p", pl);
233 AG_SetEvent(pal, "sv-changed", UpdateLabel, "%p", pl);
234 }
235 ntab = AG_NotebookAdd(nb, _("Table"), AG_BOX_VERT);
236 {
237 AG_Table *tbl;
238
239 tbl = AG_TableNewPolled(ntab, AG_TABLE_MULTI|AG_TABLE_EXPAND,
240 UpdatePlotTbl, "%p", pl);
241 AG_TableAddCol(tbl, _("#"), "<88888>", NULL);
242 AG_TableAddCol(tbl, _("Value"), NULL, NULL);
243 }
244 AG_WindowShow(win);
245 return (win);
246 }
247
248 static void
ShowPlotSettings(AG_Event * event)249 ShowPlotSettings(AG_Event *event)
250 {
251 M_Plot *pl = AG_PTR(1);
252
253 M_PlotSettings(pl);
254 }
255
256 static void
MouseButtonDown(AG_Event * event)257 MouseButtonDown(AG_Event *event)
258 {
259 M_Plotter *ptr = AG_SELF();
260 M_Plot *pl, *opl;
261 int button = AG_INT(1);
262 int x = AG_INT(2);
263 int y = AG_INT(3);
264
265 switch (button) {
266 case AG_MOUSE_LEFT:
267 AG_WidgetFocus(ptr);
268 if (AG_GetModState(ptr) & (AG_KEYMOD_CTRL|AG_KEYMOD_SHIFT)) {
269 TAILQ_FOREACH(pl, &ptr->plots, plots) {
270 if (!MouseOverPlotItem(ptr, pl, x, y)) {
271 continue;
272 }
273 AG_INVFLAGS(pl->flags, M_PLOT_SELECTED);
274 AG_Redraw(ptr);
275 }
276 } else {
277 TAILQ_FOREACH(pl, &ptr->plots, plots) {
278 if (MouseOverPlotItem(ptr, pl, x, y))
279 break;
280 }
281 if (pl != NULL) {
282 TAILQ_FOREACH(opl, &ptr->plots, plots) {
283 opl->flags &= ~M_PLOT_SELECTED;
284 }
285 pl->flags |= M_PLOT_SELECTED;
286 AG_Redraw(ptr);
287 }
288 }
289 break;
290 case AG_MOUSE_RIGHT:
291 AG_WidgetFocus(ptr);
292 TAILQ_FOREACH(pl, &ptr->plots, plots) {
293 if (MouseOverPlotItem(ptr, pl, x, y)) {
294 AG_PopupMenu *pm;
295
296 pm = AG_PopupNew(ptr);
297 AG_MenuUintFlags(pm->root, _("Display plot"), NULL,
298 &pl->flags, M_PLOT_HIDDEN, 1);
299 AG_MenuAction(pm->root, _("Plot settings"), NULL,
300 ShowPlotSettings, "%p", pl);
301 AG_PopupShowAt(pm, x,y);
302 break;
303 }
304 }
305 break;
306 case AG_MOUSE_WHEELDOWN:
307 TAILQ_FOREACH(pl, &ptr->plots, plots) {
308 if (! (pl->flags & M_PLOT_SELECTED)) { continue; }
309 pl->yScale -= 0.250;
310 AG_Redraw(ptr);
311 }
312 break;
313 case AG_MOUSE_WHEELUP:
314 TAILQ_FOREACH(pl, &ptr->plots, plots) {
315 if (! (pl->flags & M_PLOT_SELECTED)) { continue; }
316 pl->yScale += 0.250;
317 AG_Redraw(ptr);
318 }
319 break;
320 }
321 if (ptr->xScale <= 0.0625) { ptr->xScale = 0.0625; }
322 if (ptr->yScale <= 0.0625) { ptr->yScale = 0.0625; }
323 }
324
325 static void
UpdateXBar(AG_Event * event)326 UpdateXBar(AG_Event *event)
327 {
328 M_Plotter *ptr = AG_PTR(1);
329 int value = AG_GetInt(ptr->hbar, "value");
330
331 if (value >= ptr->xMax - WIDTH(ptr)) {
332 ptr->flags |= M_PLOTTER_SCROLL;
333 } else {
334 ptr->flags &= ~M_PLOTTER_SCROLL;
335 }
336 }
337
338 static void
Init(void * obj)339 Init(void *obj)
340 {
341 M_Plotter *ptr = obj;
342
343 WIDGET(ptr)->flags |= AG_WIDGET_FOCUSABLE|AG_WIDGET_USE_TEXT;
344
345 ptr->type = M_PLOT_2D;
346 ptr->flags = 0;
347 ptr->xMax = 0;
348 ptr->yMin = 0.0;
349 ptr->yMax = 0.0;
350 ptr->xOffs = 0;
351 ptr->yOffs = 0;
352 ptr->wPre = 128;
353 ptr->hPre = 64;
354 ptr->xScale = 1.0;
355 ptr->yScale = 1.0;
356 ptr->font = AG_FetchFont(NULL, -1, -1);
357 ptr->r = AG_RECT(0,0,0,0);
358 TAILQ_INIT(&ptr->plots);
359
360 ptr->vMin = M_New(3,1);
361 ptr->vMax = M_New(3,1);
362 M_SetZero(ptr->vMin);
363 M_SetZero(ptr->vMax);
364
365 ptr->curColor = 0;
366 ptr->colors[0] = AG_ColorRGB(255, 255, 255);
367 ptr->colors[1] = AG_ColorRGB(0, 250, 0);
368 ptr->colors[2] = AG_ColorRGB(250, 250, 0);
369 ptr->colors[3] = AG_ColorRGB(0, 118, 163);
370 ptr->colors[4] = AG_ColorRGB(175, 143, 44);
371 ptr->colors[5] = AG_ColorRGB(169, 172, 182);
372 ptr->colors[6] = AG_ColorRGB(255, 255, 255);
373 ptr->colors[7] = AG_ColorRGB(59, 122, 87);
374 ptr->colors[8] = AG_ColorRGB(163, 151, 180);
375 ptr->colors[9] = AG_ColorRGB(249, 234, 243);
376 ptr->colors[10] = AG_ColorRGB(157, 229, 255);
377 ptr->colors[11] = AG_ColorRGB(223, 190, 111);
378 ptr->colors[12] = AG_ColorRGB(79, 168, 61);
379 ptr->colors[13] = AG_ColorRGB(234, 147, 115);
380 ptr->colors[14] = AG_ColorRGB(127, 255, 212);
381 ptr->colors[15] = AG_ColorRGB(218, 99, 4);
382
383 ptr->hbar = AG_ScrollbarNew(ptr, AG_SCROLLBAR_HORIZ, AG_SCROLLBAR_EXCL);
384 ptr->vbar = AG_ScrollbarNew(ptr, AG_SCROLLBAR_VERT, AG_SCROLLBAR_EXCL);
385 AG_BindInt(ptr->hbar, "value", &ptr->xOffs);
386 AG_BindInt(ptr->hbar, "visible", &ptr->r.w);
387 AG_BindInt(ptr->hbar, "max", &ptr->xMax);
388 AG_SetEvent(ptr->hbar, "scrollbar-changed", UpdateXBar, "%p", ptr);
389
390 AG_BindInt(ptr->vbar, "value", &ptr->yOffs);
391 /* AG_BindInt(ptr->vbar, "max", &ptr->yMax); */
392 AG_SetInt(ptr->hbar, "min", 0);
393 AG_SetInt(ptr->vbar, "min", 0);
394
395 AG_SetEvent(ptr, "key-down", KeyDown, NULL);
396 AG_SetEvent(ptr, "mouse-button-down", MouseButtonDown, NULL);
397 /* AG_SetEvent(ptr, "mouse-button-up", MouseButtonUp, NULL); */
398 AG_SetEvent(ptr, "mouse-motion", MouseMotion, NULL);
399 }
400
401 static void
Destroy(void * obj)402 Destroy(void *obj)
403 {
404 M_Plotter *ptr = obj;
405 M_Plot *plot, *plotNext;
406 M_PlotLabel *plbl, *plblNext;
407 Uint i;
408
409 for (plot = TAILQ_FIRST(&ptr->plots);
410 plot != TAILQ_END(&ptr->plots);
411 plot = plotNext) {
412 plotNext = TAILQ_NEXT(plot, plots);
413 for (plbl = TAILQ_FIRST(&plot->labels);
414 plbl != TAILQ_END(&plot->labels);
415 plbl = plblNext) {
416 plblNext = TAILQ_NEXT(plbl, labels);
417 free(plbl);
418 }
419 if (plot->type == M_PLOT_VECTORS) {
420 for (i = 0; i < plot->n; i++) {
421 M_VecFree(plot->data.v[i]);
422 }
423 Free(plot->data.v);
424 } else {
425 Free(plot->data.r);
426 }
427 free(plot);
428 }
429
430 M_Free(ptr->vMin);
431 M_Free(ptr->vMax);
432 }
433
434 void
M_PlotterSizeHint(M_Plotter * ptr,Uint w,Uint h)435 M_PlotterSizeHint(M_Plotter *ptr, Uint w, Uint h)
436 {
437 ptr->wPre = w;
438 ptr->hPre = h;
439 }
440
441 static void
SizeRequest(void * obj,AG_SizeReq * r)442 SizeRequest(void *obj, AG_SizeReq *r)
443 {
444 M_Plotter *ptr = obj;
445
446 r->w = ptr->wPre;
447 r->h = ptr->hPre;
448 if (ptr->flags & M_PLOTTER_SCROLL)
449 ptr->xOffs = 0;
450 }
451
452 static int
SizeAllocate(void * obj,const AG_SizeAlloc * a)453 SizeAllocate(void *obj, const AG_SizeAlloc *a)
454 {
455 M_Plotter *ptr = obj;
456 AG_SizeAlloc aBar;
457
458 if (a->w < 2 || a->h < 2)
459 return (-1);
460
461 ptr->r.w = a->w;
462 ptr->r.h = a->h;
463
464 aBar.x = 0;
465 aBar.y = a->h - ptr->hbar->width;
466 aBar.w = a->w;
467 aBar.h = ptr->hbar->width;
468 AG_WidgetSizeAlloc(ptr->hbar, &aBar);
469 ptr->r.h -= HEIGHT(ptr->hbar);
470
471 aBar.x = a->w - ptr->vbar->width;
472 aBar.y = 0;
473 aBar.w = ptr->vbar->width;
474 aBar.h = a->h - ptr->hbar->width;
475 AG_WidgetSizeAlloc(ptr->vbar, &aBar);
476 ptr->r.w -= WIDTH(ptr->vbar);
477
478 return (0);
479 }
480
481 static __inline__ M_Real
ScaleReal(M_Plotter * ptr,M_Plot * pl,M_Real r)482 ScaleReal(M_Plotter *ptr, M_Plot *pl, M_Real r)
483 {
484 return (r*(ptr->yScale*pl->yScale));
485 }
486
487 static void
Draw(void * obj)488 Draw(void *obj)
489 {
490 M_Plotter *ptr = obj;
491 AG_Driver *drv = WIDGET(ptr)->drv;
492 AG_Rect rw = AG_RECT(1, 1, WIDTH(ptr)-2, HEIGHT(ptr)-2);
493 M_Plot *pl;
494 M_PlotLabel *plbl;
495 Uint i;
496 int y0 = ptr->r.h/2;
497
498 AG_DrawBox(ptr, rw, -1, WCOLOR(ptr,0));
499
500 AG_PushClipRect(ptr, rw);
501
502 AG_DrawLineH(ptr, 1, ptr->r.w-2, y0, ptr->colors[0]);
503 AG_DrawLineV(ptr, ptr->xMax-1, 30, ptr->r.h-30, ptr->colors[0]);
504
505 /* First pass */
506 TAILQ_FOREACH(pl, &ptr->plots, plots) {
507 int x = pl->xOffs - ptr->xOffs;
508 int y, py = y0+pl->yOffs+ptr->yOffs;
509
510 if (pl->label >= 0) {
511 AG_Surface *su = WSURFACE(ptr,pl->label);
512
513 if (pl->flags & M_PLOT_SELECTED) {
514 AG_DrawRectOutline(ptr,
515 AG_RECT(pl->xLabel-2, pl->yLabel-2,
516 su->w+4, su->h+4),
517 pl->color);
518 } else if (pl->flags & M_PLOT_MOUSEOVER) {
519 AG_DrawRectOutline(ptr,
520 AG_RECT(pl->xLabel-2, pl->yLabel-2,
521 su->w+4, su->h+4),
522 WCOLOR(ptr,TEXT_COLOR));
523 }
524 AG_WidgetBlitSurface(ptr, pl->label, pl->xLabel,
525 pl->yLabel);
526 }
527 if (pl->flags & M_PLOT_HIDDEN) {
528 continue;
529 }
530 switch (pl->type) {
531 case M_PLOT_POINTS:
532 for (i = 0; i < pl->n; i++, x++) {
533 if (x < 0) { continue; }
534 y = ScaleReal(ptr, pl, pl->data.r[i]);
535 if ((AGDRIVER_CLASS(drv)->flags &
536 AG_DRIVER_OPENGL)) {
537 /* TODO */
538 } else {
539 AG_PutPixel(ptr, x,
540 y0 - y + pl->yOffs + ptr->yOffs,
541 pl->color);
542 }
543 if (x > ptr->r.w) { break; }
544 }
545 break;
546 case M_PLOT_LINEAR:
547 for (i = 0; i < pl->n; i++, x++) {
548 if (x < 0) { continue; }
549 y = ScaleReal(ptr, pl, pl->data.r[i]);
550 AG_DrawLine(ptr, x-1, py, x,
551 y0 - y + pl->yOffs + ptr->yOffs,
552 pl->color);
553 py = y0 - y + pl->yOffs + ptr->yOffs;
554 if (x > ptr->r.w) { break; }
555 }
556 break;
557 default:
558 break;
559 }
560 }
561 /* Second pass */
562 TAILQ_FOREACH(pl, &ptr->plots, plots) {
563 if (pl->flags & M_PLOT_HIDDEN) {
564 continue;
565 }
566 TAILQ_FOREACH(plbl, &pl->labels, labels) {
567 AG_Surface *su = WSURFACE(ptr,plbl->text_surface);
568 int xLbl, yLbl;
569 AG_Color colBG, colLine;
570
571 colBG = WCOLOR(ptr,0);
572 colBG.a = 200;
573 colLine = pl->color;
574 colLine.a /= 2;
575
576 switch (plbl->type) {
577 case M_LABEL_X:
578 xLbl = plbl->x - ptr->xOffs - pl->xOffs;
579 yLbl = ptr->r.h - su->h - 4 - plbl->y;
580 AG_DrawLineBlended(ptr,
581 xLbl, 1,
582 xLbl, ptr->r.h-2,
583 colLine, AG_ALPHA_SRC);
584 break;
585 case M_LABEL_Y:
586 xLbl = plbl->x - ptr->xOffs - pl->xOffs;
587 yLbl = ptr->r.h - su->h - 4 - plbl->y;
588 break;
589 case M_LABEL_FREE:
590 xLbl = 4 + plbl->x - ptr->xOffs - pl->xOffs;
591 yLbl = 4 + plbl->y;
592 break;
593 default:
594 xLbl = 4 + plbl->x;
595 yLbl = 4 + plbl->y;
596 break;
597 }
598 AG_DrawRect(ptr,
599 AG_RECT(xLbl+2, yLbl, su->w, su->h),
600 colBG);
601 AG_WidgetBlitSurface(ptr, plbl->text_surface,
602 xLbl+2, yLbl);
603 }
604 }
605
606 AG_PopClipRect(ptr);
607
608 AG_WidgetDraw(ptr->hbar);
609 AG_WidgetDraw(ptr->vbar);
610 }
611
612 void
M_PlotClear(M_Plot * pl)613 M_PlotClear(M_Plot *pl)
614 {
615 pl->data.r = Realloc(pl->data.r, sizeof(M_Real));
616 pl->n = 0;
617 AG_Redraw(pl->plotter);
618 }
619
620 void
M_PlotReal(M_Plot * pl,M_Real v)621 M_PlotReal(M_Plot *pl, M_Real v)
622 {
623 M_Plotter *ptr = pl->plotter;
624
625 pl->data.r = Realloc(pl->data.r, (pl->n+1)*sizeof(M_Real));
626 pl->data.r[pl->n] = v;
627 if (++pl->n > ptr->xMax) { ptr->xMax = pl->n; }
628 if (v > ptr->yMax) { ptr->yMax = v; }
629 if (v < ptr->yMin) { ptr->yMin = v; }
630 AG_Redraw(ptr);
631 }
632
633 void
M_PlotRealv(M_Plot * pl,Uint n,const M_Real * vp)634 M_PlotRealv(M_Plot *pl, Uint n, const M_Real *vp)
635 {
636 M_Plotter *ptr = pl->plotter;
637 Uint i;
638
639 pl->data.r = Realloc(pl->data.r, (pl->n+n)*sizeof(M_Real));
640 memcpy(&pl->data.r[pl->n], vp, n*sizeof(M_Real));
641 if ((pl->n += n) > ptr->xMax) { ptr->xMax = pl->n; }
642 for (i = 0; i < n; i++) {
643 if (vp[i] > ptr->yMax) { ptr->yMax = vp[i]; }
644 if (vp[i] < ptr->yMin) { ptr->yMin = vp[i]; }
645 }
646 AG_Redraw(ptr);
647 }
648
649 static void
VectorMinimum(M_Vector * c,const M_Vector * a,const M_Vector * b)650 VectorMinimum(M_Vector *c, const M_Vector *a, const M_Vector *b)
651 {
652 Uint i;
653
654 for (i = 0; i < c->m; i++) {
655 if (M_VecGet(a, i) < M_VecGet(b,i)) {
656 M_VecSet(c, i, M_VecGet(a, i));
657 } else {
658 M_VecSet(c, i, M_VecGet(b, i));
659 }
660 }
661 }
662
663 static void
VectorMaximum(M_Vector * c,const M_Vector * a,const M_Vector * b)664 VectorMaximum(M_Vector *c, const M_Vector * a, const M_Vector *b)
665 {
666 Uint i;
667
668 for (i = 0; i < c->m; i++) {
669 if (M_VecGet(a, i) > M_VecGet(b,i)) {
670 M_VecSet(c, i, M_VecGet(a, i));
671 } else {
672 M_VecSet(c, i, M_VecGet(b, i));
673 }
674 }
675 }
676
677 void
M_PlotVector(M_Plot * pl,const M_Vector * v)678 M_PlotVector(M_Plot *pl, const M_Vector *v)
679 {
680 M_Plotter *ptr = pl->plotter;
681 int i;
682
683 pl->data.v = Realloc(pl->data.v, (pl->n)*sizeof(M_Vector *));
684 pl->data.v[pl->n] = M_VecNew(v->m);
685 VectorMinimum(ptr->vMin, ptr->vMin, v);
686 VectorMaximum(ptr->vMax, ptr->vMax, v);
687 for (i = 0; i < v->m; i++) {
688 M_Real *e = M_VecGetElement(pl->data.v[pl->n], i);
689 *e = M_VecGet(v, i);
690 }
691 pl->n++;
692 AG_Redraw(ptr);
693 }
694
695 void
M_PlotVectorv(M_Plot * pl,Uint n,const M_Vector ** vp)696 M_PlotVectorv(M_Plot *pl, Uint n, const M_Vector **vp)
697 {
698 M_Plotter *ptr = pl->plotter;
699 Uint i;
700
701 pl->data.v = Realloc(pl->data.v, (pl->n+n)*sizeof(M_Vector));
702 for (i = 0; i < n; i++) {
703 pl->data.v[pl->n+i] = M_VecNew(vp[i]->m);
704 VectorMinimum(ptr->vMin, ptr->vMin, vp[i]);
705 VectorMaximum(ptr->vMax, ptr->vMax, vp[i]);
706 M_Copy(pl->data.v[pl->n+i], vp[i]);
707 }
708 pl->n += n;
709 AG_Redraw(ptr);
710 }
711
712 static __inline__ void
M_PlotDerivative(M_Plotter * ptr,M_Plot * dp)713 M_PlotDerivative(M_Plotter *ptr, M_Plot *dp)
714 {
715 M_Plot *p = dp->src.plot;
716
717 if (p->n >= 2) {
718 M_PlotReal(dp, p->data.r[p->n-1] - p->data.r[p->n-2]);
719 } else {
720 M_PlotReal(dp, p->data.r[p->n-1]);
721 }
722 }
723
724 static __inline__ M_Real
PlotVariableVFS(M_Plotter * ptr,M_Plot * pl)725 PlotVariableVFS(M_Plotter *ptr, M_Plot *pl)
726 {
727 AG_Variable *V;
728 M_Real rv;
729
730 if ((V = AG_GetVariableVFS(pl->src.varVFS.vfs, pl->src.varVFS.key))
731 == NULL) {
732 AG_Verbose("Plot \"%s\": %s\n", pl->src.varVFS.key,
733 AG_GetError());
734 return (0.0);
735 }
736 switch (AG_VARIABLE_TYPE(V)) {
737 case AG_VARIABLE_FLOAT: rv = (M_Real)V->data.flt; break;
738 case AG_VARIABLE_DOUBLE: rv = (M_Real)V->data.dbl; break;
739 case AG_VARIABLE_UINT: rv = (M_Real)V->data.u; break;
740 case AG_VARIABLE_INT: rv = (M_Real)V->data.i; break;
741 case AG_VARIABLE_UINT8: rv = (M_Real)V->data.u8; break;
742 case AG_VARIABLE_SINT8: rv = (M_Real)V->data.s8; break;
743 case AG_VARIABLE_UINT16: rv = (M_Real)V->data.u16; break;
744 case AG_VARIABLE_SINT16: rv = (M_Real)V->data.s16; break;
745 case AG_VARIABLE_UINT32: rv = (M_Real)V->data.u32; break;
746 case AG_VARIABLE_SINT32: rv = (M_Real)V->data.s32; break;
747 default:
748 AG_Verbose("Plot \"%s\": Invalid type\n", pl->src.varVFS.key);
749 rv = 0.0;
750 break;
751 }
752 AG_UnlockVariable(V);
753 return (rv);
754 }
755
756 void
M_PlotterUpdate(M_Plotter * ptr)757 M_PlotterUpdate(M_Plotter *ptr)
758 {
759 M_Plot *pl;
760 M_Real *v;
761
762 TAILQ_FOREACH(pl, &ptr->plots, plots) {
763 switch (pl->src_type) {
764 case M_PLOT_MANUALLY:
765 break;
766 case M_PLOT_FROM_VARIABLE_VFS:
767 M_PlotReal(pl, PlotVariableVFS(ptr, pl));
768 break;
769 case M_PLOT_FROM_REAL:
770 M_PlotReal(pl, *pl->src.real);
771 break;
772 case M_PLOT_FROM_INT:
773 M_PlotReal(pl, (M_Real)(*pl->src.integer));
774 break;
775 case M_PLOT_FROM_COMPONENT:
776 if (!M_ENTRY_EXISTS(pl->src.com.A, pl->src.com.i,
777 pl->src.com.j)) {
778 break;
779 }
780 v = M_GetElement(pl->src.com.A, pl->src.com.i,
781 pl->src.com.j);
782 M_PlotReal(pl, *v);
783 break;
784 case M_PLOT_DERIVATIVE:
785 M_PlotDerivative(ptr, pl);
786 break;
787 }
788 }
789 if (ptr->flags & M_PLOTTER_SCROLL) {
790 ptr->xOffs++;
791 AG_Redraw(ptr);
792 }
793 }
794
795 M_PlotLabel *
M_PlotLabelNew(M_Plot * pl,enum m_plot_label_type type,Uint x,Uint y,const char * fmt,...)796 M_PlotLabelNew(M_Plot *pl, enum m_plot_label_type type, Uint x, Uint y,
797 const char *fmt, ...)
798 {
799 M_Plotter *ptr = pl->plotter;
800 M_PlotLabel *plbl;
801 va_list args;
802
803 plbl = Malloc(sizeof(M_PlotLabel));
804 plbl->type = type;
805 plbl->x = x;
806 plbl->y = y;
807
808 va_start(args, fmt);
809 Vsnprintf(plbl->text, sizeof(plbl->text), fmt, args);
810 va_end(args);
811
812 AG_PushTextState();
813 AG_TextFont(ptr->font);
814 AG_TextColor(pl->color);
815 plbl->text_surface = AG_WidgetMapSurface(ptr, AG_TextRender(plbl->text));
816 AG_PopTextState();
817
818 TAILQ_INSERT_TAIL(&pl->labels, plbl, labels);
819
820 AG_Redraw(ptr);
821 return (plbl);
822 }
823
824 void
M_PlotLabelSetText(M_Plot * pl,M_PlotLabel * plbl,const char * fmt,...)825 M_PlotLabelSetText(M_Plot *pl, M_PlotLabel *plbl, const char *fmt, ...)
826 {
827 M_Plotter *ptr = pl->plotter;
828 va_list args;
829
830 va_start(args, fmt);
831 Vsnprintf(plbl->text, sizeof(plbl->text), fmt, args);
832 va_end(args);
833
834 AG_WidgetUnmapSurface(ptr, plbl->text_surface);
835 AG_TextFont(ptr->font);
836 AG_TextColor(pl->color);
837 plbl->text_surface = AG_WidgetMapSurface(ptr, AG_TextRender(plbl->text));
838 AG_Redraw(ptr);
839 }
840
841 /* Replace plot labels matching the given text. */
842 M_PlotLabel *
M_PlotLabelReplace(M_Plot * pl,enum m_plot_label_type type,Uint x,Uint y,const char * fmt,...)843 M_PlotLabelReplace(M_Plot *pl, enum m_plot_label_type type, Uint x, Uint y,
844 const char *fmt, ...)
845 {
846 char text[M_PLOTTER_LABEL_MAX];
847 M_PlotLabel *plbl;
848 va_list args;
849
850 va_start(args, fmt);
851 Vsnprintf(text, sizeof(text), fmt, args);
852 va_end(args);
853
854 TAILQ_FOREACH(plbl, &pl->labels, labels) {
855 if (strcmp(text, plbl->text) == 0)
856 break;
857 }
858 if (plbl != NULL) {
859 plbl->type = type;
860 switch (plbl->type) {
861 case M_LABEL_X:
862 plbl->x = x;
863 break;
864 case M_LABEL_Y:
865 plbl->y = y;
866 break;
867 case M_LABEL_FREE:
868 case M_LABEL_OVERLAY:
869 plbl->x = x;
870 plbl->y = y;
871 break;
872 }
873 } else {
874 Uint nx = x;
875 Uint ny = y;
876 AG_Surface *su;
877 reposition:
878 /* XXX */
879 /* Do what we can to avoid overlapping labels */
880 TAILQ_FOREACH(plbl, &pl->labels, labels) {
881 if (plbl->x != nx || plbl->y != ny) {
882 continue;
883 }
884 su = WSURFACE(pl->plotter,plbl->text_surface);
885 switch (plbl->type) {
886 case M_LABEL_X:
887 ny += su->h;
888 break;
889 case M_LABEL_Y:
890 case M_LABEL_FREE:
891 case M_LABEL_OVERLAY:
892 nx += su->w;
893 break;
894 }
895 goto reposition;
896 }
897 plbl = M_PlotLabelNew(pl, type, nx, ny, "%s", text);
898 }
899 return (plbl);
900 }
901
902 /* Create a new plot. */
903 M_Plot *
M_PlotNew(M_Plotter * ptr,enum m_plot_type type)904 M_PlotNew(M_Plotter *ptr, enum m_plot_type type)
905 {
906 M_Plot *pl, *pl2;
907
908 pl = Malloc(sizeof(M_Plot));
909 pl->plotter = ptr;
910 pl->flags = 0;
911 pl->type = type;
912 pl->data.r = NULL;
913 pl->n = 0;
914 pl->src_type = M_PLOT_MANUALLY;
915 pl->label = -1;
916 pl->label_txt[0] = '\0';
917 pl->color = ptr->colors[ptr->curColor++];
918 pl->xOffs = 0;
919 pl->yOffs = 0;
920 pl->xScale = 1.0;
921 pl->yScale = 1.0;
922 pl->xLabel = 5;
923 pl->yLabel = 5;
924 TAILQ_FOREACH(pl2, &ptr->plots, plots) {
925 if (pl2->label == -1) { continue; }
926 pl->xLabel += WSURFACE(ptr,pl2->label)->w + 5;
927 }
928 TAILQ_INSERT_TAIL(&ptr->plots, pl, plots);
929 TAILQ_INIT(&pl->labels);
930
931 if (ptr->curColor >= M_PLOTTER_NDEFCOLORS) {
932 ptr->curColor = 0;
933 }
934 AG_Redraw(ptr);
935 return (pl);
936 }
937
938 /* Plot the derivative of existing Plot data. */
939 M_Plot *
M_PlotFromDerivative(M_Plotter * ptr,enum m_plot_type type,M_Plot * plot)940 M_PlotFromDerivative(M_Plotter *ptr, enum m_plot_type type, M_Plot *plot)
941 {
942 M_Plot *pl;
943
944 pl = M_PlotNew(ptr, type);
945 pl->src_type = M_PLOT_DERIVATIVE;
946 pl->src.plot = plot;
947 M_PlotSetLabel(pl, "%s'", plot->label_txt);
948 return (pl);
949 }
950
951 /*
952 * Plot a Variable from a VFS "object:variable-name" path. This allows the
953 * object or variable to safely disappear as long as the VFS root remains.
954 */
955 M_Plot *
M_PlotFromVariableVFS(M_Plotter * ptr,enum m_plot_type type,const char * label,void * vfsRoot,const char * varName)956 M_PlotFromVariableVFS(M_Plotter *ptr, enum m_plot_type type, const char *label,
957 void *vfsRoot, const char *varName)
958 {
959 M_Plot *pl;
960
961 pl = M_PlotNew(ptr, type);
962 pl->src_type = M_PLOT_FROM_VARIABLE_VFS;
963 pl->src.varVFS.vfs = vfsRoot;
964 pl->src.varVFS.key = Strdup(varName);
965 M_PlotSetLabel(pl, "%s", label);
966 return (pl);
967 }
968
969 M_Plot *
M_PlotFromReal(M_Plotter * ptr,enum m_plot_type type,const char * label,M_Real * p)970 M_PlotFromReal(M_Plotter *ptr, enum m_plot_type type, const char *label,
971 M_Real *p)
972 {
973 M_Plot *pl;
974
975 pl = M_PlotNew(ptr, type);
976 pl->src_type = M_PLOT_FROM_REAL;
977 pl->src.real = p;
978 M_PlotSetLabel(pl, "%s", label);
979 return (pl);
980 }
981
982
983 M_Plot *
M_PlotFromInt(M_Plotter * ptr,enum m_plot_type type,const char * label,int * ip)984 M_PlotFromInt(M_Plotter *ptr, enum m_plot_type type, const char *label,
985 int *ip)
986 {
987 M_Plot *pl;
988
989 pl = M_PlotNew(ptr, type);
990 pl->src_type = M_PLOT_FROM_INT;
991 pl->src.integer = ip;
992 M_PlotSetLabel(pl, "%s", label);
993 return (pl);
994 }
995
996 void
M_PlotSetColor(M_Plot * pl,Uint8 r,Uint8 g,Uint8 b)997 M_PlotSetColor(M_Plot *pl, Uint8 r, Uint8 g, Uint8 b)
998 {
999 pl->color = AG_ColorRGB(r,g,b);
1000 AG_Redraw(pl->plotter);
1001 }
1002
1003 void
M_PlotSetLabel(M_Plot * pl,const char * fmt,...)1004 M_PlotSetLabel(M_Plot *pl, const char *fmt, ...)
1005 {
1006 va_list args;
1007
1008 va_start(args, fmt);
1009 Vsnprintf(pl->label_txt, sizeof(pl->label_txt), fmt, args);
1010 va_end(args);
1011
1012 M_PlotUpdateLabel(pl);
1013 }
1014
1015 void
M_PlotSetScale(M_Plot * pl,M_Real xScale,M_Real yScale)1016 M_PlotSetScale(M_Plot *pl, M_Real xScale, M_Real yScale)
1017 {
1018 if (xScale > 0.0) { pl->xScale = xScale; }
1019 if (yScale > 0.0) { pl->yScale = yScale; }
1020 AG_Redraw(pl->plotter);
1021 }
1022
1023 void
M_PlotSetXoffs(M_Plot * pl,int xOffs)1024 M_PlotSetXoffs(M_Plot *pl, int xOffs)
1025 {
1026 pl->xOffs = xOffs;
1027 AG_Redraw(pl->plotter);
1028 }
1029
1030 void
M_PlotSetYoffs(M_Plot * pl,int yOffs)1031 M_PlotSetYoffs(M_Plot *pl, int yOffs)
1032 {
1033 pl->yOffs = yOffs;
1034 AG_Redraw(pl->plotter);
1035 }
1036
1037 void
M_PlotterSetDefaultFont(M_Plotter * ptr,const char * face,int size)1038 M_PlotterSetDefaultFont(M_Plotter *ptr, const char *face, int size)
1039 {
1040 ptr->font = AG_FetchFont(face, size, 0);
1041 AG_Redraw(ptr);
1042 }
1043
1044 void
M_PlotterSetDefaultColor(M_Plotter * ptr,int i,Uint8 r,Uint8 g,Uint8 b)1045 M_PlotterSetDefaultColor(M_Plotter *ptr, int i, Uint8 r, Uint8 g, Uint8 b)
1046 {
1047 ptr->colors[i] = AG_ColorRGB(r,g,b);
1048 AG_Redraw(ptr);
1049 }
1050
1051 void
M_PlotterSetDefaultScale(M_Plotter * ptr,M_Real xScale,M_Real yScale)1052 M_PlotterSetDefaultScale(M_Plotter *ptr, M_Real xScale, M_Real yScale)
1053 {
1054 ptr->xScale = xScale;
1055 ptr->yScale = yScale;
1056 AG_Redraw(ptr);
1057 }
1058
1059 AG_WidgetClass mPlotterClass = {
1060 {
1061 "AG_Widget:M_Plotter",
1062 sizeof(M_Plotter),
1063 { 0,0 },
1064 Init,
1065 NULL, /* free */
1066 Destroy,
1067 NULL, /* load */
1068 NULL, /* save */
1069 NULL /* edit */
1070 },
1071 Draw,
1072 SizeRequest,
1073 SizeAllocate
1074 };
1075
1076 #endif /* ENABLE_GUI */
1077