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