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