diff options
author | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
---|---|---|
committer | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
commit | 513389b4c1bc8afe4b2dc9947c534bfeb105e3da (patch) | |
tree | 10e673b35651ac567fed2eda0c679c7ade64cbc6 /apps/plugins/pdbox/PDa/src/g_template.c | |
parent | 95fa7f6a2ef466444fbe3fe87efc6d5db6b77b36 (diff) | |
download | rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.tar.gz rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.zip |
Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin project of Wincent Balin. Stripped some non-sourcefiles and added a rockbox readme that needs a bit more info from Wincent. Is added to CATEGORIES and viewers, but not yet to SUBDIRS (ie doesn't build yet)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21044 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_template.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/src/g_template.c | 3358 |
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 | /* | ||
14 | This file contains text objects you would put in a canvas to define a | ||
15 | template. 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 | ||
20 | with 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 | |||
25 | struct _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 | |||
38 | static void template_conformarray(t_template *tfrom, t_template *tto, | ||
39 | int *conformaction, t_array *a); | ||
40 | static void template_conformglist(t_template *tfrom, t_template *tto, | ||
41 | t_glist *glist, int *conformaction); | ||
42 | |||
43 | /* ---------------------- storage ------------------------- */ | ||
44 | |||
45 | static t_class *gtemplate_class; | ||
46 | static t_class *template_class; | ||
47 | |||
48 | /* there's a pre-defined "float" template. LATER should we bind this | ||
49 | to a symbol such as "pd-float"??? */ | ||
50 | |||
51 | static t_dataslot template_float_vec = | ||
52 | { | ||
53 | DT_FLOAT, | ||
54 | &s_y, | ||
55 | &s_ | ||
56 | }; | ||
57 | |||
58 | static 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 */ | ||
68 | static 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 | |||
79 | t_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 | |||
135 | int template_size(t_template *x) | ||
136 | { | ||
137 | return (x->t_n * sizeof(t_word)); | ||
138 | } | ||
139 | |||
140 | int 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 | |||
162 | t_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 | |||
180 | void 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 | |||
196 | t_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 | |||
214 | void 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". */ | ||
233 | int 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 | ||
255 | in their template. The old template is assumed to be the "installed" one | ||
256 | so we can delete old items; but making new ones we have to avoid scalar_new | ||
257 | which would make an old one whereas we will want a new one (but whose array | ||
258 | elements 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 */ | ||
262 | static 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 */ | ||
280 | static 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 */ | ||
348 | static 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 | |||
378 | static 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 */ | ||
394 | void 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 | |||
448 | t_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 | |||
456 | t_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. */ | ||
474 | static 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. */ | ||
514 | void 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 | |||
521 | static 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, | ||
533 | a "template" (above). Other objects in the canvas then can give drawing | ||
534 | instructions for the template. The template doesn't go away when the | ||
535 | gtemplate is deleted, so that you can replace it with | ||
536 | another one to add new fields, for example. */ | ||
537 | |||
538 | static 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 | |||
594 | static 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 */ | ||
604 | static 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 | |||
618 | t_template *gtemplate_get(t_gtemplate *x) | ||
619 | { | ||
620 | return (x->x_template); | ||
621 | } | ||
622 | |||
623 | static 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 | |||
658 | static 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, | ||
670 | it's the name of a field in the template we belong to. LATER, we might | ||
671 | want to cache the offset of the field so we don't have to search for it | ||
672 | every single time we draw the object. | ||
673 | */ | ||
674 | |||
675 | typedef 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 | |||
698 | static 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 | |||
706 | static 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 | |||
714 | static 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 | |||
731 | static 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 | /* | ||
751 | curves belong to templates and describe how the data in the template are to | ||
752 | be drawn. The coordinates of the curve (and other display features) can | ||
753 | be attached to fields in the template. | ||
754 | */ | ||
755 | |||
756 | t_class *curve_class; | ||
757 | |||
758 | typedef 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 | |||
769 | static 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 | |||
803 | static 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 | |||
828 | static 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 | |||
835 | static 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 | |||
842 | static 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 | |||
849 | static 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 | |||
857 | static 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 | |||
868 | static 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 | |||
922 | static int curve_motion_field; | ||
923 | static float curve_motion_xcumulative; | ||
924 | static float curve_motion_xbase; | ||
925 | static float curve_motion_xper; | ||
926 | static float curve_motion_ycumulative; | ||
927 | static float curve_motion_ybase; | ||
928 | static float curve_motion_yper; | ||
929 | static t_glist *curve_motion_glist; | ||
930 | static t_gobj *curve_motion_gobj; | ||
931 | static t_word *curve_motion_wp; | ||
932 | static 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 | |||
937 | static 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 | |||
962 | static 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 | |||
1014 | t_parentwidgetbehavior curve_widgetbehavior = | ||
1015 | { | ||
1016 | curve_getrect, | ||
1017 | curve_displace, | ||
1018 | curve_select, | ||
1019 | curve_activate, | ||
1020 | curve_vis, | ||
1021 | curve_click, | ||
1022 | }; | ||
1023 | |||
1024 | static void curve_free(t_curve *x) | ||
1025 | { | ||
1026 | t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec)); | ||
1027 | } | ||
1028 | |||
1029 | static 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 | |||
1045 | t_class *plot_class; | ||
1046 | |||
1047 | typedef 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 | |||
1059 | static 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 */ | ||
1092 | static 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" */ | ||
1130 | int 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 | |||
1176 | static 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 | |||
1218 | static 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 | |||
1225 | static 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 | |||
1232 | static 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 | |||
1239 | static 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 | |||
1422 | static 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 | |||
1443 | t_parentwidgetbehavior plot_widgetbehavior = | ||
1444 | { | ||
1445 | plot_getrect, | ||
1446 | plot_displace, | ||
1447 | plot_select, | ||
1448 | plot_activate, | ||
1449 | plot_vis, | ||
1450 | plot_click, | ||
1451 | }; | ||
1452 | |||
1453 | static 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 | |||
1469 | t_class *drawnumber_class; | ||
1470 | |||
1471 | #define DRAW_SYMBOL 1 | ||
1472 | |||
1473 | typedef 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 | |||
1484 | static 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 | ||
1510 | static 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 | |||
1519 | static 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 | |||
1542 | static 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 | |||
1549 | static 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 | |||
1557 | static 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 | |||
1564 | static 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 | |||
1593 | static float drawnumber_motion_ycumulative; | ||
1594 | static t_glist *drawnumber_motion_glist; | ||
1595 | static t_gobj *drawnumber_motion_gobj; | ||
1596 | static t_word *drawnumber_motion_wp; | ||
1597 | static 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 | |||
1602 | static 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 | |||
1615 | static 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 | |||
1643 | t_parentwidgetbehavior drawnumber_widgetbehavior = | ||
1644 | { | ||
1645 | drawnumber_getrect, | ||
1646 | drawnumber_displace, | ||
1647 | drawnumber_select, | ||
1648 | drawnumber_activate, | ||
1649 | drawnumber_vis, | ||
1650 | drawnumber_click, | ||
1651 | }; | ||
1652 | |||
1653 | static void drawnumber_free(t_drawnumber *x) | ||
1654 | { | ||
1655 | } | ||
1656 | |||
1657 | static 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 | |||
1670 | void 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 | /* | ||
1693 | This file contains text objects you would put in a canvas to define a | ||
1694 | template. 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 | ||
1699 | with 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 | |||
1704 | struct _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 | |||
1717 | static void template_conformarray(t_template *tfrom, t_template *tto, | ||
1718 | int *conformaction, t_array *a); | ||
1719 | static void template_conformglist(t_template *tfrom, t_template *tto, | ||
1720 | t_glist *glist, int *conformaction); | ||
1721 | |||
1722 | /* ---------------------- storage ------------------------- */ | ||
1723 | |||
1724 | static t_class *gtemplate_class; | ||
1725 | static t_class *template_class; | ||
1726 | |||
1727 | /* there's a pre-defined "float" template. LATER should we bind this | ||
1728 | to a symbol such as "pd-float"??? */ | ||
1729 | |||
1730 | static t_dataslot template_float_vec = | ||
1731 | { | ||
1732 | DT_FLOAT, | ||
1733 | &s_y, | ||
1734 | &s_ | ||
1735 | }; | ||
1736 | |||
1737 | static 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 */ | ||
1747 | static 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 | |||
1758 | t_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 | |||
1814 | int template_size(t_template *x) | ||
1815 | { | ||
1816 | return (x->t_n * sizeof(t_word)); | ||
1817 | } | ||
1818 | |||
1819 | int 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 | |||
1841 | t_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 | |||
1859 | void 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 | |||
1875 | t_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 | |||
1893 | void 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". */ | ||
1912 | int 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 | ||
1934 | in their template. The old template is assumed to be the "installed" one | ||
1935 | so we can delete old items; but making new ones we have to avoid scalar_new | ||
1936 | which would make an old one whereas we will want a new one (but whose array | ||
1937 | elements 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 */ | ||
1941 | static 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 */ | ||
1959 | static 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 */ | ||
2027 | static 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 | |||
2057 | static 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 */ | ||
2073 | void 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 | |||
2127 | t_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 | |||
2135 | t_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. */ | ||
2153 | static 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. */ | ||
2193 | void 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 | |||
2200 | static 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, | ||
2212 | a "template" (above). Other objects in the canvas then can give drawing | ||
2213 | instructions for the template. The template doesn't go away when the | ||
2214 | gtemplate is deleted, so that you can replace it with | ||
2215 | another one to add new fields, for example. */ | ||
2216 | |||
2217 | static 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 | |||
2273 | static 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 */ | ||
2283 | static 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 | |||
2297 | t_template *gtemplate_get(t_gtemplate *x) | ||
2298 | { | ||
2299 | return (x->x_template); | ||
2300 | } | ||
2301 | |||
2302 | static 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 | |||
2337 | static 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, | ||
2349 | it's the name of a field in the template we belong to. LATER, we might | ||
2350 | want to cache the offset of the field so we don't have to search for it | ||
2351 | every single time we draw the object. | ||
2352 | */ | ||
2353 | |||
2354 | typedef 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 | |||
2377 | static 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 | |||
2385 | static 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 | |||
2393 | static 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 | |||
2410 | static 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 | /* | ||
2430 | curves belong to templates and describe how the data in the template are to | ||
2431 | be drawn. The coordinates of the curve (and other display features) can | ||
2432 | be attached to fields in the template. | ||
2433 | */ | ||
2434 | |||
2435 | t_class *curve_class; | ||
2436 | |||
2437 | typedef 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 | |||
2448 | static 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 | |||
2482 | static 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 | |||
2507 | static 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 | |||
2514 | static 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 | |||
2521 | static 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 | |||
2528 | static 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 | |||
2536 | static 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 | |||
2547 | static 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 | |||
2601 | static int curve_motion_field; | ||
2602 | static float curve_motion_xcumulative; | ||
2603 | static float curve_motion_xbase; | ||
2604 | static float curve_motion_xper; | ||
2605 | static float curve_motion_ycumulative; | ||
2606 | static float curve_motion_ybase; | ||
2607 | static float curve_motion_yper; | ||
2608 | static t_glist *curve_motion_glist; | ||
2609 | static t_gobj *curve_motion_gobj; | ||
2610 | static t_word *curve_motion_wp; | ||
2611 | static 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 | |||
2616 | static 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 | |||
2641 | static 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 | |||
2693 | t_parentwidgetbehavior curve_widgetbehavior = | ||
2694 | { | ||
2695 | curve_getrect, | ||
2696 | curve_displace, | ||
2697 | curve_select, | ||
2698 | curve_activate, | ||
2699 | curve_vis, | ||
2700 | curve_click, | ||
2701 | }; | ||
2702 | |||
2703 | static void curve_free(t_curve *x) | ||
2704 | { | ||
2705 | t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec)); | ||
2706 | } | ||
2707 | |||
2708 | static 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 | |||
2724 | t_class *plot_class; | ||
2725 | |||
2726 | typedef 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 | |||
2738 | static 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 */ | ||
2771 | static 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" */ | ||
2809 | int 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 | |||
2855 | static 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 | |||
2897 | static 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 | |||
2904 | static 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 | |||
2911 | static 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 | |||
2918 | static 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 | |||
3101 | static 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 | |||
3122 | t_parentwidgetbehavior plot_widgetbehavior = | ||
3123 | { | ||
3124 | plot_getrect, | ||
3125 | plot_displace, | ||
3126 | plot_select, | ||
3127 | plot_activate, | ||
3128 | plot_vis, | ||
3129 | plot_click, | ||
3130 | }; | ||
3131 | |||
3132 | static 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 | |||
3148 | t_class *drawnumber_class; | ||
3149 | |||
3150 | #define DRAW_SYMBOL 1 | ||
3151 | |||
3152 | typedef 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 | |||
3163 | static 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 | ||
3189 | static 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 | |||
3198 | static 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 | |||
3221 | static 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 | |||
3228 | static 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 | |||
3236 | static 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 | |||
3243 | static 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 | |||
3272 | static float drawnumber_motion_ycumulative; | ||
3273 | static t_glist *drawnumber_motion_glist; | ||
3274 | static t_gobj *drawnumber_motion_gobj; | ||
3275 | static t_word *drawnumber_motion_wp; | ||
3276 | static 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 | |||
3281 | static 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 | |||
3294 | static 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 | |||
3322 | t_parentwidgetbehavior drawnumber_widgetbehavior = | ||
3323 | { | ||
3324 | drawnumber_getrect, | ||
3325 | drawnumber_displace, | ||
3326 | drawnumber_select, | ||
3327 | drawnumber_activate, | ||
3328 | drawnumber_vis, | ||
3329 | drawnumber_click, | ||
3330 | }; | ||
3331 | |||
3332 | static void drawnumber_free(t_drawnumber *x) | ||
3333 | { | ||
3334 | } | ||
3335 | |||
3336 | static 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 | |||
3349 | void 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 | |||