1 
2 /*
3   FL_graph.cpp: code for drawing graphs using FLTK library
4 
5   Copyright (C) 2003 John ffitch
6 
7   This file is part of Csound.
8 
9   The Csound Library is free software; you can redistribute it
10   and/or modify it under the terms of the GNU Lesser General Public
11   License as published by the Free Software Foundation; either
12   version 2.1 of the License, or (at your option) any later version.
13 
14   Csound is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU Lesser General Public License for more details.
18 
19   You should have received a copy of the GNU Lesser General Public
20   License along with Csound; if not, write to the Free Software
21   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   02110-1301 USA
23 */
24 
25 #include <FL/Fl.H>
26 #include <FL/Fl_Window.H>
27 #include <FL/fl_draw.H>
28 #include <FL/Fl_Choice.H>
29 #include <FL/Fl_Button.H>
30 
31 #ifdef _MSC_VER
32 #include <math.h>
33 #endif
34 #include "csdl.h"
35 #include "cwindow.h"
36 #include "winFLTK.h"
37 #include <inttypes.h>
38 
39 #define NUMOFWINDOWS (30)
40 #define XINIT    10      /* set default window location */
41 #define YINIT    50
42 #define WIDTH    450     /* start off small - user resizes */
43 #define HEIGHT   150
44 #define BDR      5
45 #define BORDERW  10      /* inset from L & R edge */
46 #define TXHGHT   14      /* baseline offset for text */
47 #define MAXLSEGS 4096    /* X can only deal with so many linesegs .. */
48 
49 class graph_box : public Fl_Window {
50   void draw();
51 public:
52   int curr;
53   int last;
54   CSOUND *csound;
graph_box(void * cs,int x,int y,int w,int h,const char * l=0)55   graph_box(void *cs, int x, int y, int w, int h, const char *l = 0)
56     : Fl_Window(x, y, w, h, l)
57   {
58       curr = last = -1;
59       csound = (CSOUND*)cs;
60   }
61   //void add_graph(WINDAT *wdptr);
62 };
63 
64 typedef struct {
65 
66   Fl_Choice   *choice;
67   Fl_Button   *end;
68   Fl_Menu_Item *menu;
69   graph_box   *graph;
70   int graph_created;
71   Fl_Window   *form;
72 } FLGRAPH_GLOBALS;
73 
74 #define ST(x)   ((flgraphGlobals)->x)
75 
76 /*void printouts(CSOUND *csound){
77     csound->Message(csound, "menu object: %p\n", ST(menu));
78     csound->Message(csound, "form object: %p\n", ST(form));
79     csound->Message(csound, "graph object: %p\n", ST(graph));
80     csound->Message(csound, "choice object: %p\n", ST(choice));
81     csound->Message(csound, "globals %p\n",  csound->flgraphGlobals);
82 }*/
83 
flgraph_init(CSOUND * csound)84 void flgraph_init(CSOUND *csound)
85 {
86 
87     FLGRAPH_GLOBALS *flgraphGlobals =
88          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
89                                                          "FLGRAPH_GLOBALS");
90     if (flgraphGlobals==NULL) {
91      csound->CreateGlobalVariable(csound, "FLGRAPH_GLOBALS",
92                                   sizeof(FLGRAPH_GLOBALS));
93      flgraphGlobals =
94          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
95                                                          "FLGRAPH_GLOBALS");
96       //csound->flgraphGlobals =
97       //(FLGRAPH_GLOBALS*) csound->Malloc(csound,sizeof(FLGRAPH_GLOBALS));
98     }
99       ST(form) = (Fl_Window *) 0;
100       ST(choice) = (Fl_Choice *) 0;
101       ST(end) = (Fl_Button *) 0;
102       ST(graph) = (graph_box *) 0;
103       ST(menu) = (Fl_Menu_Item *) 0;
104       ST(graph_created) = 0;
105 
106 
107       /*ST(menu) =  (Fl_Menu_Item*) csound->Calloc(csound,
108         sizeof(Fl_Menu_Item)*(1+NUMOFWINDOWS));*/
109       /* VL: moved menu object to be built at each new compilation */
110 
111 }
112 
113 /* static  graph_box   *graph = (graph_box *) 0; */
114 
draw()115 void graph_box::draw()
116 {
117     FLGRAPH_GLOBALS *flgraphGlobals =
118          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
119                                                          "FLGRAPH_GLOBALS");
120     Fl_Window::draw();
121     fl_color(0, 0, 0);
122     fl_line_style(FL_SOLID);
123     fl_rect(0, 0, w(), h());
124     if (curr >= 0) {
125       WINDAT      *win   = (WINDAT*) ST(menu)[curr].user_data_;
126       if (!win)
127         return;
128       MYFLT       *fdata = win->fdata;
129       int32       npts   = win->npts;
130       char        *msg   = win->caption;
131       short       win_x, win_y,        win_h;     /* window rect */
132       short       gra_x, gra_y, gra_w, gra_h;     /* graph rect is inset */
133       short       y_axis;
134       int         lsegs, pts_pls;
135       int         pol;
136       char        string[400];
137 
138       pol  = win->polarity;
139 
140       win_x = 0;  win_y = 0;              /* window pixels addressed relative */
141       win_h = h();
142 
143       /* set new width and height so we leave a 20% border around the plot */
144       gra_w = w() - 2*BORDERW;
145       gra_h = h();
146       gra_x = win_x + BORDERW;
147       gra_y = win_y;
148       /* figure height of Y axis - top, middle or bottom */
149       if (pol == (short)BIPOL)
150         y_axis = gra_y + (gra_h/2);
151       else if (pol == (short)NEGPOL)
152         y_axis = gra_y;
153         else                /* POSPOL */
154         y_axis = gra_y + gra_h;
155 
156       if (npts < MAXLSEGS) {
157         lsegs = npts;                   /* one lineseg per datum */
158         pts_pls = 1;
159       }
160       else {
161         pts_pls = npts/MAXLSEGS;
162         if (npts%MAXLSEGS) pts_pls++;
163         lsegs = npts/pts_pls;
164       }
165       fl_begin_line();
166       {       /* take scale factors out of for-loop for faster run-time */
167         MYFLT x_scale = gra_w / (MYFLT) (lsegs - 1);
168         MYFLT y_scale = gra_h / win->oabsmax; /* unipolar default */
169         MYFLT f, ma, mi, *fdptr = fdata;
170         int   c, i = 0, j = lsegs;
171 
172         if (pol == (short) BIPOL)
173           y_scale /= 2.0;             /* max data scales to h/2 */
174         /* put x-y pairs into a point list for XDraw */
175         while (j--) {
176           int x = gra_x + (short) ((MYFLT) i++ * x_scale);
177           int y;
178           if (pts_pls == 1)
179             f = *fdptr++;
180           else {
181             ma = mi = *fdptr++;
182             for (c = 1; c < pts_pls; ++c)
183               if ((f = *fdptr++) > ma)    ma = f;
184               else if ( f<mi )            mi = f;
185             if (ma < 0)             f = mi;
186             else if (mi > 0)        f = ma;
187             else if (ma > -mi)      f = ma;
188             else f = mi;
189           }
190           y = y_axis - (short) (f * y_scale);
191           fl_vertex(x, y);
192         }
193       }
194       fl_end_line();
195 
196       /* now draw axes: y-axis is always on the left edge,
197          x-axis height is determined by the case we're in */
198       fl_line(gra_x, y_axis, (gra_x + gra_w), y_axis);
199       fl_line(gra_x, y_axis, (gra_x + gra_w), y_axis);
200 
201 
202       fl_line(gra_x, gra_y, gra_x, (gra_y + gra_h));
203       if (win->danflag) {       /* flag to add dotted divider */
204         fl_line_style(FL_DOT);
205         fl_line(win_x+w()/2, win_y, win_x+w()/2, win_y+win_h);
206       }
207       if (pol != NEGPOL)
208       sprintf(string, "%s  %" PRIi32 " points, max %5.3f", msg, npts, win->oabsmax);
209       else
210       sprintf(string, "%s  %" PRIi32 " points, max %5.3f", msg, npts, win->max);
211 
212       ST(form)->label(string);
213     }
214     fl_line_style(FL_SOLID);
215 }
216 
217 
218 
add_graph(CSOUND * csound,WINDAT * wdptr)219 void add_graph(CSOUND *csound, WINDAT *wdptr)
220 {
221     FLGRAPH_GLOBALS *flgraphGlobals =
222          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
223                                                          "FLGRAPH_GLOBALS");
224     WINDAT *n = (WINDAT*) malloc(sizeof(WINDAT));
225     int    m;
226     WINDAT *old;
227     int    replacing = 0;
228 
229     memcpy(n, wdptr, sizeof(WINDAT));
230     n->fdata = (MYFLT*) malloc(n->npts * sizeof(MYFLT));
231     memcpy(n->fdata, wdptr->fdata, n->npts * sizeof(MYFLT));
232 
233 
234     for (m = 0; m < NUMOFWINDOWS; m++) {  // If text the same use slot
235       if (ST(menu) != NULL) {
236         if (ST(menu)[m].text != NULL && strlen(wdptr->caption) == 0){
237           if(strcmp(wdptr->caption, ST(menu)[m].text) == 0) {
238             replacing = 1;
239             goto replace;
240           }
241         }
242       }
243     }
244     // Use a new slot, cycling round
245     ST(graph)->last++;
246     m = ST(graph)->last;
247     if (m >= NUMOFWINDOWS)
248       m = ST(graph)->last = 0;
249  replace:
250 
251     old = (WINDAT*)ST(menu)[m].user_data_;
252     if (old) {
253       free((void*) old->fdata);
254       free((void*) old);
255     }
256     ST(menu)[m].user_data_ = n;
257     if (replacing == 0) {
258       if (ST(menu)[m].text != NULL)
259         free((void*) ST(menu)[m].text);
260       ST(menu)[m].text = (const char*) malloc(strlen(n->caption) + 1);
261       strcpy((char*) ST(menu)[m].text, n->caption);
262     }
263 
264 
265     // ST(graph)->curr = m;
266     //  ST(choice)->value(m);
267 
268     ST(graph)->curr = ST(choice)->value();  /* VL: 29.04.09 fix */
269     ST(graph)->redraw();
270 }
271 
do_redraw(Fl_Widget *,void * cs)272 void do_redraw(Fl_Widget *, void *cs)
273 {
274     CSOUND *csound = (CSOUND*)cs;
275     FLGRAPH_GLOBALS *flgraphGlobals =
276          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
277                                                          "FLGRAPH_GLOBALS");
278     ST(graph)->curr = ST(choice)->value();
279     ST(graph)->redraw();
280 }
281 
makeWindow(CSOUND * csound,char * name)282 void makeWindow(CSOUND *csound, char *name)
283 {
284     FLGRAPH_GLOBALS *flgraphGlobals =
285          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
286                                                          "FLGRAPH_GLOBALS");
287     if (ST(form))
288       return;
289 
290     ST(form) = new Fl_Window(WIDTH, HEIGHT, name);
291     ST(menu) = new Fl_Menu_Item[1+NUMOFWINDOWS];
292     memset(ST(menu), 0, sizeof(Fl_Menu_Item)*(1+NUMOFWINDOWS));
293     ST(choice) = new Fl_Choice(140, 0, 140, 20, "Choose Graph");
294     ST(choice)->menu(ST(menu));
295     ST(choice)->value(0);
296     ST(choice)->callback((Fl_Callback*) do_redraw,  (void*)csound);
297     ST(graph) = new graph_box(csound,
298                           BDR, 30 + BDR, WIDTH - 2 * BDR, HEIGHT - 30 - 2 * BDR);
299     ST(graph)->end();
300     ST(end) = new Fl_Button(WIDTH - 40, 0, 35, 20, "Quit");
301     ST(end)->hide();
302     ST(form)->resizable(ST(graph));
303     ST(form)->end();
304     ST(graph_created) = 1;
305 
306 }
307 
graphs_reset(CSOUND * csound)308 void graphs_reset(CSOUND * csound){
309   IGN(csound);
310   //if (csound->flgraphGlobals != NULL)
311   //  csound->Free(csound, csound->flgraphGlobals);
312 }
313 
314 extern "C" {
315 
DrawGraph_FLTK(CSOUND * csound,WINDAT * wdptr)316   void DrawGraph_FLTK(CSOUND *csound, WINDAT *wdptr)
317   {
318 
319       add_graph(csound, wdptr);
320       csound->CheckEvents(csound);
321   }
322 
MakeWindow_FLTK(CSOUND * csound,char * name)323   uintptr_t MakeWindow_FLTK(CSOUND *csound, char *name)
324   {
325       FLGRAPH_GLOBALS *flgraphGlobals =
326          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
327                                                          "FLGRAPH_GLOBALS");
328       if (ST(form) == NULL) {
329         makeWindow(csound, name);
330         ST(form)->show();
331       }
332 
333       return (uintptr_t) ST(form);
334   }
335 
CsoundYield_FLTK(CSOUND * csound)336   int CsoundYield_FLTK(CSOUND *csound){
337 
338 #ifndef NO_FLTK_THREADS
339 
340       /* nothing to do, unless no widget thread is running */
341       if (csound->QueryGlobalVariable(csound, "_widgets_globals") != NULL)
342         return 1;
343 #endif
344       Fl_wait_locked(csound, 0.0);
345       return 1;
346   }
347 
kill_graph(CSOUND * csound,uintptr_t m)348   void kill_graph(CSOUND *csound, uintptr_t m)
349   {
350       FLGRAPH_GLOBALS *flgraphGlobals =
351          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
352                                                          "FLGRAPH_GLOBALS");
353       for (int i = 0; i < NUMOFWINDOWS; i++) {
354         WINDAT *n = (WINDAT*) ST(menu)[i].user_data_;
355         if (n != NULL && ((uintptr_t) n == m ||n->windid == m)) {
356           free(n->fdata);
357           free(n);
358           free((void*) ST(menu)[i].text);
359           ST(menu)[i].user_data_ = (void*) 0;
360           ST(menu)[i].text = (char*) 0;
361           return;
362         }
363       }
364   }
365 
ExitGraph_FLTK(CSOUND * csound)366   int ExitGraph_FLTK(CSOUND *csound)
367   {
368       FLGRAPH_GLOBALS *flgraphGlobals =
369          (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound,
370                                                         "FLGRAPH_GLOBALS");
371       if (flgraphGlobals == 0) {
372           return OK;
373       }
374       if (ST(form) && ST(graph_created) == 1) {
375 
376         if (ST(form)->shown() && !(getFLTKFlags(csound) & 256)) {
377         const char *env = csound->GetEnv(csound, "CSNOSTOP");
378         if (env == NULL || strcmp(env, "yes") != 0) {
379           ST(end)->show();
380            // print click-Exit message in most recently active window
381           while (ST(end)->value() == 0 && ST(form)->shown()) {
382             Fl_wait_locked(csound, 0.03);
383           }
384           }
385          }
386 
387         delete ST(form);
388         ST(form) = (Fl_Window *) 0;
389         Fl_wait_locked(csound, 0.0);
390 
391       ST(choice) = (Fl_Choice *) 0;
392       ST(graph) = (graph_box *) 0;
393       ST(end) = (Fl_Button *) 0;
394       ST(graph_created) = 0;
395 
396        for (int i = 0; i < NUMOFWINDOWS; i++) {
397         WINDAT *n = (WINDAT*) ST(menu)[i].user_data_;
398         if (n)
399           kill_graph(csound, (uintptr_t) ((void*) n));
400        }
401        if (ST(menu)){
402          delete[] ST(menu);
403          ST(menu) = (Fl_Menu_Item *) 0;
404        }
405 
406 
407 
408       }
409 
410       return 0;
411   }
412 
413 #define GUTTERH 20           /* space for text at top & bottom */
414 #define BORDERW 10           /* inset from L & R edge */
415 
MakeXYin_FLTK(CSOUND * csound,XYINDAT * w,MYFLT x,MYFLT y)416   void MakeXYin_FLTK(CSOUND *csound, XYINDAT *w, MYFLT x, MYFLT y)
417   {
418       if (w->windid == (uintptr_t) 0) {
419         Fl_Window *xyin = new Fl_Window(WIDTH, WIDTH, "XY input");
420         short   win_x, win_y;
421         short   gra_x, gra_y, gra_w, gra_h;
422 
423         win_x = 0;  win_y = 0;           /* window pixels addressed relative */
424 
425         Fl_lock(csound);
426         xyin->show();
427         Fl_wait(csound, 0.0);
428         Fl_unlock(csound);
429 
430         /* set new width and height so we leave a 20% border around the plot */
431         gra_w = xyin->w() - 2*BORDERW;      gra_h = xyin->h() - 2*GUTTERH;
432         gra_x = win_x + BORDERW;            gra_y = win_y + GUTTERH;
433         w->m_x = gra_x + (int) (x * (MYFLT) gra_w);
434         w->m_y = gra_y + (int) (y * (MYFLT) gra_h);
435         w->down = 0;
436 
437         Fl_lock(csound);
438         Fl_wait(csound, 0.0);
439         xyin->make_current();
440         fl_color(0, 0, 0);
441         fl_line_style(FL_DOT);
442         fl_line(gra_x, w->m_y, (gra_x + gra_w), w->m_y);
443         fl_line(w->m_x, gra_y, w->m_x, (gra_y + gra_h));
444         Fl_unlock(csound);
445         w->windid = (uintptr_t) xyin;
446       }
447   }
448 
ReadXYin_FLTK(CSOUND * csound,XYINDAT * wdptr)449   void ReadXYin_FLTK(CSOUND *csound, XYINDAT *wdptr)
450   {
451       short     win_x, win_y;
452       short     gra_x, gra_y, gra_w, gra_h;
453       short     m_x, m_y;
454       Fl_Window *xwin = (Fl_Window*) wdptr->windid;
455 
456       Fl_lock(csound);
457       Fl_wait(csound, 0.0);
458       m_x = Fl::event_x();
459       m_y = Fl::event_y();
460       wdptr->down = (short) (Fl::event_button1() ? 1 : 0);
461       Fl_unlock(csound);
462 
463       if (!wdptr->down)
464         return;
465 
466       win_x = 0;  win_y = 0;      /* window pixels addressed relative */
467 
468       /* set new width and height so we leave a 20% border around the plot */
469       gra_w = xwin->w() - 2*BORDERW;      gra_h = xwin->h() - 2*GUTTERH;
470       gra_x = win_x + BORDERW;            gra_y = win_y + GUTTERH;
471 
472       /* clip mouse position */
473       if (m_x < gra_x)            m_x = gra_x;
474       else if (m_x > gra_x+gra_w) m_x = gra_x+gra_w;
475       if (m_y < gra_y)            m_y = gra_y;
476       else if (m_y > gra_y+gra_h) m_y = gra_y+gra_h;
477 
478       if (m_x != wdptr->m_x || m_y != wdptr->m_y) { /* only redo if changed */
479         Fl_lock(csound);
480         xwin->make_current();
481         /* undraw old crosshairs */
482         fl_color(FL_BACKGROUND_COLOR);
483         fl_line_style(FL_SOLID);
484         fl_line(gra_x, wdptr->m_y, (gra_x + gra_w), wdptr->m_y);
485         fl_line(wdptr->m_x, gra_y, wdptr->m_x, (gra_y + gra_h));
486         // Draw new
487         fl_color(0, 0, 0);
488         fl_line_style(FL_SOLID);
489         fl_line(gra_x, m_y, (gra_x + gra_w), m_y);
490         fl_line(m_x, gra_y, m_x, (gra_y + gra_h));
491         Fl_unlock(csound);
492         wdptr->m_x = m_x;       wdptr->m_y = m_y;
493         wdptr->x = ((MYFLT) m_x - gra_x) / (MYFLT) gra_w;
494         wdptr->y = ((MYFLT) m_y - gra_y) / (MYFLT) gra_h;
495       }
496   }
497 
KillXYin_FLTK(CSOUND * csound,XYINDAT * wdptr)498   void KillXYin_FLTK(CSOUND *csound, XYINDAT *wdptr)
499   {
500     IGN(csound);
501       delete ((Fl_Window*) wdptr->windid);
502       wdptr->windid = (uintptr_t) 0;
503   }
504 }
505