summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/g_template.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_template.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/g_template.c3358
1 files changed, 3358 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/g_template.c b/apps/plugins/pdbox/PDa/src/g_template.c
new file mode 100644
index 0000000000..c65613a0d6
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/g_template.c
@@ -0,0 +1,3358 @@
1/* Copyright (c) 1997-1999 Miller Puckette.
2* For information on usage and redistribution, and for a DISCLAIMER OF ALL
3* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
4
5#include <stdlib.h>
6#include <string.h>
7#include <stdio.h>
8
9#include "m_pd.h"
10#include "s_stuff.h" /* for sys_hostfontsize */
11#include "g_canvas.h"
12
13/*
14This file contains text objects you would put in a canvas to define a
15template. Templates describe objects of type "array" (g_array.c) and
16"scalar" (g_scalar.c).
17*/
18
19/* T.Grill - changed the _template.t_pd member to t_pdobj to avoid name clashes
20with the t_pd type */
21
22 /* the structure of a "struct" object (also the obsolete "gtemplate"
23 you get when using the name "template" in a box.) */
24
25struct _gtemplate
26{
27 t_object x_obj;
28 t_template *x_template;
29 t_canvas *x_owner;
30 t_symbol *x_sym;
31 struct _gtemplate *x_next;
32 int x_argc;
33 t_atom *x_argv;
34};
35
36/* ---------------- forward definitions ---------------- */
37
38static void template_conformarray(t_template *tfrom, t_template *tto,
39 int *conformaction, t_array *a);
40static void template_conformglist(t_template *tfrom, t_template *tto,
41 t_glist *glist, int *conformaction);
42
43/* ---------------------- storage ------------------------- */
44
45static t_class *gtemplate_class;
46static t_class *template_class;
47
48/* there's a pre-defined "float" template. LATER should we bind this
49to a symbol such as "pd-float"??? */
50
51static t_dataslot template_float_vec =
52{
53 DT_FLOAT,
54 &s_y,
55 &s_
56};
57
58static t_template template_float =
59{
60 0, /* class -- fill in in setup routine */
61 0, /* list of "struct"/t_gtemplate objects */
62 &s_float, /* name */
63 1, /* number of items */
64 &template_float_vec
65};
66
67 /* return true if two dataslot definitions match */
68static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2,
69 int nametoo)
70{
71 return ((!nametoo || ds1->ds_name == ds2->ds_name) &&
72 ds1->ds_type == ds2->ds_type &&
73 (ds1->ds_type != DT_ARRAY ||
74 ds1->ds_arraytemplate == ds2->ds_arraytemplate));
75}
76
77/* -- templates, the active ingredient in gtemplates defined below. ------- */
78
79t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv)
80{
81 t_template *x = (t_template *)pd_new(template_class);
82 x->t_n = 0;
83 x->t_vec = (t_dataslot *)t_getbytes(0);
84 while (argc > 0)
85 {
86 int newtype, oldn, newn;
87 t_symbol *newname, *newarraytemplate = &s_, *newtypesym;
88 if (argc < 2 || argv[0].a_type != A_SYMBOL ||
89 argv[1].a_type != A_SYMBOL)
90 goto bad;
91 newtypesym = argv[0].a_w.w_symbol;
92 newname = argv[1].a_w.w_symbol;
93 if (newtypesym == &s_float)
94 newtype = DT_FLOAT;
95 else if (newtypesym == &s_symbol)
96 newtype = DT_SYMBOL;
97 else if (newtypesym == &s_list)
98 newtype = DT_LIST;
99 else if (newtypesym == gensym("array"))
100 {
101 if (argc < 3 || argv[2].a_type != A_SYMBOL)
102 {
103 pd_error(x, "array lacks element template or name");
104 goto bad;
105 }
106 newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol);
107 newtype = DT_ARRAY;
108 argc--;
109 argv++;
110 }
111 else
112 {
113 pd_error(x, "%s: no such type", newtypesym->s_name);
114 return (0);
115 }
116 newn = (oldn = x->t_n) + 1;
117 x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec,
118 oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec));
119 x->t_n = newn;
120 x->t_vec[oldn].ds_type = newtype;
121 x->t_vec[oldn].ds_name = newname;
122 x->t_vec[oldn].ds_arraytemplate = newarraytemplate;
123 bad:
124 argc -= 2; argv += 2;
125 }
126 if (templatesym->s_name)
127 {
128 x->t_sym = templatesym;
129 pd_bind(&x->t_pdobj, x->t_sym);
130 }
131 else x->t_sym = templatesym;
132 return (x);
133}
134
135int template_size(t_template *x)
136{
137 return (x->t_n * sizeof(t_word));
138}
139
140int template_find_field(t_template *x, t_symbol *name, int *p_onset,
141 int *p_type, t_symbol **p_arraytype)
142{
143 t_template *t;
144 int i, n;
145 if (!x)
146 {
147 bug("template_find_field");
148 return (0);
149 }
150 n = x->t_n;
151 for (i = 0; i < n; i++)
152 if (x->t_vec[i].ds_name == name)
153 {
154 *p_onset = i * sizeof(t_word);
155 *p_type = x->t_vec[i].ds_type;
156 *p_arraytype = x->t_vec[i].ds_arraytemplate;
157 return (1);
158 }
159 return (0);
160}
161
162t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp,
163 int loud)
164{
165 int onset, type;
166 t_symbol *arraytype;
167 t_sample val = 0;
168 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
169 {
170 if (type == DT_FLOAT)
171 val = *(t_sample *)(((char *)wp) + onset);
172 else if (loud) error("%s.%s: not a number",
173 x->t_sym->s_name, fieldname->s_name);
174 }
175 else if (loud) error("%s.%s: no such field",
176 x->t_sym->s_name, fieldname->s_name);
177 return (fixtof(val));
178}
179
180void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp,
181 t_float f, int loud)
182{
183 int onset, type;
184 t_symbol *arraytype;
185 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
186 {
187 if (type == DT_FLOAT)
188 *(t_sample *)(((char *)wp) + onset) = ftofix(f);
189 else if (loud) error("%s.%s: not a number",
190 x->t_sym->s_name, fieldname->s_name);
191 }
192 else if (loud) error("%s.%s: no such field",
193 x->t_sym->s_name, fieldname->s_name);
194}
195
196t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp,
197 int loud)
198{
199 int onset, type;
200 t_symbol *arraytype;
201 t_symbol *val = &s_;
202 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
203 {
204 if (type == DT_SYMBOL)
205 val = *(t_symbol **)(((char *)wp) + onset);
206 else if (loud) error("%s.%s: not a symbol",
207 x->t_sym->s_name, fieldname->s_name);
208 }
209 else if (loud) error("%s.%s: no such field",
210 x->t_sym->s_name, fieldname->s_name);
211 return (val);
212}
213
214void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp,
215 t_symbol *s, int loud)
216{
217 int onset, type;
218 t_symbol *arraytype;
219 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
220 {
221 if (type == DT_SYMBOL)
222 *(t_symbol **)(((char *)wp) + onset) = s;
223 else if (loud) error("%s.%s: not a symbol",
224 x->t_sym->s_name, fieldname->s_name);
225 }
226 else if (loud) error("%s.%s: no such field",
227 x->t_sym->s_name, fieldname->s_name);
228}
229
230 /* stringent check to see if a "saved" template, x2, matches the current
231 one (x1). It's OK if x1 has additional scalar elements but not (yet)
232 arrays or lists. This is used for reading in "data files". */
233int template_match(t_template *x1, t_template *x2)
234{
235 int i;
236 if (x1->t_n < x2->t_n)
237 return (0);
238 for (i = x2->t_n; i < x1->t_n; i++)
239 {
240 if (x1->t_vec[i].ds_type == DT_ARRAY ||
241 x1->t_vec[i].ds_type == DT_LIST)
242 return (0);
243 }
244 if (x2->t_n > x1->t_n)
245 post("add elements...");
246 for (i = 0; i < x2->t_n; i++)
247 if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1))
248 return (0);
249 return (1);
250}
251
252/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */
253
254/* the following routines handle updating scalars to agree with changes
255in their template. The old template is assumed to be the "installed" one
256so we can delete old items; but making new ones we have to avoid scalar_new
257which would make an old one whereas we will want a new one (but whose array
258elements might still be old ones.
259 LATER deal with graphics updates too... */
260
261 /* conform the word vector of a scalar to the new template */
262static void template_conformwords(t_template *tfrom, t_template *tto,
263 int *conformaction, t_word *wfrom, t_word *wto)
264{
265 int nfrom = tfrom->t_n, nto = tto->t_n, i;
266 for (i = 0; i < nto; i++)
267 {
268 if (conformaction[i] >= 0)
269 {
270 /* we swap the two, in case it's an array or list, so that
271 when "wfrom" is deleted the old one gets cleaned up. */
272 t_word wwas = wto[i];
273 wto[i] = wfrom[conformaction[i]];
274 wfrom[conformaction[i]] = wwas;
275 }
276 }
277}
278
279 /* conform a scalar, recursively conforming sublists and arrays */
280static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto,
281 int *conformaction, t_glist *glist, t_scalar *scfrom)
282{
283 t_scalar *x;
284 t_gpointer gp;
285 int nto = tto->t_n, nfrom = tfrom->t_n, i;
286 t_template *scalartemplate;
287 /* post("conform scalar"); */
288 /* possibly replace the scalar */
289 if (scfrom->sc_template == tfrom->t_sym)
290 {
291 /* see scalar_new() for comment about the gpointer. */
292 gpointer_init(&gp);
293 x = (t_scalar *)getbytes(sizeof(t_scalar) +
294 (tto->t_n - 1) * sizeof(*x->sc_vec));
295 x->sc_gobj.g_pd = scalar_class;
296 x->sc_template = tfrom->t_sym;
297 gpointer_setglist(&gp, glist, x);
298 /* Here we initialize to the new template, but array and list
299 elements will still belong to old template. */
300 word_init(x->sc_vec, tto, &gp);
301
302 template_conformwords(tfrom, tto, conformaction,
303 scfrom->sc_vec, x->sc_vec);
304
305 /* replace the old one with the new one in the list */
306 if (glist->gl_list == &scfrom->sc_gobj)
307 {
308 glist->gl_list = &x->sc_gobj;
309 x->sc_gobj.g_next = scfrom->sc_gobj.g_next;
310 }
311 else
312 {
313 t_gobj *y, *y2;
314 for (y = glist->gl_list; y2 = y->g_next; y = y2)
315 if (y2 == &scfrom->sc_gobj)
316 {
317 x->sc_gobj.g_next = y2->g_next;
318 y->g_next = &x->sc_gobj;
319 goto nobug;
320 }
321 bug("template_conformscalar");
322 nobug: ;
323 }
324 /* burn the old one */
325 pd_free(&scfrom->sc_gobj.g_pd);
326 }
327 else x = scfrom;
328 scalartemplate = template_findbyname(x->sc_template);
329 /* convert all array elements and sublists */
330 for (i = 0; i < scalartemplate->t_n; i++)
331 {
332 t_dataslot *ds = scalartemplate->t_vec + i;
333 if (ds->ds_type == DT_LIST)
334 {
335 t_glist *gl2 = x->sc_vec[i].w_list;
336 template_conformglist(tfrom, tto, gl2, conformaction);
337 }
338 else if (ds->ds_type == DT_ARRAY)
339 {
340 template_conformarray(tfrom, tto, conformaction,
341 x->sc_vec[i].w_array);
342 }
343 }
344 return (x);
345}
346
347 /* conform an array, recursively conforming sublists and arrays */
348static void template_conformarray(t_template *tfrom, t_template *tto,
349 int *conformaction, t_array *a)
350{
351 int i;
352 if (a->a_templatesym == tfrom->t_sym)
353 {
354 /* the array elements must all be conformed */
355 int oldelemsize = sizeof(t_word) * tfrom->t_n,
356 newelemsize = sizeof(t_word) * tto->t_n;
357 char *newarray = getbytes(sizeof(t_word) * tto->t_n * a->a_n);
358 char *oldarray = a->a_vec;
359 if (a->a_elemsize != oldelemsize)
360 bug("template_conformarray");
361 for (i = 0; i < a->a_n; i++)
362 {
363 t_word *wp = (t_word *)(newarray + newelemsize * i);
364 word_init(wp, tto, &a->a_gp);
365 template_conformwords(tfrom, tto, conformaction,
366 (t_word *)(oldarray + oldelemsize * i), wp);
367 }
368 }
369 bug("template_conformarray: this part not written");
370 /* go through item by item conforming subarrays and sublists... */
371}
372
373 /* this routine searches for every scalar in the glist that belongs
374 to the "from" template and makes it belong to the "to" template. Descend
375 glists recursively.
376 We don't handle redrawing here; this is to be filled in LATER... */
377
378static void template_conformglist(t_template *tfrom, t_template *tto,
379 t_glist *glist, int *conformaction)
380{
381 t_gobj *g;
382 /* post("conform glist %s", glist->gl_name->s_name); */
383 for (g = glist->gl_list; g; g = g->g_next)
384 {
385 if (pd_class(&g->g_pd) == scalar_class)
386 g = &template_conformscalar(tfrom, tto, conformaction,
387 glist, (t_scalar *)g)->sc_gobj;
388 else if (pd_class(&g->g_pd) == canvas_class)
389 template_conformglist(tfrom, tto, (t_glist *)g, conformaction);
390 }
391}
392
393 /* globally conform all scalars from one template to another */
394void template_conform(t_template *tfrom, t_template *tto)
395{
396 int nto = tto->t_n, nfrom = tfrom->t_n, i, j,
397 *conformaction = (int *)getbytes(sizeof(int) * nto),
398 *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0;
399 for (i = 0; i < nto; i++)
400 conformaction[i] = -1;
401 for (i = 0; i < nfrom; i++)
402 conformedfrom[i] = 0;
403 for (i = 0; i < nto; i++)
404 {
405 t_dataslot *dataslot = &tto->t_vec[i];
406 for (j = 0; j < nfrom; j++)
407 {
408 t_dataslot *dataslot2 = &tfrom->t_vec[j];
409 if (dataslot_matches(dataslot, dataslot2, 1))
410 {
411 conformaction[i] = j;
412 conformedfrom[j] = 1;
413 }
414 }
415 }
416 for (i = 0; i < nto; i++)
417 if (conformaction[i] < 0)
418 {
419 t_dataslot *dataslot = &tto->t_vec[i];
420 for (j = 0; j < nfrom; j++)
421 if (!conformedfrom[j] &&
422 dataslot_matches(dataslot, &tfrom->t_vec[j], 1))
423 {
424 conformaction[i] = j;
425 conformedfrom[j] = 1;
426 }
427 }
428 if (nto != nfrom)
429 doit = 1;
430 else for (i = 0; i < nto; i++)
431 if (conformaction[i] != i)
432 doit = 1;
433
434 if (doit)
435 {
436 t_glist *gl;
437 /* post("conforming template '%s' to new structure",
438 tfrom->t_sym->s_name);
439 for (i = 0; i < nto; i++)
440 post("... %d", conformaction[i]); */
441 for (gl = canvas_list; gl; gl = gl->gl_next)
442 template_conformglist(tfrom, tto, gl, conformaction);
443 }
444 freebytes(conformaction, sizeof(int) * nto);
445 freebytes(conformedfrom, sizeof(int) * nfrom);
446}
447
448t_template *template_findbyname(t_symbol *s)
449{
450 int i;
451 if (s == &s_float)
452 return (&template_float);
453 else return ((t_template *)pd_findbyclass(s, template_class));
454}
455
456t_canvas *template_findcanvas(t_template *template)
457{
458 t_gtemplate *gt;
459 if (!template)
460 bug("template_findcanvas");
461 if (!(gt = template->t_list))
462 return (0);
463 return (gt->x_owner);
464 /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */
465}
466
467 /* call this when reading a patch from a file to declare what templates
468 we'll need. If there's already a template, check if it matches.
469 If it doesn't it's still OK as long as there are no "struct" (gtemplate)
470 objects hanging from it; we just conform everyone to the new template.
471 If there are still struct objects belonging to the other template, we're
472 in trouble. LATER we'll figure out how to conform the new patch's objects
473 to the pre-existing struct. */
474static void *template_usetemplate(void *dummy, t_symbol *s,
475 int argc, t_atom *argv)
476{
477 t_template *x;
478 t_symbol *templatesym =
479 canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
480 if (!argc)
481 return (0);
482 argc--; argv++;
483 /* check if there's already a template by this name. */
484 if ((x = (t_template *)pd_findbyclass(templatesym, template_class)))
485 {
486 t_template *y = template_new(&s_, argc, argv);
487 /* If the new template is the same as the old one,
488 there's nothing to do. */
489 if (!template_match(x, y))
490 {
491 /* Are there "struct" objects upholding this template? */
492 if (x->t_list)
493 {
494 /* don't know what to do here! */
495 error("%s: template mismatch",
496 templatesym->s_name);
497 }
498 else
499 {
500 /* conform everyone to the new template */
501 template_conform(x, y);
502 pd_free(&x->t_pdobj);
503 template_new(templatesym, argc, argv);
504 }
505 }
506 pd_free(&y->t_pdobj);
507 }
508 /* otherwise, just make one. */
509 else template_new(templatesym, argc, argv);
510 return (0);
511}
512
513 /* here we assume someone has already cleaned up all instances of this. */
514void template_free(t_template *x)
515{
516 if (*x->t_sym->s_name)
517 pd_unbind(&x->t_pdobj, x->t_sym);
518 t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec));
519}
520
521static void template_setup(void)
522{
523 template_class = class_new(gensym("template"), 0, (t_method)template_free,
524 sizeof(t_template), CLASS_PD, 0);
525 class_addmethod(pd_canvasmaker, (t_method)template_usetemplate,
526 gensym("struct"), A_GIMME, 0);
527
528}
529
530/* ---------------- gtemplates. One per canvas. ----------- */
531
532/* this is a "text" object that searches for, and if necessary creates,
533a "template" (above). Other objects in the canvas then can give drawing
534instructions for the template. The template doesn't go away when the
535gtemplate is deleted, so that you can replace it with
536another one to add new fields, for example. */
537
538static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv)
539{
540 t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
541 t_template *t = template_findbyname(sym);
542 int i;
543 t_symbol *sx = gensym("x");
544 x->x_owner = canvas_getcurrent();
545 x->x_next = 0;
546 x->x_sym = sym;
547 x->x_argc = argc;
548 x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom));
549 for (i = 0; i < argc; i++)
550 x->x_argv[i] = argv[i];
551
552 /* already have a template by this name? */
553 if (t)
554 {
555 x->x_template = t;
556 /* if it's already got a "struct" or "gtemplate" object we
557 just tack this one to the end of the list and leave it
558 there. */
559 if (t->t_list)
560 {
561 t_gtemplate *x2, *x3;
562 for (x2 = x->x_template->t_list; x3 = x2->x_next; x2 = x3)
563 ;
564 x2->x_next = x;
565 post("template %s: warning: already exists.", sym->s_name);
566 }
567 else
568 {
569 /* if there's none, we just replace the template with
570 our own and conform it. */
571 t_template *y = template_new(&s_, argc, argv);
572 /* Unless the new template is different from the old one,
573 there's nothing to do. */
574 if (!template_match(t, y))
575 {
576 /* conform everyone to the new template */
577 template_conform(t, y);
578 pd_free(&t->t_pdobj);
579 t = template_new(sym, argc, argv);
580 }
581 pd_free(&y->t_pdobj);
582 t->t_list = x;
583 }
584 }
585 else
586 {
587 /* otherwise make a new one and we're the only struct on it. */
588 x->x_template = t = template_new(sym, argc, argv);
589 t->t_list = x;
590 }
591 return (x);
592}
593
594static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv)
595{
596 t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
597 t_symbol *sym = atom_getsymbolarg(0, argc, argv);
598 if (argc >= 1)
599 argc--; argv++;
600 return (gtemplate_donew(canvas_makebindsym(sym), argc, argv));
601}
602
603 /* old version (0.34) -- delete 2003 or so */
604static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv)
605{
606 t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
607 t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name);
608 static int warned;
609 if (!warned)
610 {
611 post("warning -- 'template' (%s) is obsolete; replace with 'struct'",
612 sym->s_name);
613 warned = 1;
614 }
615 return (gtemplate_donew(sym, argc, argv));
616}
617
618t_template *gtemplate_get(t_gtemplate *x)
619{
620 return (x->x_template);
621}
622
623static void gtemplate_free(t_gtemplate *x)
624{
625 /* get off the template's list */
626 t_template *t = x->x_template;
627 if (x == t->t_list)
628 {
629 if (x->x_next)
630 {
631 /* if we were first on the list, and there are others on
632 the list, make a new template corresponding to the new
633 first-on-list and replace teh existing template with it. */
634 t_template *z = template_new(&s_, x->x_argc, x->x_argv);
635 template_conform(t, z);
636 pd_free(&t->t_pdobj);
637 pd_free(&z->t_pdobj);
638 z = template_new(x->x_sym, x->x_argc, x->x_argv);
639 z->t_list = x->x_next;
640 }
641 else t->t_list = 0;
642 }
643 else
644 {
645 t_gtemplate *x2, *x3;
646 for (x2 = t->t_list; x3 = x2->x_next; x2 = x3)
647 {
648 if (x == x3)
649 {
650 x2->x_next = x3->x_next;
651 break;
652 }
653 }
654 }
655 freebytes(x->x_argv, sizeof(t_atom) * x->x_argc);
656}
657
658static void gtemplate_setup(void)
659{
660 gtemplate_class = class_new(gensym("struct"),
661 (t_newmethod)gtemplate_new, (t_method)gtemplate_free,
662 sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0);
663 class_addcreator((t_newmethod)gtemplate_new_old, gensym("template"),
664 A_GIMME, 0);
665}
666
667/* --------------- FIELD DESCRIPTORS ---------------------- */
668
669/* a field descriptor can hold a constant or a variable; if a variable,
670it's the name of a field in the template we belong to. LATER, we might
671want to cache the offset of the field so we don't have to search for it
672every single time we draw the object.
673*/
674
675typedef struct _fielddesc
676{
677 char fd_type; /* LATER consider removing this? */
678 char fd_var;
679 union
680 {
681 t_float fd_float; /* the field is a constant float */
682 t_symbol *fd_symbol; /* the field is a constant symbol */
683 t_symbol *fd_varsym; /* the field is variable and this is the name */
684 } fd_un;
685} t_fielddesc;
686
687#define FIELDDESC_SETFLOAT(x, f) \
688 ((x)->fd_type = A_FLOAT, (x)->fd_var = 0, (x)->fd_un.fd_float = (f))
689#define FIELDDESC_SETSYMBOL(x, s) \
690 ((x)->fd_type = A_SYMBOL, (x)->fd_var = 0, (x)->fd_un.fd_symbol = (s))
691#define FIELDDESC_SETVAR(x, s, type) \
692 ((x)->fd_type = type, (x)->fd_var = 1, (x)->fd_un.fd_varsym = (s))
693
694#define CLOSED 1
695#define BEZ 2
696#define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */
697
698static void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv)
699{
700 if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0);
701 else if (argv->a_type == A_SYMBOL)
702 FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_FLOAT);
703 else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float);
704}
705
706static void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv)
707{
708 if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0);
709 else if (argv->a_type == A_SYMBOL)
710 FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_ARRAY);
711 else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float);
712}
713
714static t_float fielddesc_getfloat(t_fielddesc *f, t_template *template,
715 t_word *wp, int loud)
716{
717 if (f->fd_type == A_FLOAT)
718 {
719 if (f->fd_var)
720 return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud));
721 else return (f->fd_un.fd_float);
722 }
723 else
724 {
725 if (loud)
726 error("symbolic data field used as number");
727 return (0);
728 }
729}
730
731static t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template,
732 t_word *wp, int loud)
733{
734 if (f->fd_type == A_SYMBOL)
735 {
736 if (f->fd_var)
737 return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud));
738 else return (f->fd_un.fd_symbol);
739 }
740 else
741 {
742 if (loud)
743 error("numeric data field used as symbol");
744 return (&s_);
745 }
746}
747
748/* ---------------- curves and polygons (joined segments) ---------------- */
749
750/*
751curves belong to templates and describe how the data in the template are to
752be drawn. The coordinates of the curve (and other display features) can
753be attached to fields in the template.
754*/
755
756t_class *curve_class;
757
758typedef struct _curve
759{
760 t_object x_obj;
761 int x_flags; /* CLOSED and/or BEZ */
762 t_fielddesc x_fillcolor;
763 t_fielddesc x_outlinecolor;
764 t_fielddesc x_width;
765 int x_npoints;
766 t_fielddesc *x_vec;
767} t_curve;
768
769static void *curve_new(t_symbol *classsym, t_int argc, t_atom *argv)
770{
771 t_curve *x = (t_curve *)pd_new(curve_class);
772 char *classname = classsym->s_name;
773 int flags = 0;
774 int nxy, i;
775 t_fielddesc *fd;
776 if (classname[0] == 'f')
777 {
778 classname += 6;
779 flags |= CLOSED;
780 if (argc) fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++);
781 else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
782 }
783 else classname += 4;
784 if (classname[0] == 'c') flags |= BEZ;
785 x->x_flags = flags;
786 if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);
787 else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
788 if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);
789 else FIELDDESC_SETFLOAT(&x->x_width, 1);
790 if (argc < 0) argc = 0;
791 nxy = (argc + (argc & 1));
792 x->x_npoints = (nxy>>1);
793 x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc));
794 for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++)
795 fielddesc_setfloatarg(fd, 1, argv);
796 if (argc & 1) FIELDDESC_SETFLOAT(fd, 0);
797
798 return (x);
799}
800
801/* -------------------- widget behavior for curve ------------ */
802
803static void curve_getrect(t_gobj *z, t_glist *glist,
804 t_word *data, t_template *template, float basex, float basey,
805 int *xp1, int *yp1, int *xp2, int *yp2)
806{
807 t_curve *x = (t_curve *)z;
808 int i, n = x->x_npoints;
809 t_fielddesc *f = x->x_vec;
810 int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;
811 for (i = 0, f = x->x_vec; i < n; i++, f += 2)
812 {
813 int xloc = glist_xtopixels(glist,
814 basex + fielddesc_getfloat(f, template, data, 0));
815 int yloc = glist_ytopixels(glist,
816 basey + fielddesc_getfloat(f+1, template, data, 0));
817 if (xloc < x1) x1 = xloc;
818 if (xloc > x2) x2 = xloc;
819 if (yloc < y1) y1 = yloc;
820 if (yloc > y2) y2 = yloc;
821 }
822 *xp1 = x1;
823 *yp1 = y1;
824 *xp2 = x2;
825 *yp2 = y2;
826}
827
828static void curve_displace(t_gobj *z, t_glist *glist,
829 t_word *data, t_template *template, float basex, float basey,
830 int dx, int dy)
831{
832 /* refuse */
833}
834
835static void curve_select(t_gobj *z, t_glist *glist,
836 t_word *data, t_template *template, float basex, float basey,
837 int state)
838{
839 /* fill in later */
840}
841
842static void curve_activate(t_gobj *z, t_glist *glist,
843 t_word *data, t_template *template, float basex, float basey,
844 int state)
845{
846 /* fill in later */
847}
848
849static int rangecolor(int n) /* 0 to 9 in 5 steps */
850{
851 int n2 = n/2; /* 0 to 4 */
852 int ret = (n2 << 6); /* 0 to 256 in 5 steps */
853 if (ret > 255) ret = 255;
854 return (ret);
855}
856
857static void numbertocolor(int n, char *s)
858{
859 int red, blue, green;
860 if (n < 0) n = 0;
861 red = n / 100;
862 blue = ((n / 10) % 10);
863 green = n % 10;
864 sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(red), rangecolor(blue),
865 rangecolor(green));
866}
867
868static void curve_vis(t_gobj *z, t_glist *glist,
869 t_word *data, t_template *template, float basex, float basey,
870 int vis)
871{
872 t_curve *x = (t_curve *)z;
873 int i, n = x->x_npoints;
874 t_fielddesc *f = x->x_vec;
875
876 if (vis)
877 {
878 if (n > 1)
879 {
880 int flags = x->x_flags, closed = (flags & CLOSED);
881 float width = fielddesc_getfloat(&x->x_width, template, data, 1);
882 char outline[20], fill[20];
883 if (width < 1) width = 1;
884 numbertocolor(
885 fielddesc_getfloat(&x->x_outlinecolor, template, data, 1),
886 outline);
887 if (flags & CLOSED)
888 {
889 numbertocolor(
890 fielddesc_getfloat(&x->x_fillcolor, template, data, 1),
891 fill);
892 sys_vgui(".x%x.c create polygon\\\n",
893 glist_getcanvas(glist));
894 }
895 else sys_vgui(".x%x.c create line\\\n",
896 glist_getcanvas(glist));
897 for (i = 0, f = x->x_vec; i < n; i++, f += 2)
898 {
899 float xloc = glist_xtopixels(glist,
900 basex + fielddesc_getfloat(f, template, data, 1));
901 float yloc = glist_ytopixels(glist,
902 basey + fielddesc_getfloat(f+1, template, data, 1));
903 sys_vgui("%d %d\\\n", (int)xloc, (int)yloc);
904 }
905 sys_vgui("-width %f\\\n",
906 fielddesc_getfloat(&x->x_width, template, data, 1));
907 if (flags & CLOSED) sys_vgui("-fill %s -outline %s\\\n",
908 fill, outline);
909 else sys_vgui("-fill %s\\\n", outline);
910 if (flags & BEZ) sys_vgui("-smooth 1\\\n");
911 sys_vgui("-tags curve%x\n", data);
912 }
913 else post("warning: curves need at least two points to be graphed");
914 }
915 else
916 {
917 if (n > 1) sys_vgui(".x%x.c delete curve%x\n",
918 glist_getcanvas(glist), data);
919 }
920}
921
922static int curve_motion_field;
923static float curve_motion_xcumulative;
924static float curve_motion_xbase;
925static float curve_motion_xper;
926static float curve_motion_ycumulative;
927static float curve_motion_ybase;
928static float curve_motion_yper;
929static t_glist *curve_motion_glist;
930static t_gobj *curve_motion_gobj;
931static t_word *curve_motion_wp;
932static t_template *curve_motion_template;
933
934 /* LATER protect against the template changing or the scalar disappearing
935 probably by attaching a gpointer here ... */
936
937static void curve_motion(void *z, t_floatarg dx, t_floatarg dy)
938{
939 t_curve *x = (t_curve *)z;
940 t_fielddesc *f = x->x_vec + curve_motion_field;
941 curve_motion_xcumulative += dx;
942 curve_motion_ycumulative += dy;
943 if (f->fd_var)
944 {
945 template_setfloat(curve_motion_template,
946 f->fd_un.fd_varsym,
947 curve_motion_wp,
948 curve_motion_xbase + curve_motion_xcumulative * curve_motion_xper,
949 1);
950 }
951 if ((f+1)->fd_var)
952 {
953 template_setfloat(curve_motion_template,
954 (f+1)->fd_un.fd_varsym,
955 curve_motion_wp,
956 curve_motion_ybase + curve_motion_ycumulative * curve_motion_yper,
957 1);
958 }
959 glist_redrawitem(curve_motion_glist, curve_motion_gobj);
960}
961
962static int curve_click(t_gobj *z, t_glist *glist,
963 t_scalar *sc, t_template *template, float basex, float basey,
964 int xpix, int ypix, int shift, int alt, int dbl, int doit)
965{
966 t_curve *x = (t_curve *)z;
967 int i, n = x->x_npoints;
968 int bestn = -1;
969 int besterror = 0x7fffffff;
970 t_fielddesc *f = x->x_vec;
971 t_word *data = sc->sc_vec;
972 for (i = 0, f = x->x_vec; i < n; i++, f += 2)
973 {
974 int xloc = glist_xtopixels(glist,
975 basex + fielddesc_getfloat(f, template, data, 0));
976 int yloc = glist_ytopixels(glist,
977 basey + fielddesc_getfloat(f+1, template, data, 0));
978 int xerr = xloc - xpix, yerr = yloc - ypix;
979 if (!f->fd_var && !(f+1)->fd_var)
980 continue;
981 if (xerr < 0)
982 xerr = -xerr;
983 if (yerr < 0)
984 yerr = -yerr;
985 if (yerr > xerr)
986 xerr = yerr;
987 if (xerr < besterror)
988 {
989 besterror = xerr;
990 bestn = i;
991 curve_motion_xbase = fielddesc_getfloat(f, template, data, 0);
992 curve_motion_ybase = fielddesc_getfloat(f+1, template, data, 0);
993 }
994 }
995 if (besterror > 10)
996 return (0);
997 if (doit)
998 {
999 curve_motion_xper = glist_pixelstox(glist, 1)
1000 - glist_pixelstox(glist, 0);
1001 curve_motion_yper = glist_pixelstoy(glist, 1)
1002 - glist_pixelstoy(glist, 0);
1003 curve_motion_xcumulative = curve_motion_ycumulative = 0;
1004 curve_motion_glist = glist;
1005 curve_motion_gobj = &sc->sc_gobj;
1006 curve_motion_wp = data;
1007 curve_motion_field = 2*bestn;
1008 curve_motion_template = template;
1009 glist_grab(glist, z, curve_motion, 0, xpix, ypix);
1010 }
1011 return (1);
1012}
1013
1014t_parentwidgetbehavior curve_widgetbehavior =
1015{
1016 curve_getrect,
1017 curve_displace,
1018 curve_select,
1019 curve_activate,
1020 curve_vis,
1021 curve_click,
1022};
1023
1024static void curve_free(t_curve *x)
1025{
1026 t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec));
1027}
1028
1029static void curve_setup(void)
1030{
1031 curve_class = class_new(gensym("drawpolygon"), (t_newmethod)curve_new,
1032 (t_method)curve_free, sizeof(t_curve), CLASS_NOINLET, A_GIMME, 0);
1033 class_setdrawcommand(curve_class);
1034 class_addcreator((t_newmethod)curve_new, gensym("drawcurve"),
1035 A_GIMME, 0);
1036 class_addcreator((t_newmethod)curve_new, gensym("filledpolygon"),
1037 A_GIMME, 0);
1038 class_addcreator((t_newmethod)curve_new, gensym("filledcurve"),
1039 A_GIMME, 0);
1040 class_setparentwidget(curve_class, &curve_widgetbehavior);
1041}
1042
1043/* --------- plots for showing arrays --------------- */
1044
1045t_class *plot_class;
1046
1047typedef struct _plot
1048{
1049 t_object x_obj;
1050 int x_flags;
1051 t_fielddesc x_outlinecolor;
1052 t_fielddesc x_width;
1053 t_fielddesc x_xloc;
1054 t_fielddesc x_yloc;
1055 t_fielddesc x_xinc;
1056 t_fielddesc x_data;
1057} t_plot;
1058
1059static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv)
1060{
1061 t_plot *x = (t_plot *)pd_new(plot_class);
1062 int flags = 0;
1063 int nxy, i;
1064 t_fielddesc *fd;
1065 t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);
1066 if (!strcmp(firstarg->s_name, "curve"))
1067 {
1068 flags |= BEZ;
1069 argc--, argv++;
1070 }
1071 if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++);
1072 else FIELDDESC_SETFLOAT(&x->x_data, 1);
1073 if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);
1074 else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
1075 if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);
1076 else FIELDDESC_SETFLOAT(&x->x_width, 1);
1077 if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
1078 else FIELDDESC_SETFLOAT(&x->x_xloc, 1);
1079 if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
1080 else FIELDDESC_SETFLOAT(&x->x_yloc, 1);
1081 if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++);
1082 else FIELDDESC_SETFLOAT(&x->x_xinc, 1);
1083 x->x_flags = flags;
1084 return (x);
1085}
1086
1087/* -------------------- widget behavior for plot ------------ */
1088
1089
1090 /* get everything we'll need from the owner template of the array being
1091 plotted. Not used for garrays, but see below */
1092static int plot_readownertemplate(t_plot *x,
1093 t_word *data, t_template *ownertemplate,
1094 t_symbol **elemtemplatesymp, t_array **arrayp,
1095 float *linewidthp, float *xlocp, float *xincp, float *ylocp)
1096{
1097 int arrayonset, type;
1098 t_symbol *elemtemplatesym;
1099 t_array *array;
1100
1101 /* find the data and verify it's an array */
1102 if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var)
1103 {
1104 error("plot: needs an array field");
1105 return (-1);
1106 }
1107 if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym,
1108 &arrayonset, &type, &elemtemplatesym))
1109 {
1110 error("plot: %s: no such field", x->x_data.fd_un.fd_varsym->s_name);
1111 return (-1);
1112 }
1113 if (type != DT_ARRAY)
1114 {
1115 error("plot: %s: not an array", x->x_data.fd_un.fd_varsym->s_name);
1116 return (-1);
1117 }
1118 array = *(t_array **)(((char *)data) + arrayonset);
1119 *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1);
1120 *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1);
1121 *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1);
1122 *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1);
1123 *elemtemplatesymp = elemtemplatesym;
1124 *arrayp = array;
1125 return (0);
1126}
1127
1128 /* get everything else you could possibly need about a plot,
1129 either for plot's own purposes or for plotting a "garray" */
1130int array_getfields(t_symbol *elemtemplatesym,
1131 t_canvas **elemtemplatecanvasp,
1132 t_template **elemtemplatep, int *elemsizep,
1133 int *xonsetp, int *yonsetp, int *wonsetp)
1134{
1135 int arrayonset, elemsize, yonset, wonset, xonset, type;
1136 t_template *elemtemplate;
1137 t_symbol *dummy;
1138 t_canvas *elemtemplatecanvas = 0;
1139
1140 /* the "float" template is special in not having to have a canvas;
1141 template_findbyname is hardwired to return a predefined
1142 template. */
1143
1144 if (!(elemtemplate = template_findbyname(elemtemplatesym)))
1145 {
1146 error("plot: %s: no such template", elemtemplatesym->s_name);
1147 return (-1);
1148 }
1149 if (!((elemtemplatesym == &s_float) ||
1150 (elemtemplatecanvas = template_findcanvas(elemtemplate))))
1151 {
1152 error("plot: %s: no canvas for this template", elemtemplatesym->s_name);
1153 return (-1);
1154 }
1155 elemsize = elemtemplate->t_n * sizeof(t_word);
1156 if (!template_find_field(elemtemplate, gensym("y"), &yonset, &type, &dummy)
1157 || type != DT_FLOAT)
1158 yonset = -1;
1159 if (!template_find_field(elemtemplate, gensym("x"), &xonset, &type, &dummy)
1160 || type != DT_FLOAT)
1161 xonset = -1;
1162 if (!template_find_field(elemtemplate, gensym("w"), &wonset, &type, &dummy)
1163 || type != DT_FLOAT)
1164 wonset = -1;
1165
1166 /* fill in slots for return values */
1167 *elemtemplatecanvasp = elemtemplatecanvas;
1168 *elemtemplatep = elemtemplate;
1169 *elemsizep = elemsize;
1170 *xonsetp = xonset;
1171 *yonsetp = yonset;
1172 *wonsetp = wonset;
1173 return (0);
1174}
1175
1176static void plot_getrect(t_gobj *z, t_glist *glist,
1177 t_word *data, t_template *template, float basex, float basey,
1178 int *xp1, int *yp1, int *xp2, int *yp2)
1179{
1180 t_plot *x = (t_plot *)z;
1181 int elemsize, yonset, wonset, xonset;
1182 t_canvas *elemtemplatecanvas;
1183 t_template *elemtemplate;
1184 t_symbol *elemtemplatesym;
1185 float linewidth, xloc, xinc, yloc;
1186 t_array *array;
1187 float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
1188 int i;
1189 float xpix, ypix, wpix;
1190
1191 if (!plot_readownertemplate(x, data, template,
1192 &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) &&
1193 !array_getfields(elemtemplatesym, &elemtemplatecanvas,
1194 &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
1195 {
1196 for (i = 0; i < array->a_n; i++)
1197 {
1198 array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,
1199 xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc,
1200 &xpix, &ypix, &wpix);
1201 if (xpix < x1)
1202 x1 = xpix;
1203 if (xpix > x2)
1204 x2 = xpix;
1205 if (ypix - wpix < y1)
1206 y1 = ypix - wpix;
1207 if (ypix + wpix > y2)
1208 y2 = ypix + wpix;
1209 }
1210 }
1211
1212 *xp1 = x1;
1213 *yp1 = y1;
1214 *xp2 = x2;
1215 *yp2 = y2;
1216}
1217
1218static void plot_displace(t_gobj *z, t_glist *glist,
1219 t_word *data, t_template *template, float basex, float basey,
1220 int dx, int dy)
1221{
1222 /* not yet */
1223}
1224
1225static void plot_select(t_gobj *z, t_glist *glist,
1226 t_word *data, t_template *template, float basex, float basey,
1227 int state)
1228{
1229 /* not yet */
1230}
1231
1232static void plot_activate(t_gobj *z, t_glist *glist,
1233 t_word *data, t_template *template, float basex, float basey,
1234 int state)
1235{
1236 /* not yet */
1237}
1238
1239static void plot_vis(t_gobj *z, t_glist *glist,
1240 t_word *data, t_template *template, float basex, float basey,
1241 int vis)
1242{
1243 t_plot *x = (t_plot *)z;
1244 int elemsize, yonset, wonset, xonset;
1245 t_canvas *elemtemplatecanvas;
1246 t_template *elemtemplate;
1247 t_symbol *elemtemplatesym;
1248 float linewidth, xloc, xinc, yloc;
1249 t_array *array;
1250 int nelem;
1251 char *elem;
1252 if (plot_readownertemplate(x, data, template,
1253 &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) ||
1254 array_getfields(elemtemplatesym, &elemtemplatecanvas,
1255 &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
1256 return;
1257 nelem = array->a_n;
1258 elem = (char *)array->a_vec;
1259 if (vis)
1260 {
1261 char outline[20];
1262 int lastpixel = -1, ndrawn = 0;
1263 float xsum, yval = 0, wval = 0, xpix;
1264 int ixpix = 0, i;
1265
1266 /* draw the trace */
1267 numbertocolor(fielddesc_getfloat(&x->x_outlinecolor, template, data, 1),
1268 outline);
1269 if (wonset >= 0)
1270 {
1271 /* found "w" field which controls linewidth. The trace is
1272 a filled polygon with 2n points. */
1273 sys_vgui(".x%x.c create polygon \\\n",
1274 glist_getcanvas(glist));
1275
1276 for (i = 0, xsum = xloc; i < nelem; i++)
1277 {
1278 float usexloc;
1279 if (xonset >= 0)
1280 usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
1281 else usexloc = xsum, xsum += xinc;
1282 if (yonset >= 0)
1283 yval = *(float *)((elem + elemsize * i) + yonset);
1284 else yval = 0;
1285 wval = *(float *)((elem + elemsize * i) + wonset);
1286 xpix = glist_xtopixels(glist, basex + usexloc);
1287 ixpix = xpix + 0.5;
1288 if (xonset >= 0 || ixpix != lastpixel)
1289 {
1290 sys_vgui("%d %f \\\n", ixpix,
1291 glist_ytopixels(glist,
1292 basey + yloc + yval - wval));
1293 ndrawn++;
1294 }
1295 lastpixel = ixpix;
1296 if (ndrawn >= 1000) goto ouch;
1297 }
1298 lastpixel = -1;
1299 for (i = nelem-1; i >= 0; i--)
1300 {
1301 float usexloc;
1302 if (xonset >= 0)
1303 usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
1304 else xsum -= xinc, usexloc = xsum;
1305 if (yonset >= 0)
1306 yval = *(float *)((elem + elemsize * i) + yonset);
1307 else yval = 0;
1308 wval = *(float *)((elem + elemsize * i) + wonset);
1309 xpix = glist_xtopixels(glist, basex + usexloc);
1310 ixpix = xpix + 0.5;
1311 if (xonset >= 0 || ixpix != lastpixel)
1312 {
1313 sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist,
1314 basey + yloc + yval + wval));
1315 ndrawn++;
1316 }
1317 lastpixel = ixpix;
1318 if (ndrawn >= 1000) goto ouch;
1319 }
1320 /* TK will complain if there aren't at least 3 points. There
1321 should be at least two already. */
1322 if (ndrawn < 4)
1323 {
1324 sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist,
1325 basey + yloc + yval + wval));
1326 sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist,
1327 basey + yloc + yval - wval));
1328 }
1329 ouch:
1330 sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline);
1331 if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n");
1332
1333 sys_vgui("-tags plot%x\n", data);
1334 }
1335 else if (linewidth > 0)
1336 {
1337 /* no "w" field. If the linewidth is positive, draw a
1338 segmented line with the requested width; otherwise don't
1339 draw the trace at all. */
1340 sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist));
1341
1342 for (xsum = xloc, i = 0; i < nelem; i++)
1343 {
1344 float usexloc;
1345 if (xonset >= 0)
1346 usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
1347 else usexloc = xsum, xsum += xinc;
1348 if (yonset >= 0)
1349 yval = *(float *)((elem + elemsize * i) + yonset);
1350 else yval = 0;
1351 xpix = glist_xtopixels(glist, basex + usexloc);
1352 ixpix = xpix + 0.5;
1353 if (xonset >= 0 || ixpix != lastpixel)
1354 {
1355 sys_vgui("%d %f \\\n", ixpix,
1356 glist_ytopixels(glist, basey + yloc + yval));
1357 ndrawn++;
1358 }
1359 lastpixel = ixpix;
1360 if (ndrawn >= 1000) break;
1361 }
1362 /* TK will complain if there aren't at least 2 points... */
1363 if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n");
1364 else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix + 10,
1365 glist_ytopixels(glist, basey + yloc + yval));
1366
1367 sys_vgui("-width %f\\\n", linewidth);
1368 sys_vgui("-fill %s\\\n", outline);
1369 if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n");
1370
1371 sys_vgui("-tags plot%x\n", data);
1372 }
1373 /* We're done with the outline; now draw all the points.
1374 This code is inefficient since the template has to be
1375 searched for drawing instructions for every last point. */
1376
1377 for (xsum = xloc, i = 0; i < nelem; i++)
1378 {
1379 float usexloc, useyloc;
1380 t_gobj *y;
1381 if (xonset >= 0)
1382 usexloc = basex + xloc +
1383 *(float *)((elem + elemsize * i) + xonset);
1384 else usexloc = basex + xsum, xsum += xinc;
1385 if (yonset >= 0)
1386 yval = *(float *)((elem + elemsize * i) + yonset);
1387 else yval = 0;
1388 useyloc = basey + yloc + yval;
1389 for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
1390 {
1391 t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
1392 if (!wb) continue;
1393 (*wb->w_parentvisfn)(y, glist,
1394 (t_word *)(elem + elemsize * i),
1395 elemtemplate, usexloc, useyloc, vis);
1396 }
1397 }
1398 }
1399 else
1400 {
1401 /* un-draw the individual points */
1402 int i;
1403 for (i = 0; i < nelem; i++)
1404 {
1405 t_gobj *y;
1406 for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
1407 {
1408 t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
1409 if (!wb) continue;
1410 (*wb->w_parentvisfn)(y, glist,
1411 (t_word *)(elem + elemsize * i), elemtemplate,
1412 0, 0, 0);
1413 }
1414 }
1415 /* and then the trace */
1416 sys_vgui(".x%x.c delete plot%x\n",
1417 glist_getcanvas(glist), data);
1418 }
1419}
1420
1421
1422static int plot_click(t_gobj *z, t_glist *glist,
1423 t_scalar *sc, t_template *template, float basex, float basey,
1424 int xpix, int ypix, int shift, int alt, int dbl, int doit)
1425{
1426 t_plot *x = (t_plot *)z;
1427 t_symbol *elemtemplatesym;
1428 float linewidth, xloc, xinc, yloc;
1429 t_array *array;
1430 t_word *data = sc->sc_vec;
1431
1432 if (!plot_readownertemplate(x, data, template,
1433 &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc))
1434 {
1435 return (array_doclick(array, glist, &sc->sc_gobj,
1436 elemtemplatesym,
1437 linewidth, basex + xloc, xinc, basey + yloc,
1438 xpix, ypix, shift, alt, dbl, doit));
1439 }
1440 else return (0);
1441}
1442
1443t_parentwidgetbehavior plot_widgetbehavior =
1444{
1445 plot_getrect,
1446 plot_displace,
1447 plot_select,
1448 plot_activate,
1449 plot_vis,
1450 plot_click,
1451};
1452
1453static void plot_setup(void)
1454{
1455 plot_class = class_new(gensym("plot"), (t_newmethod)plot_new, 0,
1456 sizeof(t_plot), CLASS_NOINLET, A_GIMME, 0);
1457 class_setdrawcommand(plot_class);
1458 class_setparentwidget(plot_class, &plot_widgetbehavior);
1459}
1460
1461/* ---------------- drawnumber: draw a number ---------------- */
1462
1463/*
1464 drawnumbers draw numeric fields at controllable locations, with
1465 controllable color and label .
1466 invocation: (drawnumber|drawsymbol) variable x y color label
1467*/
1468
1469t_class *drawnumber_class;
1470
1471#define DRAW_SYMBOL 1
1472
1473typedef struct _drawnumber
1474{
1475 t_object x_obj;
1476 t_fielddesc x_value;
1477 t_fielddesc x_xloc;
1478 t_fielddesc x_yloc;
1479 t_fielddesc x_color;
1480 t_symbol *x_label;
1481 int x_flags;
1482} t_drawnumber;
1483
1484static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv)
1485{
1486 t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class);
1487 char *classname = classsym->s_name;
1488 int flags = 0;
1489 if (classname[4] == 's')
1490 flags |= DRAW_SYMBOL;
1491 x->x_flags = flags;
1492 if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++);
1493 else FIELDDESC_SETFLOAT(&x->x_value, 0);
1494 if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
1495 else FIELDDESC_SETFLOAT(&x->x_xloc, 0);
1496 if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
1497 else FIELDDESC_SETFLOAT(&x->x_yloc, 0);
1498 if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++);
1499 else FIELDDESC_SETFLOAT(&x->x_color, 1);
1500 if (argc)
1501 x->x_label = atom_getsymbolarg(0, argc, argv);
1502 else x->x_label = &s_;
1503
1504 return (x);
1505}
1506
1507/* -------------------- widget behavior for drawnumber ------------ */
1508
1509#define DRAWNUMBER_BUFSIZE 80
1510static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap)
1511{
1512 int nchars;
1513 strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE);
1514 buf[DRAWNUMBER_BUFSIZE - 1] = 0;
1515 nchars = strlen(buf);
1516 atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars);
1517}
1518
1519static void drawnumber_getrect(t_gobj *z, t_glist *glist,
1520 t_word *data, t_template *template, float basex, float basey,
1521 int *xp1, int *yp1, int *xp2, int *yp2)
1522{
1523 t_drawnumber *x = (t_drawnumber *)z;
1524 t_atom at;
1525 int xloc = glist_xtopixels(glist,
1526 basex + fielddesc_getfloat(&x->x_xloc, template, data, 0));
1527 int yloc = glist_ytopixels(glist,
1528 basey + fielddesc_getfloat(&x->x_yloc, template, data, 0));
1529 int font = glist_getfont(glist);
1530 int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font);
1531 char buf[DRAWNUMBER_BUFSIZE];
1532 if (x->x_flags & DRAW_SYMBOL)
1533 SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
1534 else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
1535 drawnumber_sprintf(x, buf, &at);
1536 *xp1 = xloc;
1537 *yp1 = yloc;
1538 *xp2 = xloc + fontwidth * strlen(buf);
1539 *yp2 = yloc + fontheight;
1540}
1541
1542static void drawnumber_displace(t_gobj *z, t_glist *glist,
1543 t_word *data, t_template *template, float basex, float basey,
1544 int dx, int dy)
1545{
1546 /* refuse */
1547}
1548
1549static void drawnumber_select(t_gobj *z, t_glist *glist,
1550 t_word *data, t_template *template, float basex, float basey,
1551 int state)
1552{
1553 post("drawnumber_select %d", state);
1554 /* fill in later */
1555}
1556
1557static void drawnumber_activate(t_gobj *z, t_glist *glist,
1558 t_word *data, t_template *template, float basex, float basey,
1559 int state)
1560{
1561 post("drawnumber_activate %d", state);
1562}
1563
1564static void drawnumber_vis(t_gobj *z, t_glist *glist,
1565 t_word *data, t_template *template, float basex, float basey,
1566 int vis)
1567{
1568 t_drawnumber *x = (t_drawnumber *)z;
1569
1570 if (vis)
1571 {
1572 t_atom at;
1573 int xloc = glist_xtopixels(glist,
1574 basex + fielddesc_getfloat(&x->x_xloc, template, data, 0));
1575 int yloc = glist_ytopixels(glist,
1576 basey + fielddesc_getfloat(&x->x_yloc, template, data, 0));
1577 char colorstring[20], buf[DRAWNUMBER_BUFSIZE];
1578 numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1),
1579 colorstring);
1580 if (x->x_flags & DRAW_SYMBOL)
1581 SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
1582 else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
1583 drawnumber_sprintf(x, buf, &at);
1584 sys_vgui(".x%x.c create text %d %d -anchor nw -fill %s -text {%s}",
1585 glist_getcanvas(glist), xloc, yloc, colorstring, buf);
1586 sys_vgui(" -font -*-courier-bold--normal--%d-*",
1587 sys_hostfontsize(glist_getfont(glist)));
1588 sys_vgui(" -tags drawnumber%x\n", data);
1589 }
1590 else sys_vgui(".x%x.c delete drawnumber%x\n", glist_getcanvas(glist), data);
1591}
1592
1593static float drawnumber_motion_ycumulative;
1594static t_glist *drawnumber_motion_glist;
1595static t_gobj *drawnumber_motion_gobj;
1596static t_word *drawnumber_motion_wp;
1597static t_template *drawnumber_motion_template;
1598
1599 /* LATER protect against the template changing or the scalar disappearing
1600 probably by attaching a gpointer here ... */
1601
1602static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy)
1603{
1604 t_drawnumber *x = (t_drawnumber *)z;
1605 t_fielddesc *f = &x->x_value;
1606 drawnumber_motion_ycumulative -= dy;
1607 template_setfloat(drawnumber_motion_template,
1608 f->fd_un.fd_varsym,
1609 drawnumber_motion_wp,
1610 drawnumber_motion_ycumulative,
1611 1);
1612 glist_redrawitem(drawnumber_motion_glist, drawnumber_motion_gobj);
1613}
1614
1615static int drawnumber_click(t_gobj *z, t_glist *glist,
1616 t_scalar *sc, t_template *template, float basex, float basey,
1617 int xpix, int ypix, int shift, int alt, int dbl, int doit)
1618{
1619 t_drawnumber *x = (t_drawnumber *)z;
1620 int x1, y1, x2, y2;
1621 t_word *data = sc->sc_vec;
1622 drawnumber_getrect(z, glist,
1623 sc->sc_vec, template, basex, basey,
1624 &x1, &y1, &x2, &y2);
1625 if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2
1626 && x->x_value.fd_var)
1627 {
1628 if (doit)
1629 {
1630 drawnumber_motion_glist = glist;
1631 drawnumber_motion_gobj = &sc->sc_gobj;
1632 drawnumber_motion_wp = data;
1633 drawnumber_motion_template = template;
1634 drawnumber_motion_ycumulative =
1635 fielddesc_getfloat(&x->x_value, template, data, 0);
1636 glist_grab(glist, z, drawnumber_motion, 0, xpix, ypix);
1637 }
1638 return (1);
1639 }
1640 else return (0);
1641}
1642
1643t_parentwidgetbehavior drawnumber_widgetbehavior =
1644{
1645 drawnumber_getrect,
1646 drawnumber_displace,
1647 drawnumber_select,
1648 drawnumber_activate,
1649 drawnumber_vis,
1650 drawnumber_click,
1651};
1652
1653static void drawnumber_free(t_drawnumber *x)
1654{
1655}
1656
1657static void drawnumber_setup(void)
1658{
1659 drawnumber_class = class_new(gensym("drawnumber"),
1660 (t_newmethod)drawnumber_new, (t_method)drawnumber_free,
1661 sizeof(t_drawnumber), CLASS_NOINLET, A_GIMME, 0);
1662 class_setdrawcommand(drawnumber_class);
1663 class_addcreator((t_newmethod)drawnumber_new, gensym("drawsymbol"),
1664 A_GIMME, 0);
1665 class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior);
1666}
1667
1668/* ---------------------- setup function ---------------------------- */
1669
1670void g_template_setup(void)
1671{
1672 template_setup();
1673 gtemplate_setup();
1674 template_float.t_pdobj = template_class;
1675 curve_setup();
1676 plot_setup();
1677 drawnumber_setup();
1678}
1679
1680/* Copyright (c) 1997-1999 Miller Puckette.
1681* For information on usage and redistribution, and for a DISCLAIMER OF ALL
1682* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
1683
1684#include <stdlib.h>
1685#include <string.h>
1686#include <stdio.h>
1687
1688#include "m_pd.h"
1689#include "s_stuff.h" /* for sys_hostfontsize */
1690#include "g_canvas.h"
1691
1692/*
1693This file contains text objects you would put in a canvas to define a
1694template. Templates describe objects of type "array" (g_array.c) and
1695"scalar" (g_scalar.c).
1696*/
1697
1698/* T.Grill - changed the _template.t_pd member to t_pdobj to avoid name clashes
1699with the t_pd type */
1700
1701 /* the structure of a "struct" object (also the obsolete "gtemplate"
1702 you get when using the name "template" in a box.) */
1703
1704struct _gtemplate
1705{
1706 t_object x_obj;
1707 t_template *x_template;
1708 t_canvas *x_owner;
1709 t_symbol *x_sym;
1710 struct _gtemplate *x_next;
1711 int x_argc;
1712 t_atom *x_argv;
1713};
1714
1715/* ---------------- forward definitions ---------------- */
1716
1717static void template_conformarray(t_template *tfrom, t_template *tto,
1718 int *conformaction, t_array *a);
1719static void template_conformglist(t_template *tfrom, t_template *tto,
1720 t_glist *glist, int *conformaction);
1721
1722/* ---------------------- storage ------------------------- */
1723
1724static t_class *gtemplate_class;
1725static t_class *template_class;
1726
1727/* there's a pre-defined "float" template. LATER should we bind this
1728to a symbol such as "pd-float"??? */
1729
1730static t_dataslot template_float_vec =
1731{
1732 DT_FLOAT,
1733 &s_y,
1734 &s_
1735};
1736
1737static t_template template_float =
1738{
1739 0, /* class -- fill in in setup routine */
1740 0, /* list of "struct"/t_gtemplate objects */
1741 &s_float, /* name */
1742 1, /* number of items */
1743 &template_float_vec
1744};
1745
1746 /* return true if two dataslot definitions match */
1747static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2,
1748 int nametoo)
1749{
1750 return ((!nametoo || ds1->ds_name == ds2->ds_name) &&
1751 ds1->ds_type == ds2->ds_type &&
1752 (ds1->ds_type != DT_ARRAY ||
1753 ds1->ds_arraytemplate == ds2->ds_arraytemplate));
1754}
1755
1756/* -- templates, the active ingredient in gtemplates defined below. ------- */
1757
1758t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv)
1759{
1760 t_template *x = (t_template *)pd_new(template_class);
1761 x->t_n = 0;
1762 x->t_vec = (t_dataslot *)t_getbytes(0);
1763 while (argc > 0)
1764 {
1765 int newtype, oldn, newn;
1766 t_symbol *newname, *newarraytemplate = &s_, *newtypesym;
1767 if (argc < 2 || argv[0].a_type != A_SYMBOL ||
1768 argv[1].a_type != A_SYMBOL)
1769 goto bad;
1770 newtypesym = argv[0].a_w.w_symbol;
1771 newname = argv[1].a_w.w_symbol;
1772 if (newtypesym == &s_float)
1773 newtype = DT_FLOAT;
1774 else if (newtypesym == &s_symbol)
1775 newtype = DT_SYMBOL;
1776 else if (newtypesym == &s_list)
1777 newtype = DT_LIST;
1778 else if (newtypesym == gensym("array"))
1779 {
1780 if (argc < 3 || argv[2].a_type != A_SYMBOL)
1781 {
1782 pd_error(x, "array lacks element template or name");
1783 goto bad;
1784 }
1785 newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol);
1786 newtype = DT_ARRAY;
1787 argc--;
1788 argv++;
1789 }
1790 else
1791 {
1792 pd_error(x, "%s: no such type", newtypesym->s_name);
1793 return (0);
1794 }
1795 newn = (oldn = x->t_n) + 1;
1796 x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec,
1797 oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec));
1798 x->t_n = newn;
1799 x->t_vec[oldn].ds_type = newtype;
1800 x->t_vec[oldn].ds_name = newname;
1801 x->t_vec[oldn].ds_arraytemplate = newarraytemplate;
1802 bad:
1803 argc -= 2; argv += 2;
1804 }
1805 if (templatesym->s_name)
1806 {
1807 x->t_sym = templatesym;
1808 pd_bind(&x->t_pdobj, x->t_sym);
1809 }
1810 else x->t_sym = templatesym;
1811 return (x);
1812}
1813
1814int template_size(t_template *x)
1815{
1816 return (x->t_n * sizeof(t_word));
1817}
1818
1819int template_find_field(t_template *x, t_symbol *name, int *p_onset,
1820 int *p_type, t_symbol **p_arraytype)
1821{
1822 t_template *t;
1823 int i, n;
1824 if (!x)
1825 {
1826 bug("template_find_field");
1827 return (0);
1828 }
1829 n = x->t_n;
1830 for (i = 0; i < n; i++)
1831 if (x->t_vec[i].ds_name == name)
1832 {
1833 *p_onset = i * sizeof(t_word);
1834 *p_type = x->t_vec[i].ds_type;
1835 *p_arraytype = x->t_vec[i].ds_arraytemplate;
1836 return (1);
1837 }
1838 return (0);
1839}
1840
1841t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp,
1842 int loud)
1843{
1844 int onset, type;
1845 t_symbol *arraytype;
1846 t_sample val = 0;
1847 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
1848 {
1849 if (type == DT_FLOAT)
1850 val = *(t_sample *)(((char *)wp) + onset);
1851 else if (loud) error("%s.%s: not a number",
1852 x->t_sym->s_name, fieldname->s_name);
1853 }
1854 else if (loud) error("%s.%s: no such field",
1855 x->t_sym->s_name, fieldname->s_name);
1856 return (fixtof(val));
1857}
1858
1859void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp,
1860 t_float f, int loud)
1861{
1862 int onset, type;
1863 t_symbol *arraytype;
1864 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
1865 {
1866 if (type == DT_FLOAT)
1867 *(t_sample *)(((char *)wp) + onset) = ftofix(f);
1868 else if (loud) error("%s.%s: not a number",
1869 x->t_sym->s_name, fieldname->s_name);
1870 }
1871 else if (loud) error("%s.%s: no such field",
1872 x->t_sym->s_name, fieldname->s_name);
1873}
1874
1875t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp,
1876 int loud)
1877{
1878 int onset, type;
1879 t_symbol *arraytype;
1880 t_symbol *val = &s_;
1881 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
1882 {
1883 if (type == DT_SYMBOL)
1884 val = *(t_symbol **)(((char *)wp) + onset);
1885 else if (loud) error("%s.%s: not a symbol",
1886 x->t_sym->s_name, fieldname->s_name);
1887 }
1888 else if (loud) error("%s.%s: no such field",
1889 x->t_sym->s_name, fieldname->s_name);
1890 return (val);
1891}
1892
1893void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp,
1894 t_symbol *s, int loud)
1895{
1896 int onset, type;
1897 t_symbol *arraytype;
1898 if (template_find_field(x, fieldname, &onset, &type, &arraytype))
1899 {
1900 if (type == DT_SYMBOL)
1901 *(t_symbol **)(((char *)wp) + onset) = s;
1902 else if (loud) error("%s.%s: not a symbol",
1903 x->t_sym->s_name, fieldname->s_name);
1904 }
1905 else if (loud) error("%s.%s: no such field",
1906 x->t_sym->s_name, fieldname->s_name);
1907}
1908
1909 /* stringent check to see if a "saved" template, x2, matches the current
1910 one (x1). It's OK if x1 has additional scalar elements but not (yet)
1911 arrays or lists. This is used for reading in "data files". */
1912int template_match(t_template *x1, t_template *x2)
1913{
1914 int i;
1915 if (x1->t_n < x2->t_n)
1916 return (0);
1917 for (i = x2->t_n; i < x1->t_n; i++)
1918 {
1919 if (x1->t_vec[i].ds_type == DT_ARRAY ||
1920 x1->t_vec[i].ds_type == DT_LIST)
1921 return (0);
1922 }
1923 if (x2->t_n > x1->t_n)
1924 post("add elements...");
1925 for (i = 0; i < x2->t_n; i++)
1926 if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1))
1927 return (0);
1928 return (1);
1929}
1930
1931/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */
1932
1933/* the following routines handle updating scalars to agree with changes
1934in their template. The old template is assumed to be the "installed" one
1935so we can delete old items; but making new ones we have to avoid scalar_new
1936which would make an old one whereas we will want a new one (but whose array
1937elements might still be old ones.
1938 LATER deal with graphics updates too... */
1939
1940 /* conform the word vector of a scalar to the new template */
1941static void template_conformwords(t_template *tfrom, t_template *tto,
1942 int *conformaction, t_word *wfrom, t_word *wto)
1943{
1944 int nfrom = tfrom->t_n, nto = tto->t_n, i;
1945 for (i = 0; i < nto; i++)
1946 {
1947 if (conformaction[i] >= 0)
1948 {
1949 /* we swap the two, in case it's an array or list, so that
1950 when "wfrom" is deleted the old one gets cleaned up. */
1951 t_word wwas = wto[i];
1952 wto[i] = wfrom[conformaction[i]];
1953 wfrom[conformaction[i]] = wwas;
1954 }
1955 }
1956}
1957
1958 /* conform a scalar, recursively conforming sublists and arrays */
1959static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto,
1960 int *conformaction, t_glist *glist, t_scalar *scfrom)
1961{
1962 t_scalar *x;
1963 t_gpointer gp;
1964 int nto = tto->t_n, nfrom = tfrom->t_n, i;
1965 t_template *scalartemplate;
1966 /* post("conform scalar"); */
1967 /* possibly replace the scalar */
1968 if (scfrom->sc_template == tfrom->t_sym)
1969 {
1970 /* see scalar_new() for comment about the gpointer. */
1971 gpointer_init(&gp);
1972 x = (t_scalar *)getbytes(sizeof(t_scalar) +
1973 (tto->t_n - 1) * sizeof(*x->sc_vec));
1974 x->sc_gobj.g_pd = scalar_class;
1975 x->sc_template = tfrom->t_sym;
1976 gpointer_setglist(&gp, glist, x);
1977 /* Here we initialize to the new template, but array and list
1978 elements will still belong to old template. */
1979 word_init(x->sc_vec, tto, &gp);
1980
1981 template_conformwords(tfrom, tto, conformaction,
1982 scfrom->sc_vec, x->sc_vec);
1983
1984 /* replace the old one with the new one in the list */
1985 if (glist->gl_list == &scfrom->sc_gobj)
1986 {
1987 glist->gl_list = &x->sc_gobj;
1988 x->sc_gobj.g_next = scfrom->sc_gobj.g_next;
1989 }
1990 else
1991 {
1992 t_gobj *y, *y2;
1993 for (y = glist->gl_list; y2 = y->g_next; y = y2)
1994 if (y2 == &scfrom->sc_gobj)
1995 {
1996 x->sc_gobj.g_next = y2->g_next;
1997 y->g_next = &x->sc_gobj;
1998 goto nobug;
1999 }
2000 bug("template_conformscalar");
2001 nobug: ;
2002 }
2003 /* burn the old one */
2004 pd_free(&scfrom->sc_gobj.g_pd);
2005 }
2006 else x = scfrom;
2007 scalartemplate = template_findbyname(x->sc_template);
2008 /* convert all array elements and sublists */
2009 for (i = 0; i < scalartemplate->t_n; i++)
2010 {
2011 t_dataslot *ds = scalartemplate->t_vec + i;
2012 if (ds->ds_type == DT_LIST)
2013 {
2014 t_glist *gl2 = x->sc_vec[i].w_list;
2015 template_conformglist(tfrom, tto, gl2, conformaction);
2016 }
2017 else if (ds->ds_type == DT_ARRAY)
2018 {
2019 template_conformarray(tfrom, tto, conformaction,
2020 x->sc_vec[i].w_array);
2021 }
2022 }
2023 return (x);
2024}
2025
2026 /* conform an array, recursively conforming sublists and arrays */
2027static void template_conformarray(t_template *tfrom, t_template *tto,
2028 int *conformaction, t_array *a)
2029{
2030 int i;
2031 if (a->a_templatesym == tfrom->t_sym)
2032 {
2033 /* the array elements must all be conformed */
2034 int oldelemsize = sizeof(t_word) * tfrom->t_n,
2035 newelemsize = sizeof(t_word) * tto->t_n;
2036 char *newarray = getbytes(sizeof(t_word) * tto->t_n * a->a_n);
2037 char *oldarray = a->a_vec;
2038 if (a->a_elemsize != oldelemsize)
2039 bug("template_conformarray");
2040 for (i = 0; i < a->a_n; i++)
2041 {
2042 t_word *wp = (t_word *)(newarray + newelemsize * i);
2043 word_init(wp, tto, &a->a_gp);
2044 template_conformwords(tfrom, tto, conformaction,
2045 (t_word *)(oldarray + oldelemsize * i), wp);
2046 }
2047 }
2048 bug("template_conformarray: this part not written");
2049 /* go through item by item conforming subarrays and sublists... */
2050}
2051
2052 /* this routine searches for every scalar in the glist that belongs
2053 to the "from" template and makes it belong to the "to" template. Descend
2054 glists recursively.
2055 We don't handle redrawing here; this is to be filled in LATER... */
2056
2057static void template_conformglist(t_template *tfrom, t_template *tto,
2058 t_glist *glist, int *conformaction)
2059{
2060 t_gobj *g;
2061 /* post("conform glist %s", glist->gl_name->s_name); */
2062 for (g = glist->gl_list; g; g = g->g_next)
2063 {
2064 if (pd_class(&g->g_pd) == scalar_class)
2065 g = &template_conformscalar(tfrom, tto, conformaction,
2066 glist, (t_scalar *)g)->sc_gobj;
2067 else if (pd_class(&g->g_pd) == canvas_class)
2068 template_conformglist(tfrom, tto, (t_glist *)g, conformaction);
2069 }
2070}
2071
2072 /* globally conform all scalars from one template to another */
2073void template_conform(t_template *tfrom, t_template *tto)
2074{
2075 int nto = tto->t_n, nfrom = tfrom->t_n, i, j,
2076 *conformaction = (int *)getbytes(sizeof(int) * nto),
2077 *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0;
2078 for (i = 0; i < nto; i++)
2079 conformaction[i] = -1;
2080 for (i = 0; i < nfrom; i++)
2081 conformedfrom[i] = 0;
2082 for (i = 0; i < nto; i++)
2083 {
2084 t_dataslot *dataslot = &tto->t_vec[i];
2085 for (j = 0; j < nfrom; j++)
2086 {
2087 t_dataslot *dataslot2 = &tfrom->t_vec[j];
2088 if (dataslot_matches(dataslot, dataslot2, 1))
2089 {
2090 conformaction[i] = j;
2091 conformedfrom[j] = 1;
2092 }
2093 }
2094 }
2095 for (i = 0; i < nto; i++)
2096 if (conformaction[i] < 0)
2097 {
2098 t_dataslot *dataslot = &tto->t_vec[i];
2099 for (j = 0; j < nfrom; j++)
2100 if (!conformedfrom[j] &&
2101 dataslot_matches(dataslot, &tfrom->t_vec[j], 1))
2102 {
2103 conformaction[i] = j;
2104 conformedfrom[j] = 1;
2105 }
2106 }
2107 if (nto != nfrom)
2108 doit = 1;
2109 else for (i = 0; i < nto; i++)
2110 if (conformaction[i] != i)
2111 doit = 1;
2112
2113 if (doit)
2114 {
2115 t_glist *gl;
2116 /* post("conforming template '%s' to new structure",
2117 tfrom->t_sym->s_name);
2118 for (i = 0; i < nto; i++)
2119 post("... %d", conformaction[i]); */
2120 for (gl = canvas_list; gl; gl = gl->gl_next)
2121 template_conformglist(tfrom, tto, gl, conformaction);
2122 }
2123 freebytes(conformaction, sizeof(int) * nto);
2124 freebytes(conformedfrom, sizeof(int) * nfrom);
2125}
2126
2127t_template *template_findbyname(t_symbol *s)
2128{
2129 int i;
2130 if (s == &s_float)
2131 return (&template_float);
2132 else return ((t_template *)pd_findbyclass(s, template_class));
2133}
2134
2135t_canvas *template_findcanvas(t_template *template)
2136{
2137 t_gtemplate *gt;
2138 if (!template)
2139 bug("template_findcanvas");
2140 if (!(gt = template->t_list))
2141 return (0);
2142 return (gt->x_owner);
2143 /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */
2144}
2145
2146 /* call this when reading a patch from a file to declare what templates
2147 we'll need. If there's already a template, check if it matches.
2148 If it doesn't it's still OK as long as there are no "struct" (gtemplate)
2149 objects hanging from it; we just conform everyone to the new template.
2150 If there are still struct objects belonging to the other template, we're
2151 in trouble. LATER we'll figure out how to conform the new patch's objects
2152 to the pre-existing struct. */
2153static void *template_usetemplate(void *dummy, t_symbol *s,
2154 int argc, t_atom *argv)
2155{
2156 t_template *x;
2157 t_symbol *templatesym =
2158 canvas_makebindsym(atom_getsymbolarg(0, argc, argv));
2159 if (!argc)
2160 return (0);
2161 argc--; argv++;
2162 /* check if there's already a template by this name. */
2163 if ((x = (t_template *)pd_findbyclass(templatesym, template_class)))
2164 {
2165 t_template *y = template_new(&s_, argc, argv);
2166 /* If the new template is the same as the old one,
2167 there's nothing to do. */
2168 if (!template_match(x, y))
2169 {
2170 /* Are there "struct" objects upholding this template? */
2171 if (x->t_list)
2172 {
2173 /* don't know what to do here! */
2174 error("%s: template mismatch",
2175 templatesym->s_name);
2176 }
2177 else
2178 {
2179 /* conform everyone to the new template */
2180 template_conform(x, y);
2181 pd_free(&x->t_pdobj);
2182 template_new(templatesym, argc, argv);
2183 }
2184 }
2185 pd_free(&y->t_pdobj);
2186 }
2187 /* otherwise, just make one. */
2188 else template_new(templatesym, argc, argv);
2189 return (0);
2190}
2191
2192 /* here we assume someone has already cleaned up all instances of this. */
2193void template_free(t_template *x)
2194{
2195 if (*x->t_sym->s_name)
2196 pd_unbind(&x->t_pdobj, x->t_sym);
2197 t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec));
2198}
2199
2200static void template_setup(void)
2201{
2202 template_class = class_new(gensym("template"), 0, (t_method)template_free,
2203 sizeof(t_template), CLASS_PD, 0);
2204 class_addmethod(pd_canvasmaker, (t_method)template_usetemplate,
2205 gensym("struct"), A_GIMME, 0);
2206
2207}
2208
2209/* ---------------- gtemplates. One per canvas. ----------- */
2210
2211/* this is a "text" object that searches for, and if necessary creates,
2212a "template" (above). Other objects in the canvas then can give drawing
2213instructions for the template. The template doesn't go away when the
2214gtemplate is deleted, so that you can replace it with
2215another one to add new fields, for example. */
2216
2217static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv)
2218{
2219 t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
2220 t_template *t = template_findbyname(sym);
2221 int i;
2222 t_symbol *sx = gensym("x");
2223 x->x_owner = canvas_getcurrent();
2224 x->x_next = 0;
2225 x->x_sym = sym;
2226 x->x_argc = argc;
2227 x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom));
2228 for (i = 0; i < argc; i++)
2229 x->x_argv[i] = argv[i];
2230
2231 /* already have a template by this name? */
2232 if (t)
2233 {
2234 x->x_template = t;
2235 /* if it's already got a "struct" or "gtemplate" object we
2236 just tack this one to the end of the list and leave it
2237 there. */
2238 if (t->t_list)
2239 {
2240 t_gtemplate *x2, *x3;
2241 for (x2 = x->x_template->t_list; x3 = x2->x_next; x2 = x3)
2242 ;
2243 x2->x_next = x;
2244 post("template %s: warning: already exists.", sym->s_name);
2245 }
2246 else
2247 {
2248 /* if there's none, we just replace the template with
2249 our own and conform it. */
2250 t_template *y = template_new(&s_, argc, argv);
2251 /* Unless the new template is different from the old one,
2252 there's nothing to do. */
2253 if (!template_match(t, y))
2254 {
2255 /* conform everyone to the new template */
2256 template_conform(t, y);
2257 pd_free(&t->t_pdobj);
2258 t = template_new(sym, argc, argv);
2259 }
2260 pd_free(&y->t_pdobj);
2261 t->t_list = x;
2262 }
2263 }
2264 else
2265 {
2266 /* otherwise make a new one and we're the only struct on it. */
2267 x->x_template = t = template_new(sym, argc, argv);
2268 t->t_list = x;
2269 }
2270 return (x);
2271}
2272
2273static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv)
2274{
2275 t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
2276 t_symbol *sym = atom_getsymbolarg(0, argc, argv);
2277 if (argc >= 1)
2278 argc--; argv++;
2279 return (gtemplate_donew(canvas_makebindsym(sym), argc, argv));
2280}
2281
2282 /* old version (0.34) -- delete 2003 or so */
2283static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv)
2284{
2285 t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class);
2286 t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name);
2287 static int warned;
2288 if (!warned)
2289 {
2290 post("warning -- 'template' (%s) is obsolete; replace with 'struct'",
2291 sym->s_name);
2292 warned = 1;
2293 }
2294 return (gtemplate_donew(sym, argc, argv));
2295}
2296
2297t_template *gtemplate_get(t_gtemplate *x)
2298{
2299 return (x->x_template);
2300}
2301
2302static void gtemplate_free(t_gtemplate *x)
2303{
2304 /* get off the template's list */
2305 t_template *t = x->x_template;
2306 if (x == t->t_list)
2307 {
2308 if (x->x_next)
2309 {
2310 /* if we were first on the list, and there are others on
2311 the list, make a new template corresponding to the new
2312 first-on-list and replace teh existing template with it. */
2313 t_template *z = template_new(&s_, x->x_argc, x->x_argv);
2314 template_conform(t, z);
2315 pd_free(&t->t_pdobj);
2316 pd_free(&z->t_pdobj);
2317 z = template_new(x->x_sym, x->x_argc, x->x_argv);
2318 z->t_list = x->x_next;
2319 }
2320 else t->t_list = 0;
2321 }
2322 else
2323 {
2324 t_gtemplate *x2, *x3;
2325 for (x2 = t->t_list; x3 = x2->x_next; x2 = x3)
2326 {
2327 if (x == x3)
2328 {
2329 x2->x_next = x3->x_next;
2330 break;
2331 }
2332 }
2333 }
2334 freebytes(x->x_argv, sizeof(t_atom) * x->x_argc);
2335}
2336
2337static void gtemplate_setup(void)
2338{
2339 gtemplate_class = class_new(gensym("struct"),
2340 (t_newmethod)gtemplate_new, (t_method)gtemplate_free,
2341 sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0);
2342 class_addcreator((t_newmethod)gtemplate_new_old, gensym("template"),
2343 A_GIMME, 0);
2344}
2345
2346/* --------------- FIELD DESCRIPTORS ---------------------- */
2347
2348/* a field descriptor can hold a constant or a variable; if a variable,
2349it's the name of a field in the template we belong to. LATER, we might
2350want to cache the offset of the field so we don't have to search for it
2351every single time we draw the object.
2352*/
2353
2354typedef struct _fielddesc
2355{
2356 char fd_type; /* LATER consider removing this? */
2357 char fd_var;
2358 union
2359 {
2360 t_float fd_float; /* the field is a constant float */
2361 t_symbol *fd_symbol; /* the field is a constant symbol */
2362 t_symbol *fd_varsym; /* the field is variable and this is the name */
2363 } fd_un;
2364} t_fielddesc;
2365
2366#define FIELDDESC_SETFLOAT(x, f) \
2367 ((x)->fd_type = A_FLOAT, (x)->fd_var = 0, (x)->fd_un.fd_float = (f))
2368#define FIELDDESC_SETSYMBOL(x, s) \
2369 ((x)->fd_type = A_SYMBOL, (x)->fd_var = 0, (x)->fd_un.fd_symbol = (s))
2370#define FIELDDESC_SETVAR(x, s, type) \
2371 ((x)->fd_type = type, (x)->fd_var = 1, (x)->fd_un.fd_varsym = (s))
2372
2373#define CLOSED 1
2374#define BEZ 2
2375#define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */
2376
2377static void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv)
2378{
2379 if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0);
2380 else if (argv->a_type == A_SYMBOL)
2381 FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_FLOAT);
2382 else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float);
2383}
2384
2385static void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv)
2386{
2387 if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0);
2388 else if (argv->a_type == A_SYMBOL)
2389 FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_ARRAY);
2390 else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float);
2391}
2392
2393static t_float fielddesc_getfloat(t_fielddesc *f, t_template *template,
2394 t_word *wp, int loud)
2395{
2396 if (f->fd_type == A_FLOAT)
2397 {
2398 if (f->fd_var)
2399 return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud));
2400 else return (f->fd_un.fd_float);
2401 }
2402 else
2403 {
2404 if (loud)
2405 error("symbolic data field used as number");
2406 return (0);
2407 }
2408}
2409
2410static t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template,
2411 t_word *wp, int loud)
2412{
2413 if (f->fd_type == A_SYMBOL)
2414 {
2415 if (f->fd_var)
2416 return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud));
2417 else return (f->fd_un.fd_symbol);
2418 }
2419 else
2420 {
2421 if (loud)
2422 error("numeric data field used as symbol");
2423 return (&s_);
2424 }
2425}
2426
2427/* ---------------- curves and polygons (joined segments) ---------------- */
2428
2429/*
2430curves belong to templates and describe how the data in the template are to
2431be drawn. The coordinates of the curve (and other display features) can
2432be attached to fields in the template.
2433*/
2434
2435t_class *curve_class;
2436
2437typedef struct _curve
2438{
2439 t_object x_obj;
2440 int x_flags; /* CLOSED and/or BEZ */
2441 t_fielddesc x_fillcolor;
2442 t_fielddesc x_outlinecolor;
2443 t_fielddesc x_width;
2444 int x_npoints;
2445 t_fielddesc *x_vec;
2446} t_curve;
2447
2448static void *curve_new(t_symbol *classsym, t_int argc, t_atom *argv)
2449{
2450 t_curve *x = (t_curve *)pd_new(curve_class);
2451 char *classname = classsym->s_name;
2452 int flags = 0;
2453 int nxy, i;
2454 t_fielddesc *fd;
2455 if (classname[0] == 'f')
2456 {
2457 classname += 6;
2458 flags |= CLOSED;
2459 if (argc) fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++);
2460 else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
2461 }
2462 else classname += 4;
2463 if (classname[0] == 'c') flags |= BEZ;
2464 x->x_flags = flags;
2465 if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);
2466 else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
2467 if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);
2468 else FIELDDESC_SETFLOAT(&x->x_width, 1);
2469 if (argc < 0) argc = 0;
2470 nxy = (argc + (argc & 1));
2471 x->x_npoints = (nxy>>1);
2472 x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc));
2473 for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++)
2474 fielddesc_setfloatarg(fd, 1, argv);
2475 if (argc & 1) FIELDDESC_SETFLOAT(fd, 0);
2476
2477 return (x);
2478}
2479
2480/* -------------------- widget behavior for curve ------------ */
2481
2482static void curve_getrect(t_gobj *z, t_glist *glist,
2483 t_word *data, t_template *template, float basex, float basey,
2484 int *xp1, int *yp1, int *xp2, int *yp2)
2485{
2486 t_curve *x = (t_curve *)z;
2487 int i, n = x->x_npoints;
2488 t_fielddesc *f = x->x_vec;
2489 int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff;
2490 for (i = 0, f = x->x_vec; i < n; i++, f += 2)
2491 {
2492 int xloc = glist_xtopixels(glist,
2493 basex + fielddesc_getfloat(f, template, data, 0));
2494 int yloc = glist_ytopixels(glist,
2495 basey + fielddesc_getfloat(f+1, template, data, 0));
2496 if (xloc < x1) x1 = xloc;
2497 if (xloc > x2) x2 = xloc;
2498 if (yloc < y1) y1 = yloc;
2499 if (yloc > y2) y2 = yloc;
2500 }
2501 *xp1 = x1;
2502 *yp1 = y1;
2503 *xp2 = x2;
2504 *yp2 = y2;
2505}
2506
2507static void curve_displace(t_gobj *z, t_glist *glist,
2508 t_word *data, t_template *template, float basex, float basey,
2509 int dx, int dy)
2510{
2511 /* refuse */
2512}
2513
2514static void curve_select(t_gobj *z, t_glist *glist,
2515 t_word *data, t_template *template, float basex, float basey,
2516 int state)
2517{
2518 /* fill in later */
2519}
2520
2521static void curve_activate(t_gobj *z, t_glist *glist,
2522 t_word *data, t_template *template, float basex, float basey,
2523 int state)
2524{
2525 /* fill in later */
2526}
2527
2528static int rangecolor(int n) /* 0 to 9 in 5 steps */
2529{
2530 int n2 = n/2; /* 0 to 4 */
2531 int ret = (n2 << 6); /* 0 to 256 in 5 steps */
2532 if (ret > 255) ret = 255;
2533 return (ret);
2534}
2535
2536static void numbertocolor(int n, char *s)
2537{
2538 int red, blue, green;
2539 if (n < 0) n = 0;
2540 red = n / 100;
2541 blue = ((n / 10) % 10);
2542 green = n % 10;
2543 sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(red), rangecolor(blue),
2544 rangecolor(green));
2545}
2546
2547static void curve_vis(t_gobj *z, t_glist *glist,
2548 t_word *data, t_template *template, float basex, float basey,
2549 int vis)
2550{
2551 t_curve *x = (t_curve *)z;
2552 int i, n = x->x_npoints;
2553 t_fielddesc *f = x->x_vec;
2554
2555 if (vis)
2556 {
2557 if (n > 1)
2558 {
2559 int flags = x->x_flags, closed = (flags & CLOSED);
2560 float width = fielddesc_getfloat(&x->x_width, template, data, 1);
2561 char outline[20], fill[20];
2562 if (width < 1) width = 1;
2563 numbertocolor(
2564 fielddesc_getfloat(&x->x_outlinecolor, template, data, 1),
2565 outline);
2566 if (flags & CLOSED)
2567 {
2568 numbertocolor(
2569 fielddesc_getfloat(&x->x_fillcolor, template, data, 1),
2570 fill);
2571 sys_vgui(".x%x.c create polygon\\\n",
2572 glist_getcanvas(glist));
2573 }
2574 else sys_vgui(".x%x.c create line\\\n",
2575 glist_getcanvas(glist));
2576 for (i = 0, f = x->x_vec; i < n; i++, f += 2)
2577 {
2578 float xloc = glist_xtopixels(glist,
2579 basex + fielddesc_getfloat(f, template, data, 1));
2580 float yloc = glist_ytopixels(glist,
2581 basey + fielddesc_getfloat(f+1, template, data, 1));
2582 sys_vgui("%d %d\\\n", (int)xloc, (int)yloc);
2583 }
2584 sys_vgui("-width %f\\\n",
2585 fielddesc_getfloat(&x->x_width, template, data, 1));
2586 if (flags & CLOSED) sys_vgui("-fill %s -outline %s\\\n",
2587 fill, outline);
2588 else sys_vgui("-fill %s\\\n", outline);
2589 if (flags & BEZ) sys_vgui("-smooth 1\\\n");
2590 sys_vgui("-tags curve%x\n", data);
2591 }
2592 else post("warning: curves need at least two points to be graphed");
2593 }
2594 else
2595 {
2596 if (n > 1) sys_vgui(".x%x.c delete curve%x\n",
2597 glist_getcanvas(glist), data);
2598 }
2599}
2600
2601static int curve_motion_field;
2602static float curve_motion_xcumulative;
2603static float curve_motion_xbase;
2604static float curve_motion_xper;
2605static float curve_motion_ycumulative;
2606static float curve_motion_ybase;
2607static float curve_motion_yper;
2608static t_glist *curve_motion_glist;
2609static t_gobj *curve_motion_gobj;
2610static t_word *curve_motion_wp;
2611static t_template *curve_motion_template;
2612
2613 /* LATER protect against the template changing or the scalar disappearing
2614 probably by attaching a gpointer here ... */
2615
2616static void curve_motion(void *z, t_floatarg dx, t_floatarg dy)
2617{
2618 t_curve *x = (t_curve *)z;
2619 t_fielddesc *f = x->x_vec + curve_motion_field;
2620 curve_motion_xcumulative += dx;
2621 curve_motion_ycumulative += dy;
2622 if (f->fd_var)
2623 {
2624 template_setfloat(curve_motion_template,
2625 f->fd_un.fd_varsym,
2626 curve_motion_wp,
2627 curve_motion_xbase + curve_motion_xcumulative * curve_motion_xper,
2628 1);
2629 }
2630 if ((f+1)->fd_var)
2631 {
2632 template_setfloat(curve_motion_template,
2633 (f+1)->fd_un.fd_varsym,
2634 curve_motion_wp,
2635 curve_motion_ybase + curve_motion_ycumulative * curve_motion_yper,
2636 1);
2637 }
2638 glist_redrawitem(curve_motion_glist, curve_motion_gobj);
2639}
2640
2641static int curve_click(t_gobj *z, t_glist *glist,
2642 t_scalar *sc, t_template *template, float basex, float basey,
2643 int xpix, int ypix, int shift, int alt, int dbl, int doit)
2644{
2645 t_curve *x = (t_curve *)z;
2646 int i, n = x->x_npoints;
2647 int bestn = -1;
2648 int besterror = 0x7fffffff;
2649 t_fielddesc *f = x->x_vec;
2650 t_word *data = sc->sc_vec;
2651 for (i = 0, f = x->x_vec; i < n; i++, f += 2)
2652 {
2653 int xloc = glist_xtopixels(glist,
2654 basex + fielddesc_getfloat(f, template, data, 0));
2655 int yloc = glist_ytopixels(glist,
2656 basey + fielddesc_getfloat(f+1, template, data, 0));
2657 int xerr = xloc - xpix, yerr = yloc - ypix;
2658 if (!f->fd_var && !(f+1)->fd_var)
2659 continue;
2660 if (xerr < 0)
2661 xerr = -xerr;
2662 if (yerr < 0)
2663 yerr = -yerr;
2664 if (yerr > xerr)
2665 xerr = yerr;
2666 if (xerr < besterror)
2667 {
2668 besterror = xerr;
2669 bestn = i;
2670 curve_motion_xbase = fielddesc_getfloat(f, template, data, 0);
2671 curve_motion_ybase = fielddesc_getfloat(f+1, template, data, 0);
2672 }
2673 }
2674 if (besterror > 10)
2675 return (0);
2676 if (doit)
2677 {
2678 curve_motion_xper = glist_pixelstox(glist, 1)
2679 - glist_pixelstox(glist, 0);
2680 curve_motion_yper = glist_pixelstoy(glist, 1)
2681 - glist_pixelstoy(glist, 0);
2682 curve_motion_xcumulative = curve_motion_ycumulative = 0;
2683 curve_motion_glist = glist;
2684 curve_motion_gobj = &sc->sc_gobj;
2685 curve_motion_wp = data;
2686 curve_motion_field = 2*bestn;
2687 curve_motion_template = template;
2688 glist_grab(glist, z, curve_motion, 0, xpix, ypix);
2689 }
2690 return (1);
2691}
2692
2693t_parentwidgetbehavior curve_widgetbehavior =
2694{
2695 curve_getrect,
2696 curve_displace,
2697 curve_select,
2698 curve_activate,
2699 curve_vis,
2700 curve_click,
2701};
2702
2703static void curve_free(t_curve *x)
2704{
2705 t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec));
2706}
2707
2708static void curve_setup(void)
2709{
2710 curve_class = class_new(gensym("drawpolygon"), (t_newmethod)curve_new,
2711 (t_method)curve_free, sizeof(t_curve), CLASS_NOINLET, A_GIMME, 0);
2712 class_setdrawcommand(curve_class);
2713 class_addcreator((t_newmethod)curve_new, gensym("drawcurve"),
2714 A_GIMME, 0);
2715 class_addcreator((t_newmethod)curve_new, gensym("filledpolygon"),
2716 A_GIMME, 0);
2717 class_addcreator((t_newmethod)curve_new, gensym("filledcurve"),
2718 A_GIMME, 0);
2719 class_setparentwidget(curve_class, &curve_widgetbehavior);
2720}
2721
2722/* --------- plots for showing arrays --------------- */
2723
2724t_class *plot_class;
2725
2726typedef struct _plot
2727{
2728 t_object x_obj;
2729 int x_flags;
2730 t_fielddesc x_outlinecolor;
2731 t_fielddesc x_width;
2732 t_fielddesc x_xloc;
2733 t_fielddesc x_yloc;
2734 t_fielddesc x_xinc;
2735 t_fielddesc x_data;
2736} t_plot;
2737
2738static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv)
2739{
2740 t_plot *x = (t_plot *)pd_new(plot_class);
2741 int flags = 0;
2742 int nxy, i;
2743 t_fielddesc *fd;
2744 t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);
2745 if (!strcmp(firstarg->s_name, "curve"))
2746 {
2747 flags |= BEZ;
2748 argc--, argv++;
2749 }
2750 if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++);
2751 else FIELDDESC_SETFLOAT(&x->x_data, 1);
2752 if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++);
2753 else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0);
2754 if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++);
2755 else FIELDDESC_SETFLOAT(&x->x_width, 1);
2756 if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
2757 else FIELDDESC_SETFLOAT(&x->x_xloc, 1);
2758 if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
2759 else FIELDDESC_SETFLOAT(&x->x_yloc, 1);
2760 if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++);
2761 else FIELDDESC_SETFLOAT(&x->x_xinc, 1);
2762 x->x_flags = flags;
2763 return (x);
2764}
2765
2766/* -------------------- widget behavior for plot ------------ */
2767
2768
2769 /* get everything we'll need from the owner template of the array being
2770 plotted. Not used for garrays, but see below */
2771static int plot_readownertemplate(t_plot *x,
2772 t_word *data, t_template *ownertemplate,
2773 t_symbol **elemtemplatesymp, t_array **arrayp,
2774 float *linewidthp, float *xlocp, float *xincp, float *ylocp)
2775{
2776 int arrayonset, type;
2777 t_symbol *elemtemplatesym;
2778 t_array *array;
2779
2780 /* find the data and verify it's an array */
2781 if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var)
2782 {
2783 error("plot: needs an array field");
2784 return (-1);
2785 }
2786 if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym,
2787 &arrayonset, &type, &elemtemplatesym))
2788 {
2789 error("plot: %s: no such field", x->x_data.fd_un.fd_varsym->s_name);
2790 return (-1);
2791 }
2792 if (type != DT_ARRAY)
2793 {
2794 error("plot: %s: not an array", x->x_data.fd_un.fd_varsym->s_name);
2795 return (-1);
2796 }
2797 array = *(t_array **)(((char *)data) + arrayonset);
2798 *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1);
2799 *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1);
2800 *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1);
2801 *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1);
2802 *elemtemplatesymp = elemtemplatesym;
2803 *arrayp = array;
2804 return (0);
2805}
2806
2807 /* get everything else you could possibly need about a plot,
2808 either for plot's own purposes or for plotting a "garray" */
2809int array_getfields(t_symbol *elemtemplatesym,
2810 t_canvas **elemtemplatecanvasp,
2811 t_template **elemtemplatep, int *elemsizep,
2812 int *xonsetp, int *yonsetp, int *wonsetp)
2813{
2814 int arrayonset, elemsize, yonset, wonset, xonset, type;
2815 t_template *elemtemplate;
2816 t_symbol *dummy;
2817 t_canvas *elemtemplatecanvas = 0;
2818
2819 /* the "float" template is special in not having to have a canvas;
2820 template_findbyname is hardwired to return a predefined
2821 template. */
2822
2823 if (!(elemtemplate = template_findbyname(elemtemplatesym)))
2824 {
2825 error("plot: %s: no such template", elemtemplatesym->s_name);
2826 return (-1);
2827 }
2828 if (!((elemtemplatesym == &s_float) ||
2829 (elemtemplatecanvas = template_findcanvas(elemtemplate))))
2830 {
2831 error("plot: %s: no canvas for this template", elemtemplatesym->s_name);
2832 return (-1);
2833 }
2834 elemsize = elemtemplate->t_n * sizeof(t_word);
2835 if (!template_find_field(elemtemplate, gensym("y"), &yonset, &type, &dummy)
2836 || type != DT_FLOAT)
2837 yonset = -1;
2838 if (!template_find_field(elemtemplate, gensym("x"), &xonset, &type, &dummy)
2839 || type != DT_FLOAT)
2840 xonset = -1;
2841 if (!template_find_field(elemtemplate, gensym("w"), &wonset, &type, &dummy)
2842 || type != DT_FLOAT)
2843 wonset = -1;
2844
2845 /* fill in slots for return values */
2846 *elemtemplatecanvasp = elemtemplatecanvas;
2847 *elemtemplatep = elemtemplate;
2848 *elemsizep = elemsize;
2849 *xonsetp = xonset;
2850 *yonsetp = yonset;
2851 *wonsetp = wonset;
2852 return (0);
2853}
2854
2855static void plot_getrect(t_gobj *z, t_glist *glist,
2856 t_word *data, t_template *template, float basex, float basey,
2857 int *xp1, int *yp1, int *xp2, int *yp2)
2858{
2859 t_plot *x = (t_plot *)z;
2860 int elemsize, yonset, wonset, xonset;
2861 t_canvas *elemtemplatecanvas;
2862 t_template *elemtemplate;
2863 t_symbol *elemtemplatesym;
2864 float linewidth, xloc, xinc, yloc;
2865 t_array *array;
2866 float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff;
2867 int i;
2868 float xpix, ypix, wpix;
2869
2870 if (!plot_readownertemplate(x, data, template,
2871 &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) &&
2872 !array_getfields(elemtemplatesym, &elemtemplatecanvas,
2873 &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
2874 {
2875 for (i = 0; i < array->a_n; i++)
2876 {
2877 array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize,
2878 xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc,
2879 &xpix, &ypix, &wpix);
2880 if (xpix < x1)
2881 x1 = xpix;
2882 if (xpix > x2)
2883 x2 = xpix;
2884 if (ypix - wpix < y1)
2885 y1 = ypix - wpix;
2886 if (ypix + wpix > y2)
2887 y2 = ypix + wpix;
2888 }
2889 }
2890
2891 *xp1 = x1;
2892 *yp1 = y1;
2893 *xp2 = x2;
2894 *yp2 = y2;
2895}
2896
2897static void plot_displace(t_gobj *z, t_glist *glist,
2898 t_word *data, t_template *template, float basex, float basey,
2899 int dx, int dy)
2900{
2901 /* not yet */
2902}
2903
2904static void plot_select(t_gobj *z, t_glist *glist,
2905 t_word *data, t_template *template, float basex, float basey,
2906 int state)
2907{
2908 /* not yet */
2909}
2910
2911static void plot_activate(t_gobj *z, t_glist *glist,
2912 t_word *data, t_template *template, float basex, float basey,
2913 int state)
2914{
2915 /* not yet */
2916}
2917
2918static void plot_vis(t_gobj *z, t_glist *glist,
2919 t_word *data, t_template *template, float basex, float basey,
2920 int vis)
2921{
2922 t_plot *x = (t_plot *)z;
2923 int elemsize, yonset, wonset, xonset;
2924 t_canvas *elemtemplatecanvas;
2925 t_template *elemtemplate;
2926 t_symbol *elemtemplatesym;
2927 float linewidth, xloc, xinc, yloc;
2928 t_array *array;
2929 int nelem;
2930 char *elem;
2931 if (plot_readownertemplate(x, data, template,
2932 &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) ||
2933 array_getfields(elemtemplatesym, &elemtemplatecanvas,
2934 &elemtemplate, &elemsize, &xonset, &yonset, &wonset))
2935 return;
2936 nelem = array->a_n;
2937 elem = (char *)array->a_vec;
2938 if (vis)
2939 {
2940 char outline[20];
2941 int lastpixel = -1, ndrawn = 0;
2942 float xsum, yval = 0, wval = 0, xpix;
2943 int ixpix = 0, i;
2944
2945 /* draw the trace */
2946 numbertocolor(fielddesc_getfloat(&x->x_outlinecolor, template, data, 1),
2947 outline);
2948 if (wonset >= 0)
2949 {
2950 /* found "w" field which controls linewidth. The trace is
2951 a filled polygon with 2n points. */
2952 sys_vgui(".x%x.c create polygon \\\n",
2953 glist_getcanvas(glist));
2954
2955 for (i = 0, xsum = xloc; i < nelem; i++)
2956 {
2957 float usexloc;
2958 if (xonset >= 0)
2959 usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
2960 else usexloc = xsum, xsum += xinc;
2961 if (yonset >= 0)
2962 yval = *(float *)((elem + elemsize * i) + yonset);
2963 else yval = 0;
2964 wval = *(float *)((elem + elemsize * i) + wonset);
2965 xpix = glist_xtopixels(glist, basex + usexloc);
2966 ixpix = xpix + 0.5;
2967 if (xonset >= 0 || ixpix != lastpixel)
2968 {
2969 sys_vgui("%d %f \\\n", ixpix,
2970 glist_ytopixels(glist,
2971 basey + yloc + yval - wval));
2972 ndrawn++;
2973 }
2974 lastpixel = ixpix;
2975 if (ndrawn >= 1000) goto ouch;
2976 }
2977 lastpixel = -1;
2978 for (i = nelem-1; i >= 0; i--)
2979 {
2980 float usexloc;
2981 if (xonset >= 0)
2982 usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
2983 else xsum -= xinc, usexloc = xsum;
2984 if (yonset >= 0)
2985 yval = *(float *)((elem + elemsize * i) + yonset);
2986 else yval = 0;
2987 wval = *(float *)((elem + elemsize * i) + wonset);
2988 xpix = glist_xtopixels(glist, basex + usexloc);
2989 ixpix = xpix + 0.5;
2990 if (xonset >= 0 || ixpix != lastpixel)
2991 {
2992 sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist,
2993 basey + yloc + yval + wval));
2994 ndrawn++;
2995 }
2996 lastpixel = ixpix;
2997 if (ndrawn >= 1000) goto ouch;
2998 }
2999 /* TK will complain if there aren't at least 3 points. There
3000 should be at least two already. */
3001 if (ndrawn < 4)
3002 {
3003 sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist,
3004 basey + yloc + yval + wval));
3005 sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist,
3006 basey + yloc + yval - wval));
3007 }
3008 ouch:
3009 sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline);
3010 if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n");
3011
3012 sys_vgui("-tags plot%x\n", data);
3013 }
3014 else if (linewidth > 0)
3015 {
3016 /* no "w" field. If the linewidth is positive, draw a
3017 segmented line with the requested width; otherwise don't
3018 draw the trace at all. */
3019 sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist));
3020
3021 for (xsum = xloc, i = 0; i < nelem; i++)
3022 {
3023 float usexloc;
3024 if (xonset >= 0)
3025 usexloc = xloc + *(float *)((elem + elemsize * i) + xonset);
3026 else usexloc = xsum, xsum += xinc;
3027 if (yonset >= 0)
3028 yval = *(float *)((elem + elemsize * i) + yonset);
3029 else yval = 0;
3030 xpix = glist_xtopixels(glist, basex + usexloc);
3031 ixpix = xpix + 0.5;
3032 if (xonset >= 0 || ixpix != lastpixel)
3033 {
3034 sys_vgui("%d %f \\\n", ixpix,
3035 glist_ytopixels(glist, basey + yloc + yval));
3036 ndrawn++;
3037 }
3038 lastpixel = ixpix;
3039 if (ndrawn >= 1000) break;
3040 }
3041 /* TK will complain if there aren't at least 2 points... */
3042 if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n");
3043 else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix + 10,
3044 glist_ytopixels(glist, basey + yloc + yval));
3045
3046 sys_vgui("-width %f\\\n", linewidth);
3047 sys_vgui("-fill %s\\\n", outline);
3048 if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n");
3049
3050 sys_vgui("-tags plot%x\n", data);
3051 }
3052 /* We're done with the outline; now draw all the points.
3053 This code is inefficient since the template has to be
3054 searched for drawing instructions for every last point. */
3055
3056 for (xsum = xloc, i = 0; i < nelem; i++)
3057 {
3058 float usexloc, useyloc;
3059 t_gobj *y;
3060 if (xonset >= 0)
3061 usexloc = basex + xloc +
3062 *(float *)((elem + elemsize * i) + xonset);
3063 else usexloc = basex + xsum, xsum += xinc;
3064 if (yonset >= 0)
3065 yval = *(float *)((elem + elemsize * i) + yonset);
3066 else yval = 0;
3067 useyloc = basey + yloc + yval;
3068 for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
3069 {
3070 t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
3071 if (!wb) continue;
3072 (*wb->w_parentvisfn)(y, glist,
3073 (t_word *)(elem + elemsize * i),
3074 elemtemplate, usexloc, useyloc, vis);
3075 }
3076 }
3077 }
3078 else
3079 {
3080 /* un-draw the individual points */
3081 int i;
3082 for (i = 0; i < nelem; i++)
3083 {
3084 t_gobj *y;
3085 for (y = elemtemplatecanvas->gl_list; y; y = y->g_next)
3086 {
3087 t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
3088 if (!wb) continue;
3089 (*wb->w_parentvisfn)(y, glist,
3090 (t_word *)(elem + elemsize * i), elemtemplate,
3091 0, 0, 0);
3092 }
3093 }
3094 /* and then the trace */
3095 sys_vgui(".x%x.c delete plot%x\n",
3096 glist_getcanvas(glist), data);
3097 }
3098}
3099
3100
3101static int plot_click(t_gobj *z, t_glist *glist,
3102 t_scalar *sc, t_template *template, float basex, float basey,
3103 int xpix, int ypix, int shift, int alt, int dbl, int doit)
3104{
3105 t_plot *x = (t_plot *)z;
3106 t_symbol *elemtemplatesym;
3107 float linewidth, xloc, xinc, yloc;
3108 t_array *array;
3109 t_word *data = sc->sc_vec;
3110
3111 if (!plot_readownertemplate(x, data, template,
3112 &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc))
3113 {
3114 return (array_doclick(array, glist, &sc->sc_gobj,
3115 elemtemplatesym,
3116 linewidth, basex + xloc, xinc, basey + yloc,
3117 xpix, ypix, shift, alt, dbl, doit));
3118 }
3119 else return (0);
3120}
3121
3122t_parentwidgetbehavior plot_widgetbehavior =
3123{
3124 plot_getrect,
3125 plot_displace,
3126 plot_select,
3127 plot_activate,
3128 plot_vis,
3129 plot_click,
3130};
3131
3132static void plot_setup(void)
3133{
3134 plot_class = class_new(gensym("plot"), (t_newmethod)plot_new, 0,
3135 sizeof(t_plot), CLASS_NOINLET, A_GIMME, 0);
3136 class_setdrawcommand(plot_class);
3137 class_setparentwidget(plot_class, &plot_widgetbehavior);
3138}
3139
3140/* ---------------- drawnumber: draw a number ---------------- */
3141
3142/*
3143 drawnumbers draw numeric fields at controllable locations, with
3144 controllable color and label .
3145 invocation: (drawnumber|drawsymbol) variable x y color label
3146*/
3147
3148t_class *drawnumber_class;
3149
3150#define DRAW_SYMBOL 1
3151
3152typedef struct _drawnumber
3153{
3154 t_object x_obj;
3155 t_fielddesc x_value;
3156 t_fielddesc x_xloc;
3157 t_fielddesc x_yloc;
3158 t_fielddesc x_color;
3159 t_symbol *x_label;
3160 int x_flags;
3161} t_drawnumber;
3162
3163static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv)
3164{
3165 t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class);
3166 char *classname = classsym->s_name;
3167 int flags = 0;
3168 if (classname[4] == 's')
3169 flags |= DRAW_SYMBOL;
3170 x->x_flags = flags;
3171 if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++);
3172 else FIELDDESC_SETFLOAT(&x->x_value, 0);
3173 if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++);
3174 else FIELDDESC_SETFLOAT(&x->x_xloc, 0);
3175 if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++);
3176 else FIELDDESC_SETFLOAT(&x->x_yloc, 0);
3177 if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++);
3178 else FIELDDESC_SETFLOAT(&x->x_color, 1);
3179 if (argc)
3180 x->x_label = atom_getsymbolarg(0, argc, argv);
3181 else x->x_label = &s_;
3182
3183 return (x);
3184}
3185
3186/* -------------------- widget behavior for drawnumber ------------ */
3187
3188#define DRAWNUMBER_BUFSIZE 80
3189static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap)
3190{
3191 int nchars;
3192 strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE);
3193 buf[DRAWNUMBER_BUFSIZE - 1] = 0;
3194 nchars = strlen(buf);
3195 atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars);
3196}
3197
3198static void drawnumber_getrect(t_gobj *z, t_glist *glist,
3199 t_word *data, t_template *template, float basex, float basey,
3200 int *xp1, int *yp1, int *xp2, int *yp2)
3201{
3202 t_drawnumber *x = (t_drawnumber *)z;
3203 t_atom at;
3204 int xloc = glist_xtopixels(glist,
3205 basex + fielddesc_getfloat(&x->x_xloc, template, data, 0));
3206 int yloc = glist_ytopixels(glist,
3207 basey + fielddesc_getfloat(&x->x_yloc, template, data, 0));
3208 int font = glist_getfont(glist);
3209 int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font);
3210 char buf[DRAWNUMBER_BUFSIZE];
3211 if (x->x_flags & DRAW_SYMBOL)
3212 SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
3213 else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
3214 drawnumber_sprintf(x, buf, &at);
3215 *xp1 = xloc;
3216 *yp1 = yloc;
3217 *xp2 = xloc + fontwidth * strlen(buf);
3218 *yp2 = yloc + fontheight;
3219}
3220
3221static void drawnumber_displace(t_gobj *z, t_glist *glist,
3222 t_word *data, t_template *template, float basex, float basey,
3223 int dx, int dy)
3224{
3225 /* refuse */
3226}
3227
3228static void drawnumber_select(t_gobj *z, t_glist *glist,
3229 t_word *data, t_template *template, float basex, float basey,
3230 int state)
3231{
3232 post("drawnumber_select %d", state);
3233 /* fill in later */
3234}
3235
3236static void drawnumber_activate(t_gobj *z, t_glist *glist,
3237 t_word *data, t_template *template, float basex, float basey,
3238 int state)
3239{
3240 post("drawnumber_activate %d", state);
3241}
3242
3243static void drawnumber_vis(t_gobj *z, t_glist *glist,
3244 t_word *data, t_template *template, float basex, float basey,
3245 int vis)
3246{
3247 t_drawnumber *x = (t_drawnumber *)z;
3248
3249 if (vis)
3250 {
3251 t_atom at;
3252 int xloc = glist_xtopixels(glist,
3253 basex + fielddesc_getfloat(&x->x_xloc, template, data, 0));
3254 int yloc = glist_ytopixels(glist,
3255 basey + fielddesc_getfloat(&x->x_yloc, template, data, 0));
3256 char colorstring[20], buf[DRAWNUMBER_BUFSIZE];
3257 numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1),
3258 colorstring);
3259 if (x->x_flags & DRAW_SYMBOL)
3260 SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0));
3261 else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0));
3262 drawnumber_sprintf(x, buf, &at);
3263 sys_vgui(".x%x.c create text %d %d -anchor nw -fill %s -text {%s}",
3264 glist_getcanvas(glist), xloc, yloc, colorstring, buf);
3265 sys_vgui(" -font -*-courier-bold--normal--%d-*",
3266 sys_hostfontsize(glist_getfont(glist)));
3267 sys_vgui(" -tags drawnumber%x\n", data);
3268 }
3269 else sys_vgui(".x%x.c delete drawnumber%x\n", glist_getcanvas(glist), data);
3270}
3271
3272static float drawnumber_motion_ycumulative;
3273static t_glist *drawnumber_motion_glist;
3274static t_gobj *drawnumber_motion_gobj;
3275static t_word *drawnumber_motion_wp;
3276static t_template *drawnumber_motion_template;
3277
3278 /* LATER protect against the template changing or the scalar disappearing
3279 probably by attaching a gpointer here ... */
3280
3281static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy)
3282{
3283 t_drawnumber *x = (t_drawnumber *)z;
3284 t_fielddesc *f = &x->x_value;
3285 drawnumber_motion_ycumulative -= dy;
3286 template_setfloat(drawnumber_motion_template,
3287 f->fd_un.fd_varsym,
3288 drawnumber_motion_wp,
3289 drawnumber_motion_ycumulative,
3290 1);
3291 glist_redrawitem(drawnumber_motion_glist, drawnumber_motion_gobj);
3292}
3293
3294static int drawnumber_click(t_gobj *z, t_glist *glist,
3295 t_scalar *sc, t_template *template, float basex, float basey,
3296 int xpix, int ypix, int shift, int alt, int dbl, int doit)
3297{
3298 t_drawnumber *x = (t_drawnumber *)z;
3299 int x1, y1, x2, y2;
3300 t_word *data = sc->sc_vec;
3301 drawnumber_getrect(z, glist,
3302 sc->sc_vec, template, basex, basey,
3303 &x1, &y1, &x2, &y2);
3304 if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2
3305 && x->x_value.fd_var)
3306 {
3307 if (doit)
3308 {
3309 drawnumber_motion_glist = glist;
3310 drawnumber_motion_gobj = &sc->sc_gobj;
3311 drawnumber_motion_wp = data;
3312 drawnumber_motion_template = template;
3313 drawnumber_motion_ycumulative =
3314 fielddesc_getfloat(&x->x_value, template, data, 0);
3315 glist_grab(glist, z, drawnumber_motion, 0, xpix, ypix);
3316 }
3317 return (1);
3318 }
3319 else return (0);
3320}
3321
3322t_parentwidgetbehavior drawnumber_widgetbehavior =
3323{
3324 drawnumber_getrect,
3325 drawnumber_displace,
3326 drawnumber_select,
3327 drawnumber_activate,
3328 drawnumber_vis,
3329 drawnumber_click,
3330};
3331
3332static void drawnumber_free(t_drawnumber *x)
3333{
3334}
3335
3336static void drawnumber_setup(void)
3337{
3338 drawnumber_class = class_new(gensym("drawnumber"),
3339 (t_newmethod)drawnumber_new, (t_method)drawnumber_free,
3340 sizeof(t_drawnumber), CLASS_NOINLET, A_GIMME, 0);
3341 class_setdrawcommand(drawnumber_class);
3342 class_addcreator((t_newmethod)drawnumber_new, gensym("drawsymbol"),
3343 A_GIMME, 0);
3344 class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior);
3345}
3346
3347/* ---------------------- setup function ---------------------------- */
3348
3349void g_template_setup(void)
3350{
3351 template_setup();
3352 gtemplate_setup();
3353 template_float.t_pdobj = template_class;
3354 curve_setup();
3355 plot_setup();
3356 drawnumber_setup();
3357}
3358