/*
* Copyright (c) 2005-2009 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#ifdef ENABLE_GUI
/*
* Graphical plotter widget.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "m.h"
#include "m_plotter.h"
#include "m_gui.h"
#include
#include
M_Plotter *
M_PlotterNew(void *parent, Uint flags)
{
M_Plotter *pl;
pl = Malloc(sizeof(M_Plotter));
AG_ObjectInit(pl, &mPlotterClass);
pl->flags |= flags;
if (flags & M_PLOTTER_HFILL) WIDGET(pl)->flags |= AG_WIDGET_HFILL;
if (flags & M_PLOTTER_VFILL) WIDGET(pl)->flags |= AG_WIDGET_VFILL;
AG_ObjectAttach(parent, pl);
return (pl);
}
static void
keydown(AG_Event *event)
{
M_Plotter *ptr = AG_SELF();
int keysym = AG_INT(1);
switch (keysym) {
case SDLK_0:
case SDLK_1:
ptr->yScale = 1.0;
break;
case SDLK_EQUALS:
ptr->yScale += 0.125;
break;
case SDLK_MINUS:
ptr->yScale -= 0.125;
break;
}
if (ptr->yScale <= 0.125) { ptr->yScale = 0.125; }
}
static __inline__ int
MouseOverPlotItem(M_Plotter *ptr, M_Plot *pl, int x, int y)
{
AG_Surface *lbl;
if (pl->label < 0) { return (0); }
lbl = WSURFACE(ptr,pl->label);
return (x >= pl->xLabel && x <= (pl->xLabel + lbl->w) &&
y >= pl->yLabel && y <= (pl->yLabel + lbl->h));
}
static void
mousemotion(AG_Event *event)
{
M_Plotter *ptr = AG_SELF();
int x = AG_INT(1);
int y = AG_INT(2);
int dy = AG_INT(4);
int state = AG_INT(5);
M_Plot *pl;
if (!AG_WidgetRelativeArea(ptr, x, y))
return;
TAILQ_FOREACH(pl, &ptr->plots, plots) {
if (pl->flags & M_PLOT_SELECTED &&
state & SDL_BUTTON_LEFT) {
pl->yOffs += dy;
}
if (MouseOverPlotItem(ptr, pl, x, y)) {
pl->flags |= M_PLOT_MOUSEOVER;
} else {
pl->flags &= ~M_PLOT_MOUSEOVER;
}
}
}
#if 0
static void
mousebuttonup(AG_Event *event)
{
M_Plotter *ptr = AG_SELF();
int button = AG_INT(1);
int x = AG_INT(2);
int y = AG_INT(3);
M_Plot *pl;
switch (button) {
case SDL_BUTTON_LEFT:
TAILQ_FOREACH(pl, &ptr->plots, plots) {
pl->flags &= ~M_PLOT_DRAGGING;
}
break;
}
}
#endif
void
M_PlotUpdateLabel(M_Plot *pl)
{
M_Plotter *ptr = pl->plotter;
if (pl->label >= 0) {
AG_WidgetUnmapSurface(ptr, pl->label);
}
AG_TextFont(ptr->font);
AG_TextColor32(pl->color);
pl->label = (pl->label_txt == NULL) ? -1 :
AG_WidgetMapSurface(ptr, AG_TextRender(pl->label_txt));
}
static void
UpdateLabel(AG_Event *event)
{
M_PlotUpdateLabel(AG_PTR(1));
}
static void
UpdatePlotTbl(AG_Event *event)
{
AG_Table *tbl = AG_SELF();
M_Plot *pl = AG_PTR(1);
Uint i, j;
AG_TableBegin(tbl);
for (i = 0; i < pl->n; i++) {
if (pl->type == M_PLOT_VECTORS) {
M_Vector *v = pl->data.v[i];
for (j = 0; j < v->m; j++) {
AG_TableAddRow(tbl, "%u[%u]:%f", i, j,
M_VecGetElement(v,j));
}
} else {
AG_TableAddRow(tbl, "%u:%f", pl->data.r[i]);
}
}
AG_TableEnd(tbl);
}
AG_Window *
M_PlotSettings(M_Plot *pl)
{
AG_Window *win;
AG_Notebook *nb;
AG_NotebookTab *ntab;
const char *type_names[] = {
N_("Points"),
N_("Linear interpolation"),
N_("Cubic spline interpolation"),
NULL
};
if ((win = AG_WindowNewNamed(0, "plotter%p", pl)) == NULL) {
return (NULL);
}
AG_WindowSetCaption(win, _("Plot: <%s>"), pl->label_txt);
AG_WindowSetPosition(win, AG_WINDOW_MIDDLE_LEFT, 0);
nb = AG_NotebookNew(win, AG_NOTEBOOK_EXPAND);
ntab = AG_NotebookAddTab(nb, _("Trace"), AG_BOX_VERT);
{
AG_RadioNewUint(ntab, 0, type_names, (void *)&pl->type);
M_NumericalNewReal(ntab, 0, NULL, _("X-scale: "), &pl->xScale);
M_NumericalNewReal(ntab, 0, NULL, _("Y-scale: "), &pl->yScale);
AG_SeparatorNew(ntab, AG_SEPARATOR_HORIZ);
AG_NumericalNewInt(ntab, 0, "px", _("X-offset: "), &pl->xOffs);
AG_NumericalNewInt(ntab, 0, "px", _("Y-offset: "), &pl->yOffs);
}
ntab = AG_NotebookAddTab(nb, _("Color"), AG_BOX_VERT);
{
AG_HSVPal *pal;
pal = AG_HSVPalNew(ntab, AG_HSVPAL_EXPAND);
AG_BindPointer(pal, "pixel-format", (void *)&agSurfaceFmt);
AG_BindUint32(pal, "pixel", &pl->color);
AG_SetEvent(pal, "h-changed", UpdateLabel, "%p", pl);
AG_SetEvent(pal, "sv-changed", UpdateLabel, "%p", pl);
}
ntab = AG_NotebookAddTab(nb, _("Table"), AG_BOX_VERT);
{
AG_Table *tbl;
tbl = AG_TableNewPolled(ntab, AG_TABLE_MULTI|AG_TABLE_EXPAND,
UpdatePlotTbl, "%p", pl);
AG_TableAddCol(tbl, _("#"), "<88888>", NULL);
AG_TableAddCol(tbl, _("Value"), NULL, NULL);
}
AG_WindowShow(win);
return (win);
}
static void
ShowPlotSettings(AG_Event *event)
{
M_Plot *pl = AG_PTR(1);
M_PlotSettings(pl);
}
static void
mousebuttondown(AG_Event *event)
{
M_Plotter *ptr = AG_SELF();
M_Plot *pl, *opl;
int button = AG_INT(1);
int x = AG_INT(2);
int y = AG_INT(3);
SDLMod mod = SDL_GetModState();
switch (button) {
case SDL_BUTTON_LEFT:
AG_WidgetFocus(ptr);
if (mod & (KMOD_CTRL|KMOD_SHIFT)) {
TAILQ_FOREACH(pl, &ptr->plots, plots) {
if (!MouseOverPlotItem(ptr, pl, x, y)) {
continue;
}
AG_INVFLAGS(pl->flags, M_PLOT_SELECTED);
}
} else {
TAILQ_FOREACH(pl, &ptr->plots, plots) {
if (MouseOverPlotItem(ptr, pl, x, y))
break;
}
if (pl != NULL) {
TAILQ_FOREACH(opl, &ptr->plots, plots) {
opl->flags &= ~M_PLOT_SELECTED;
}
pl->flags |= M_PLOT_SELECTED;
}
}
break;
case SDL_BUTTON_RIGHT:
AG_WidgetFocus(ptr);
TAILQ_FOREACH(pl, &ptr->plots, plots) {
if (MouseOverPlotItem(ptr, pl, x, y)) {
AG_PopupMenu *pm;
pm = AG_PopupNew(ptr);
AG_MenuUintFlags(pm->item, _("Display plot"),
NULL,
&pl->flags, M_PLOT_HIDDEN, 1);
AG_MenuAction(pm->item, _("Plot settings"),
NULL,
ShowPlotSettings, "%p", pl);
AG_PopupShow(pm);
break;
}
}
break;
case SDL_BUTTON_WHEELDOWN:
TAILQ_FOREACH(pl, &ptr->plots, plots) {
if (! (pl->flags & M_PLOT_SELECTED)) { continue; }
pl->yScale -= 0.250;
}
break;
case SDL_BUTTON_WHEELUP:
TAILQ_FOREACH(pl, &ptr->plots, plots) {
if (! (pl->flags & M_PLOT_SELECTED)) { continue; }
pl->yScale += 0.250;
}
break;
default:
break;
}
if (ptr->xScale <= 0.0625) { ptr->xScale = 0.0625; }
if (ptr->yScale <= 0.0625) { ptr->yScale = 0.0625; }
}
static void
UpdateXBar(AG_Event *event)
{
M_Plotter *ptr = AG_PTR(1);
int value = AG_GetInt(ptr->hbar, "value");
if (value >= ptr->xMax - WIDTH(ptr)) {
ptr->flags |= M_PLOTTER_SCROLL;
} else {
ptr->flags &= ~M_PLOTTER_SCROLL;
}
}
static void
Init(void *obj)
{
M_Plotter *ptr = obj;
WIDGET(ptr)->flags |= AG_WIDGET_FOCUSABLE;
ptr->type = M_PLOT_2D;
ptr->flags = 0;
ptr->xMax = 0;
ptr->yMin = 0.0;
ptr->yMax = 0.0;
ptr->xOffs = 0;
ptr->yOffs = 0;
ptr->wPre = 128;
ptr->hPre = 64;
ptr->xScale = 1.0;
ptr->yScale = 1.0;
ptr->font = AG_FetchFont(NULL, -1, -1);
ptr->r = AG_RECT(0,0,0,0);
TAILQ_INIT(&ptr->plots);
ptr->vMin = M_New(3,1);
ptr->vMax = M_New(3,1);
M_SetZero(ptr->vMin);
M_SetZero(ptr->vMax);
ptr->curColor = 0;
ptr->colors[0] = AG_MapRGB(agSurfaceFmt, 255, 255, 255);
ptr->colors[1] = AG_MapRGB(agSurfaceFmt, 0, 250, 0);
ptr->colors[2] = AG_MapRGB(agSurfaceFmt, 250, 250, 0);
ptr->colors[3] = AG_MapRGB(agSurfaceFmt, 0, 118, 163);
ptr->colors[4] = AG_MapRGB(agSurfaceFmt, 175, 143, 44);
ptr->colors[5] = AG_MapRGB(agSurfaceFmt, 169, 172, 182);
ptr->colors[6] = AG_MapRGB(agSurfaceFmt, 255, 255, 255);
ptr->colors[7] = AG_MapRGB(agSurfaceFmt, 59, 122, 87);
ptr->colors[8] = AG_MapRGB(agSurfaceFmt, 163, 151, 180);
ptr->colors[9] = AG_MapRGB(agSurfaceFmt, 249, 234, 243);
ptr->colors[10] = AG_MapRGB(agSurfaceFmt, 157, 229, 255);
ptr->colors[11] = AG_MapRGB(agSurfaceFmt, 223, 190, 111);
ptr->colors[12] = AG_MapRGB(agSurfaceFmt, 79, 168, 61);
ptr->colors[13] = AG_MapRGB(agSurfaceFmt, 234, 147, 115);
ptr->colors[14] = AG_MapRGB(agSurfaceFmt, 127, 255, 212);
ptr->colors[15] = AG_MapRGB(agSurfaceFmt, 218, 99, 4);
ptr->hbar = AG_ScrollbarNew(ptr, AG_SCROLLBAR_HORIZ, 0);
ptr->vbar = AG_ScrollbarNew(ptr, AG_SCROLLBAR_VERT, 0);
AG_BindInt(ptr->hbar, "value", &ptr->xOffs);
AG_BindInt(ptr->hbar, "visible", &ptr->r.w);
AG_BindInt(ptr->hbar, "max", &ptr->xMax);
AG_SetEvent(ptr->hbar, "scrollbar-changed", UpdateXBar, "%p", ptr);
AG_BindInt(ptr->vbar, "value", &ptr->yOffs);
/* AG_BindInt(ptr->vbar, "max", &ptr->yMax); */
AG_SetInt(ptr->hbar, "min", 0);
AG_SetInt(ptr->vbar, "min", 0);
AG_SetEvent(ptr, "window-keydown", keydown, NULL);
AG_SetEvent(ptr, "window-mousebuttondown", mousebuttondown, NULL);
/* AG_SetEvent(ptr, "window-mousebuttonup", mousebuttonup, NULL); */
AG_SetEvent(ptr, "window-mousemotion", mousemotion, NULL);
}
static void
Destroy(void *obj)
{
M_Plotter *ptr = obj;
M_Plot *plot;
M_PlotLabel *plbl;
Uint i;
while ((plot = TAILQ_FIRST(&ptr->plots)) != NULL) {
while ((plbl = TAILQ_FIRST(&plot->labels)) != NULL) {
Free(plbl);
}
if (plot->type == M_PLOT_VECTORS) {
for (i = 0; i < plot->n; i++) {
M_VecFree(plot->data.v[i]);
}
free(plot->data.v);
} else {
free(plot->data.r);
}
}
M_Free(ptr->vMin);
M_Free(ptr->vMax);
}
void
M_PlotterSizeHint(M_Plotter *ptr, Uint w, Uint h)
{
ptr->wPre = w;
ptr->hPre = h;
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
M_Plotter *ptr = obj;
r->w = ptr->wPre;
r->h = ptr->hPre;
if (ptr->flags & M_PLOTTER_SCROLL)
ptr->xOffs = 0;
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
M_Plotter *ptr = obj;
AG_SizeAlloc aBar;
if (a->w < 2 || a->h < 2)
return (-1);
ptr->r.w = a->w;
ptr->r.h = a->h;
aBar.x = 0;
aBar.y = a->h - ptr->hbar->wButton;
aBar.w = a->w;
aBar.h = ptr->hbar->wButton;
AG_WidgetSizeAlloc(ptr->hbar, &aBar);
ptr->r.h -= HEIGHT(ptr->hbar);
aBar.x = a->w - ptr->vbar->wButton;
aBar.y = ptr->vbar->wButton;
aBar.w = ptr->vbar->wButton;
aBar.h = a->h - ptr->hbar->wButton;
AG_WidgetSizeAlloc(ptr->vbar, &aBar);
ptr->r.w -= WIDTH(ptr->vbar);
return (0);
}
static __inline__ M_Real
ScaleReal(M_Plotter *ptr, M_Plot *pl, M_Real r)
{
return (r*(ptr->yScale*pl->yScale));
}
static void
Draw(void *obj)
{
M_Plotter *ptr = obj;
M_Plot *pl;
M_PlotLabel *plbl;
Uint i;
int y0 = ptr->r.h/2;
AG_DrawBox(ptr, ptr->r, -1, AG_COLOR(GRAPH_BG_COLOR));
AG_WidgetDraw(ptr->hbar);
AG_WidgetDraw(ptr->vbar);
AG_PushClipRect(ptr, ptr->r);
AG_DrawLineH(ptr, 1, ptr->r.w-2, y0, ptr->colors[0]);
AG_DrawLineV(ptr, ptr->xMax-1, 30, ptr->r.h-30, ptr->colors[0]);
/* First pass */
TAILQ_FOREACH(pl, &ptr->plots, plots) {
int x = pl->xOffs - ptr->xOffs;
int y, py = y0+pl->yOffs+ptr->yOffs;
if (pl->label >= 0) {
AG_Surface *su = WSURFACE(ptr,pl->label);
if (pl->flags & M_PLOT_SELECTED) {
AG_DrawRectOutline(ptr,
AG_RECT(pl->xLabel-2, pl->yLabel-2,
su->w+4, su->h+4),
AG_VideoPixel(pl->color));
} else if (pl->flags & M_PLOT_MOUSEOVER) {
AG_DrawRectOutline(ptr,
AG_RECT(pl->xLabel-2, pl->yLabel-2,
su->w+4, su->h+4),
AG_COLOR(TEXT_COLOR));
}
AG_WidgetBlitSurface(ptr, pl->label, pl->xLabel,
pl->yLabel);
}
if (pl->flags & M_PLOT_HIDDEN) {
continue;
}
switch (pl->type) {
case M_PLOT_POINTS:
for (i = 0; i < pl->n; i++, x++) {
if (x < 0) { continue; }
y = ScaleReal(ptr, pl, pl->data.r[i]);
if (agView->opengl) {
/* TODO */
} else {
AG_LockView();
AG_WidgetPutPixel(ptr, x,
y0 - y + pl->yOffs + ptr->yOffs,
AG_VideoPixel(pl->color));
AG_UnlockView();
}
if (x > ptr->r.w) { break; }
}
break;
case M_PLOT_LINEAR:
for (i = 0; i < pl->n; i++, x++) {
if (x < 0) { continue; }
y = ScaleReal(ptr, pl, pl->data.r[i]);
AG_DrawLine(ptr, x-1, py, x,
y0 - y + pl->yOffs + ptr->yOffs,
AG_VideoPixel(pl->color));
py = y0 - y + pl->yOffs + ptr->yOffs;
if (x > ptr->r.w) { break; }
}
break;
default:
break;
}
}
/* Second pass */
TAILQ_FOREACH(pl, &ptr->plots, plots) {
if (pl->flags & M_PLOT_HIDDEN) {
continue;
}
TAILQ_FOREACH(plbl, &pl->labels, labels) {
AG_Surface *su = WSURFACE(ptr,plbl->text_surface);
int xLbl, yLbl;
Uint8 colLine[4];
Uint8 colBG[4];
AG_GetRGB(AG_COLOR(GRAPH_BG_COLOR), agVideoFmt,
&colBG[0], &colBG[1], &colBG[2]);
AG_GetRGBA(pl->color, agSurfaceFmt,
&colLine[0], &colLine[1], &colLine[2], &colLine[3]);
colLine[3] /= 2;
colBG[3] = 200;
switch (plbl->type) {
case M_LABEL_X:
xLbl = plbl->x - ptr->xOffs - pl->xOffs;
yLbl = ptr->r.h - su->h - 4 - plbl->y;
AG_DrawLineBlended(ptr,
xLbl, 1,
xLbl, ptr->r.h-2,
colLine, AG_ALPHA_SRC);
break;
case M_LABEL_Y:
xLbl = plbl->x - ptr->xOffs - pl->xOffs;
yLbl = ptr->r.h - su->h - 4 - plbl->y;
break;
case M_LABEL_FREE:
xLbl = 4 + plbl->x - ptr->xOffs - pl->xOffs;
yLbl = 4 + plbl->y;
break;
default:
xLbl = 4 + plbl->x;
yLbl = 4 + plbl->y;
break;
}
AG_DrawRectBlended(ptr,
AG_RECT(xLbl+2, yLbl, su->w, su->h),
colBG, AG_ALPHA_SRC);
AG_WidgetBlitSurface(ptr, plbl->text_surface,
xLbl+2, yLbl);
}
}
AG_PopClipRect();
}
void
M_PlotClear(M_Plot *pl)
{
pl->data.r = Realloc(pl->data.r, sizeof(M_Real));
pl->n = 0;
}
void
M_PlotReal(M_Plot *pl, M_Real v)
{
pl->data.r = Realloc(pl->data.r, (pl->n+1)*sizeof(M_Real));
pl->data.r[pl->n] = v;
if (++pl->n > pl->plotter->xMax) { pl->plotter->xMax = pl->n; }
if (v > pl->plotter->yMax) { pl->plotter->yMax = v; }
if (v < pl->plotter->yMin) { pl->plotter->yMin = v; }
}
void
M_PlotRealv(M_Plot *pl, Uint n, const M_Real *vp)
{
Uint i;
pl->data.r = Realloc(pl->data.r, (pl->n+n)*sizeof(M_Real));
memcpy(&pl->data.r[pl->n], vp, n*sizeof(M_Real));
if ((pl->n += n) > pl->plotter->xMax) { pl->plotter->xMax = pl->n; }
for (i = 0; i < n; i++) {
if (vp[i] > pl->plotter->yMax) { pl->plotter->yMax = vp[i]; }
if (vp[i] < pl->plotter->yMin) { pl->plotter->yMin = vp[i]; }
}
}
static void
VectorMinimum(M_Vector *c, const M_Vector *a, const M_Vector *b)
{
Uint i;
for (i = 0; i < c->m; i++) {
if (M_VecGet(a, i) < M_VecGet(b,i)) {
M_VecSet(c, i, M_VecGet(a, i));
} else {
M_VecSet(c, i, M_VecGet(b, i));
}
}
}
static void
VectorMaximum(M_Vector *c, const M_Vector * a, const M_Vector *b)
{
Uint i;
for (i = 0; i < c->m; i++) {
if (M_VecGet(a, i) > M_VecGet(b,i)) {
M_VecSet(c, i, M_VecGet(a, i));
} else {
M_VecSet(c, i, M_VecGet(b, i));
}
}
}
void
M_PlotVector(M_Plot *pl, const M_Vector *v)
{
int i;
pl->data.v = Realloc(pl->data.v, (pl->n)*sizeof(M_Vector *));
pl->data.v[pl->n] = M_VecNew(v->m);
VectorMinimum(pl->plotter->vMin, pl->plotter->vMin, v);
VectorMaximum(pl->plotter->vMax, pl->plotter->vMax, v);
for (i = 0; i < v->m; i++) {
M_Real *e = M_VecGetElement(pl->data.v[pl->n], i);
*e = M_VecGet(v, i);
}
pl->n++;
}
void
M_PlotVectorv(M_Plot *pl, Uint n, const M_Vector **vp)
{
Uint i;
pl->data.v = Realloc(pl->data.v, (pl->n+n)*sizeof(M_Vector));
for (i = 0; i < n; i++) {
pl->data.v[pl->n+i] = M_VecNew(vp[i]->m);
VectorMinimum(pl->plotter->vMin, pl->plotter->vMin, vp[i]);
VectorMaximum(pl->plotter->vMax, pl->plotter->vMax, vp[i]);
M_Copy(pl->data.v[pl->n+i], vp[i]);
}
pl->n += n;
}
static __inline__ void
M_PlotDerivative(M_Plotter *ptr, M_Plot *dp)
{
M_Plot *p = dp->src.plot;
if (p->n >= 2) {
M_PlotReal(dp, p->data.r[p->n-1] - p->data.r[p->n-2]);
} else {
M_PlotReal(dp, p->data.r[p->n-1]);
}
}
static __inline__ M_Real
PlotVariableVFS(M_Plotter *ptr, M_Plot *pl)
{
AG_Variable *V;
M_Real rv;
if ((V = AG_GetVariableVFS(pl->src.varVFS.vfs, pl->src.varVFS.key))
== NULL) {
AG_Verbose("Plot \"%s\": %s\n", pl->src.varVFS.key,
AG_GetError());
return (0.0);
}
switch (AG_VARIABLE_TYPE(V)) {
case AG_VARIABLE_FLOAT: rv = (M_Real)V->data.flt; break;
case AG_VARIABLE_DOUBLE: rv = (M_Real)V->data.dbl; break;
case AG_VARIABLE_UINT: rv = (M_Real)V->data.u; break;
case AG_VARIABLE_INT: rv = (M_Real)V->data.i; break;
case AG_VARIABLE_UINT8: rv = (M_Real)V->data.u8; break;
case AG_VARIABLE_SINT8: rv = (M_Real)V->data.s8; break;
case AG_VARIABLE_UINT16: rv = (M_Real)V->data.u16; break;
case AG_VARIABLE_SINT16: rv = (M_Real)V->data.s16; break;
case AG_VARIABLE_UINT32: rv = (M_Real)V->data.u32; break;
case AG_VARIABLE_SINT32: rv = (M_Real)V->data.s32; break;
default:
AG_Verbose("Plot \"%s\": Invalid type\n", pl->src.varVFS.key);
rv = 0.0;
break;
}
AG_UnlockVariable(V);
return (rv);
}
void
M_PlotterUpdate(M_Plotter *ptr)
{
M_Plot *pl;
M_Real *v;
TAILQ_FOREACH(pl, &ptr->plots, plots) {
switch (pl->src_type) {
case M_PLOT_MANUALLY:
break;
case M_PLOT_FROM_VARIABLE_VFS:
M_PlotReal(pl, PlotVariableVFS(ptr, pl));
break;
case M_PLOT_FROM_REAL:
M_PlotReal(pl, *pl->src.real);
break;
case M_PLOT_FROM_INT:
M_PlotReal(pl, (M_Real)(*pl->src.integer));
break;
case M_PLOT_FROM_COMPONENT:
if (!M_ENTRY_EXISTS(pl->src.com.A, pl->src.com.i,
pl->src.com.j)) {
break;
}
v = M_GetElement(pl->src.com.A, pl->src.com.i,
pl->src.com.j);
M_PlotReal(pl, *v);
break;
case M_PLOT_DERIVATIVE:
M_PlotDerivative(ptr, pl);
break;
default:
break;
}
}
if (ptr->flags & M_PLOTTER_SCROLL)
ptr->xOffs++;
}
M_PlotLabel *
M_PlotLabelNew(M_Plot *pl, enum m_plot_label_type type, Uint x, Uint y,
const char *fmt, ...)
{
M_PlotLabel *plbl;
va_list args;
plbl = Malloc(sizeof(M_PlotLabel));
plbl->type = type;
plbl->x = x;
plbl->y = y;
va_start(args, fmt);
Vsnprintf(plbl->text, sizeof(plbl->text), fmt, args);
va_end(args);
AG_PushTextState();
AG_TextFont(pl->plotter->font);
AG_TextColor32(pl->color);
plbl->text_surface = AG_WidgetMapSurface(pl->plotter,
AG_TextRender(plbl->text));
AG_PopTextState();
TAILQ_INSERT_TAIL(&pl->labels, plbl, labels);
return (plbl);
}
void
M_PlotLabelSetText(M_Plot *pl, M_PlotLabel *plbl, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
Vsnprintf(plbl->text, sizeof(plbl->text), fmt, args);
va_end(args);
AG_WidgetUnmapSurface(pl->plotter, plbl->text_surface);
AG_TextFont(pl->plotter->font);
AG_TextColor32(pl->color);
plbl->text_surface = AG_WidgetMapSurface(pl->plotter,
AG_TextRender(plbl->text));
}
/* Replace plot labels matching the given text. */
M_PlotLabel *
M_PlotLabelReplace(M_Plot *pl, enum m_plot_label_type type, Uint x, Uint y,
const char *fmt, ...)
{
char text[M_PLOTTER_LABEL_MAX];
M_PlotLabel *plbl;
va_list args;
va_start(args, fmt);
Vsnprintf(text, sizeof(text), fmt, args);
va_end(args);
TAILQ_FOREACH(plbl, &pl->labels, labels) {
if (strcmp(text, plbl->text) == 0)
break;
}
if (plbl != NULL) {
plbl->type = type;
switch (plbl->type) {
case M_LABEL_X:
plbl->x = x;
break;
case M_LABEL_Y:
plbl->y = y;
break;
case M_LABEL_FREE:
case M_LABEL_OVERLAY:
plbl->x = x;
plbl->y = y;
break;
}
} else {
Uint nx = x;
Uint ny = y;
AG_Surface *su;
reposition:
/* Do what we can to avoid overlapping labels */
TAILQ_FOREACH(plbl, &pl->labels, labels) {
if (plbl->x != nx || plbl->y != ny) {
continue;
}
su = WSURFACE(pl->plotter,plbl->text_surface);
switch (plbl->type) {
case M_LABEL_X:
ny += su->h;
break;
case M_LABEL_Y:
case M_LABEL_FREE:
case M_LABEL_OVERLAY:
nx += su->w;
break;
}
goto reposition;
}
plbl = M_PlotLabelNew(pl, type, nx, ny, "%s", text);
}
return (plbl);
}
/* Create a new plot. */
M_Plot *
M_PlotNew(M_Plotter *ptr, enum m_plot_type type)
{
M_Plot *pl, *pl2;
pl = Malloc(sizeof(M_Plot));
pl->plotter = ptr;
pl->flags = 0;
pl->type = type;
pl->data.r = NULL;
pl->n = 0;
pl->src_type = M_PLOT_MANUALLY;
pl->label = -1;
pl->label_txt[0] = '\0';
pl->color = ptr->colors[ptr->curColor++];
pl->xOffs = 0;
pl->yOffs = 0;
pl->xScale = 1.0;
pl->yScale = 1.0;
pl->xLabel = 5;
pl->yLabel = 5;
TAILQ_FOREACH(pl2, &ptr->plots, plots) {
if (pl2->label == -1) { continue; }
pl->xLabel += WSURFACE(ptr,pl2->label)->w + 5;
}
TAILQ_INSERT_TAIL(&ptr->plots, pl, plots);
TAILQ_INIT(&pl->labels);
if (ptr->curColor >= M_PLOTTER_NDEFCOLORS) {
ptr->curColor = 0;
}
return (pl);
}
/* Plot the derivative of existing Plot data. */
M_Plot *
M_PlotFromDerivative(M_Plotter *ptr, enum m_plot_type type, M_Plot *plot)
{
M_Plot *pl;
pl = M_PlotNew(ptr, type);
pl->src_type = M_PLOT_DERIVATIVE;
pl->src.plot = plot;
M_PlotSetLabel(pl, "%s'", plot->label_txt);
return (pl);
}
/*
* Plot a Variable from a VFS "object:variable-name" path. This allows the
* object or variable to safely disappear as long as the VFS root remains.
*/
M_Plot *
M_PlotFromVariableVFS(M_Plotter *ptr, enum m_plot_type type, const char *label,
void *vfsRoot, const char *varName)
{
M_Plot *pl;
pl = M_PlotNew(ptr, type);
pl->src_type = M_PLOT_FROM_VARIABLE_VFS;
pl->src.varVFS.vfs = vfsRoot;
pl->src.varVFS.key = Strdup(varName);
M_PlotSetLabel(pl, "%s", label);
return (pl);
}
M_Plot *
M_PlotFromReal(M_Plotter *ptr, enum m_plot_type type, const char *label,
M_Real *p)
{
M_Plot *pl;
pl = M_PlotNew(ptr, type);
pl->src_type = M_PLOT_FROM_REAL;
pl->src.real = p;
M_PlotSetLabel(pl, "%s", label);
return (pl);
}
M_Plot *
M_PlotFromInt(M_Plotter *ptr, enum m_plot_type type, const char *label,
int *ip)
{
M_Plot *pl;
pl = M_PlotNew(ptr, type);
pl->src_type = M_PLOT_FROM_INT;
pl->src.integer = ip;
M_PlotSetLabel(pl, "%s", label);
return (pl);
}
void
M_PlotSetColor(M_Plot *pl, Uint8 r, Uint8 g, Uint8 b)
{
pl->color = AG_MapRGB(agSurfaceFmt, r,g,b);
}
void
M_PlotSetLabel(M_Plot *pl, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
Vsnprintf(pl->label_txt, sizeof(pl->label_txt), fmt, args);
va_end(args);
M_PlotUpdateLabel(pl);
}
void
M_PlotSetScale(M_Plot *pl, M_Real xScale, M_Real yScale)
{
if (xScale > 0.0) { pl->xScale = xScale; }
if (yScale > 0.0) { pl->yScale = yScale; }
}
void
M_PlotSetXoffs(M_Plot *pl, int xOffs)
{
pl->xOffs = xOffs;
}
void
M_PlotSetYoffs(M_Plot *pl, int yOffs)
{
pl->yOffs = yOffs;
}
void
M_PlotterSetDefaultFont(M_Plotter *ptr, const char *face, int size)
{
ptr->font = AG_FetchFont(face, size, 0);
}
void
M_PlotterSetDefaultColor(M_Plotter *ptr, int i, Uint8 r, Uint8 g, Uint8 b)
{
ptr->colors[i] = AG_MapRGB(agSurfaceFmt, r,g,b);
}
void
M_PlotterSetDefaultScale(M_Plotter *ptr, M_Real xScale, M_Real yScale)
{
ptr->xScale = xScale;
ptr->yScale = yScale;
}
AG_WidgetClass mPlotterClass = {
{
"AG_Widget:M_Plotter",
sizeof(M_Plotter),
{ 0,0 },
Init,
NULL, /* free */
Destroy,
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};
#endif /* ENABLE_GUI */