1 /* Copyright (C) 1992-1998 The Geometry Center
2 * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3 *
4 * This file is part of Geomview.
5 *
6 * Geomview is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * Geomview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with Geomview; see the file COPYING. If not, write
18 * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
19 * USA, or visit http://www.gnu.org.
20 */
21
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #if 0
27 static char copyright[] = "Copyright (C) 1992-1998 The Geometry Center\n\
28 Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips";
29 #endif
30
31
32 /* Authors: Stuart Levy, Tamara Munzner, Mark Phillips,
33 * Celeste Fowler */
34
35 #include "mg.h"
36 #include "mgbuf.h"
37 #include "drawer.h"
38 #include "ui.h"
39 #include "geom.h"
40 #include "geomclass.h"
41 #include "instP.h"
42 #include "listP.h"
43 #include "streampool.h"
44 #include "comm.h"
45 #include "lang.h"
46 #include "main.h"
47 #include "transobj.h"
48 #include "ntransobj.h"
49 #include "worldio.h"
50 #include <string.h>
51 #include <stdlib.h>
52 #ifdef NeXT
53 #include <bsd/libc.h>
54 #else /* any other reasonable UNIX */
55 #include <unistd.h>
56 #endif
57
58
59 void set_emodule_path(char **dirs);
60 void set_load_path(char **dirs);
61
62 static void save_dgeom(Pool *p, DGeom *dg, int doaliens);
63 static void save_dgeom_geom(Pool *p, DGeom *dg, bool world, bool wrap, int cum);
64 static void save_dview(Pool *p, DView *dv);
65 static int nonidentity(Transform T);
66 static void maybe_save_xform(Pool *p, char *cmd, char *name, Handle *, Transform T);
67 static char * normalization_string(int n);
68 static char * onoff_string(int n);
69
70 int
save_world(Pool * p,int id,bool comm,int wrap,int to_coords)71 save_world(Pool *p, int id, bool comm, int wrap, int to_coords)
72 {
73 Appearance *ap;
74 int i, closeme = 0;
75 FILE *fp;
76 Transform T;
77 DGeom *dg = NULL;
78 Handle *h = NULL;
79
80 if ((fp = PoolOutputFile(p)) == NULL) {
81 return 0;
82 }
83
84 #if 0
85 /* Hack -- don't let StreamOut routines write Handle names. */
86 /* "Temporary" workaround for library reading bug. -slevy */
87 PoolSetOType(p, PO_DATA);
88 #endif
89
90 if (id == NOID) {
91 /* cH: hack, but otherwise we cannot save the universe, no aliens. */
92 id = UNIVERSE;
93 }
94
95 if (!comm) {
96 /* Write geometry */
97
98 if (wrap) {
99 if (id == UNIVERSE || ISCAM(id)) {
100 PoolFPrint(p, fp, "{ # Base appearance\n");
101 PoolIncLevel(p, 1);
102 ApStreamOut(p, NULL, drawerstate.ap);
103 PoolFPrint(p, fp, "# end base appearance\n");
104 if (ISCAM(id)) {
105 PoolIncLevel(p, -1);
106 PoolFPrint(p, fp, "}\n");
107 return !ferror(fp);
108 } else closeme = 1;
109 }
110 }
111 if (id == UNIVERSE) {
112 id = WORLDGEOM;
113 }
114 if (ISGEOM(id))
115 dg = (DGeom *)drawer_get_object(id);
116 if (id == WORLDGEOM) {
117 if (wrap) {
118 save_dgeom_geom(p, dg, true, wrap, to_coords);
119 }
120 PoolFPrint(p, fp, "{ LIST # World list \n");
121 PoolIncLevel(p, 1);
122 for(i = 1; i < dgeom_max; i++) {
123 dg = (DGeom *)drawer_get_object(GEOMID(i));
124 if (dg != NULL && dg->citizenship != ALIEN)
125 save_dgeom_geom(p, dg, false, true, WORLDGEOM);
126 }
127 PoolIncLevel(p, -1);
128 PoolFPrint(p, fp, "} #end of World List\n");
129 if (wrap) {
130 PoolIncLevel(p, -1);
131 PoolFPrint(p, fp, "} # end of World and INST\n");
132 if (closeme) {
133 PoolIncLevel(p, -1);
134 PoolFPrint(p, fp, "}\n");
135 }
136 }
137 } else {
138 save_dgeom_geom(p, dg, false, wrap, to_coords);
139 }
140 return !ferror(fp);
141 }
142
143 /* Write command language */
144
145
146 PoolFPrint(p, fp, "(progn\n");
147 PoolIncLevel(p, 1); /* Ensure embedded objects have { braces } */
148 if (id == UNIVERSE || id == WORLDGEOM) {
149 PoolFPrint(p, fp, "(merge-baseap ");
150 ApStreamOut(p, NULL, drawerstate.ap);
151 PoolFPrint(p, fp, ") # end base appearance\n");
152 if (drawerstate.NDim > 0)
153 PoolFPrint(p, fp, "(dimension %d)\n", drawerstate.NDim-1);
154 }
155
156 if (id != UNIVERSE && id != WORLDGEOM) {
157 DObject *dobj = drawer_get_object(id);
158 if (dobj) {
159 if (ISGEOM(dobj->id))
160 save_dgeom(p, (DGeom *)dobj, 1);
161 else
162 save_dview(p, (DView *)dobj);
163 }
164 } else {
165
166 /* save data associated with the World dgeom */
167 GeomGet( dgeom[0]->Item, CR_AXIS, T );
168 GeomGet( dgeom[0]->Item, CR_AXISHANDLE, &h );
169 maybe_save_xform(p, "xform-set", "worldgeom", h, T);
170 maybe_save_xform(p, "xform-incr", "worldgeom",
171 dgeom[0]->incrhandle, dgeom[0]->Incr);
172
173 GeomGet( dgeom[0]->Item, CR_APPEAR, &ap );
174 if (ap != NULL) {
175 PoolFPrint(p, fp, "(merge-ap \"worldgeom\" ");
176 ApStreamOut( p, NULL, ap );
177 PoolFPrint(p, fp, ") # end appearance \"worldgeom\"\n");
178 }
179
180 /* save the dgeoms. Include aliens if id == UNIVERSE. */
181 for (i=1; i<dgeom_max; ++i)
182 if (dgeom[i] != NULL) save_dgeom(p, dgeom[i], id == UNIVERSE);
183
184 /* save the dviews */
185 for (i=0; i<dview_max; ++i)
186 if (dview[i] != NULL) save_dview( p, dview[i] );
187
188 /* save other stuff here ... */
189
190 dg = (DGeom *)drawer_get_object(uistate.targetid);
191 if (dg) {
192 PoolFPrint(p, fp, "(ui-target \"%s\")\n", dg->name[1]);
193 }
194 }
195 PoolIncLevel(p, -1);
196 PoolFPrint(p, fp, ")\n"); /* terminates the (progn... */
197 return !ferror(fp);
198 }
199
200
201 /*
202 * Save a DGeom as geometry. Emit appearance and transform if wrap.
203 * if not 'world', also add geometry and close the braces.
204 */
205 static void
save_dgeom_geom(Pool * p,DGeom * dg,bool world,bool wrap,int to_coords)206 save_dgeom_geom(Pool *p, DGeom *dg, bool world, bool wrap, int to_coords)
207 {
208 FILE *fp = PoolOutputFile(p);
209 Transform T;
210 Handle *h = NULL;
211 Geom *g = NULL;
212 Appearance *ap = NULL;
213
214 if (dg == NULL || dg->Item == NULL)
215 return;
216
217 if (wrap) {
218
219 PoolFPrint(p, fp, "{ # %s\n", dg->name[1]);
220 PoolIncLevel(p, 1);
221 PoolFPrint(p, fp, "INST\n");
222
223 drawer_get_transform(dg->id, T, to_coords);
224 if (nonidentity(T) || h) { /* Could say "transform" here */
225 PoolFPrint(p, fp, "");
226 TransStreamOut(p, h, T); /* but TransStreamOut does that */
227 }
228 PoolFPrint(p, fp, "geom ");
229 GeomGet(dg->Item, CR_APPEAR, &ap);
230 if (ap) {
231 ap = ApCopy(ap, NULL);
232 ap->override = 0;
233 if (ap->mat) ap->mat->override = 0;
234 if (ap->lighting) ap->lighting->override = 0;
235 PoolFPrint(p, fp, "");
236 ApStreamOut(p, NULL, ap);
237 ApDelete(ap);
238 }
239 }
240 if (!world) {
241 h = NULL;
242 GeomGet(dg->Lgeom, CR_GEOM, &g);
243 GeomGet(dg->Lgeom, CR_GEOMHANDLE, &h);
244 GeomStreamOut(p, h, g);
245 if (wrap) {
246 PoolIncLevel(p, -1);
247 PoolFPrint(p, fp, "} # end (geom and INST) %s\n", dg->name[1]);
248 }
249 }
250 }
251
252 static void
maybe_save_xform(Pool * p,char * cmd,char * name,Handle * h,Transform T)253 maybe_save_xform(Pool *p, char *cmd, char *name, Handle *h, Transform T)
254 {
255 if (h || nonidentity(T)) {
256 fprintf(PoolOutputFile(p), "(%s \"%s\" ", cmd, name);
257 TransStreamOut(p, h, T);
258 fprintf(PoolOutputFile(p), ")\n");
259 }
260 }
261
262 static void
save_dgeom(Pool * p,DGeom * dg,int doaliens)263 save_dgeom(Pool *p, DGeom *dg, int doaliens)
264 {
265 Geom *g=NULL;
266 Appearance *ap=NULL;
267 FILE *fp = PoolOutputFile(p);
268 Handle *h = NULL;
269 Transform T;
270 char name[32];
271 char *truename = dg->name[1];
272
273 sprintf(name, "[%s]", dg->name[0]); /* Temporary name -- unlikely to conflict */
274
275 if (dg->citizenship == ALIEN) {
276 if (!doaliens)
277 return;
278 PoolFPrint(p, fp, "(new-alien");
279 } else {
280 PoolFPrint(p, fp, "(new-geometry");
281 }
282 fprintf(fp, " \"%s\" # %s\n", name, truename);
283 PoolIncLevel(p, 1);
284 PoolFPrint(p, fp, "");
285 GeomGet( dg->Lgeom, CR_GEOM, &g );
286 GeomGet( dg->Lgeom, CR_GEOMHANDLE, &h );
287 GeomStreamOut( p, h, g );
288 PoolIncLevel(p, -1);
289 PoolFPrint(p, fp, ") # end geometry \"%s\" %s\n", name, truename);
290
291 GeomGet( dg->Item, CR_APPEAR, &ap );
292 if (ap != NULL) {
293 PoolFPrint(p, fp, "(merge-ap \"%s\" ", name);
294 ApStreamOut( p, NULL, ap );
295 PoolFPrint(p, fp, ") # end appearance \"%s\" %s\n", name, truename);
296 }
297
298 TmIdentity(T);
299 GeomGet( dg->Item, CR_AXIS, T );
300 GeomGet( dg->Item, CR_AXISHANDLE, &h );
301 maybe_save_xform(p, "xform-set", name, h, T);
302
303 if (dg->NDT) {
304 PoolFPrint(p, fp, "(ND-xform-set \"%s\" ", name);
305 NTransStreamOut(p, NULL, dg->NDT);
306 PoolFPrint(p, fp, ")\n");
307 }
308
309 PoolFPrint(p, fp, "(bbox-draw \"%s\" %s)\n",
310 name, onoff_string(dg->bboxdraw));
311 if (dg->bboxap && dg->bboxap->mat) {
312 Color *c = &(dg->bboxap->mat->edgecolor);
313 PoolFPrint(p, fp, "(bbox-color \"%s\" %f %f %f)\n", name, c->r, c->g, c->b);
314 }
315
316 PoolFPrint(p, fp, "(normalization \"%s\" %s)\n", name,
317 normalization_string(dg->normalization));
318 PoolFPrint(p, fp, "(name-object \"%s\" \"%s\")\n\n", name, truename);
319 }
320
321 static void
save_dview(Pool * p,DView * dv)322 save_dview(Pool *p, DView *dv)
323 {
324 char *name = dv->name[1];
325 FILE *fp = PoolOutputFile(p);
326 WnPosition wp;
327
328 if (WnGet(dv->win, WN_CURPOS, &wp) > 0) {
329 WnSet(dv->win, WN_PREFPOS, &wp, WN_END);
330 PoolFPrint(p, fp, "(window default { position %d %d %d %d })\n",
331 wp.xmin,wp.xmax,wp.ymin,wp.ymax);
332 }
333
334 PoolFPrint(p, fp, "(camera \"%s\" ", name);
335 CamStreamOut(p, dv->camhandle, dv->cam);
336 PoolFPrint(p, fp, ") # camera \"%s\" ...\n", name);
337
338 PoolFPrint(p, fp, "(backcolor \"%s\" %f %f %f)\n",
339 name, dv->backcolor.r,dv->backcolor.g,dv->backcolor.b);
340 if (dv->cameradraw) {
341 PoolFPrint(p, fp, "(camera-draw \"%s\" yes)\n", name);
342 }
343
344 if (dv->cluster != NULL) {
345 cmap *cm;
346 cent *ce;
347 int i, j;
348 PoolFPrint(p, fp, "(dimension %d)\n", drawerstate.NDim-1);
349
350 PoolFPrint(p, fp, "(ND-axes \"%s\" \"%s\" %d %d %d %d)\n",
351 name, dv->cluster->name,
352 dv->NDPerm[0], dv->NDPerm[1], dv->NDPerm[2], dv->NDPerm[3]);
353
354 if (dv->cluster->C2W != NULL) {
355 PoolFPrint(p, fp, "(ND-xform-set \"%s\" ", name);
356 TmNPrint(fp, dv->cluster->C2W);
357 PoolFPrint(p, fp, ")\n");
358 }
359
360 PoolFPrint(p, fp, "(ND-color \"%s\" (\n", name);
361 for(i = 0, cm = dv->NDcmap; i < dv->nNDcmap; i++, cm++) {
362 PoolFPrint(p, fp, "\t( (");
363 fputnf(fp, cm->axis->dim, cm->axis->v, 0);
364 PoolFPrint(p, fp, ")\n");
365 for(j = VVCOUNT(cm->cents), ce = VVEC(cm->cents,cent); --j >= 0; ce++) {
366 PoolFPrint(p, fp, "\t\t%g\t", ce->v);
367 fputnf(fp, 4, (float *)&ce->c, 0);
368 fputc('\n', fp);
369 }
370 PoolFPrint(p, fp, "\t)\n");
371 }
372 PoolFPrint(p, fp, " ) ) # end ND-color \"%s\"\n", name);
373 }
374
375 PoolFPrint(p, fp, "(window \"%s\" ", name);
376 WnStreamOut(p, NULL, dv->win);
377 PoolFPrint(p, fp, ")\n");
378
379 PoolFPrint(p, fp, "# end camera %s\n\n", name);
380 }
381
nonidentity(Transform T)382 static int nonidentity(Transform T)
383 {
384 return memcmp(T, TM_IDENTITY, sizeof(TM_IDENTITY));
385 }
386
387 static char *
normalization_string(int n)388 normalization_string(int n)
389 {
390 switch (n) {
391 case NONE: return "none";
392 case ALL: return "all";
393 case EACH: return "each";
394 default: return "???";
395 }
396 }
397
398 static char *
onoff_string(int n)399 onoff_string(int n)
400 {
401 if (n) return "on";
402 else return "off";
403 }
404
405
worldio(HandleOps * ops,Pool * p,int to_coords,int id)406 int worldio(HandleOps *ops, Pool *p, int to_coords, int id)
407 {
408 int ok = -1;
409 Transform T;
410 TransformN *TN;
411 bool wrap = true;
412
413 /* no data attached to a handle has been saved yet */
414 HandlesSetObjSaved(false);
415
416 if (to_coords == SELF) {
417 wrap = false;
418 }
419 if (ops == &CommandOps) {
420 ok = save_world(p, id, true, wrap, to_coords);
421 } else if (ops == &GeomOps) {
422 if (ISTYPE(T_NONE, id)) {
423 /* bounding-box requested */
424 DGeom *dg;
425
426 id = GEOMID(INDEXOF(id));
427 if ((dg = (DGeom *)drawer_get_object(id)) != NULL && dg->bboxvalid) {
428 Geom *bbox;
429 GeomGet(dg->Lbbox, CR_GEOM, &bbox);
430 if (bbox != NULL) {
431 ok = GeomStreamOut(p, NULL, bbox);
432 }
433 }
434 } else {
435 ok = save_world(p, id, false, wrap, to_coords);
436 }
437 } else if (ops == &CamOps && ISCAM(id)) {
438 DView *dv = (DView *)drawer_get_object(id);
439 if (dv) ok = CamStreamOut(p, dv->camhandle, dv->cam);
440 } else if (ops == &WindowOps) {
441 DView *dv = (DView *)drawer_get_object(ISCAM(id) ? id : FOCUSID);
442 WnWindow *win;
443 if (dv && dv->mgctx) {
444 mgctxselect(dv->mgctx);
445 if ((ok = mgctxget(MG_WINDOW, &win)) > 0)
446 ok = WnStreamOut(p, NULL, win);
447 }
448 } else if (ops == &TransOps) {
449 drawer_get_transform(id, T, to_coords);
450 ok = TransStreamOut(p, NULL, T);
451 } else if (ops == &NTransOps) {
452 TN = drawer_get_ND_transform(id, to_coords);
453 ok = NTransStreamOut(p, NULL, TN);
454 TmNDelete(TN);
455 }
456 fflush(PoolOutputFile(p));
457
458 return ok;
459 }
460
461 static int
unique(char * new,char ** already,int nalready)462 unique(char *new, char **already, int nalready)
463 {
464 int i;
465 if (new == NULL)
466 return 0;
467 for(i = 0; i < nalready; i++)
468 if (strcmp(new, already[i]) == 0)
469 return 0;
470 return 1;
471 }
472
473 static void
setapath(LList * list,vvec * vvpath,void (* setfunc)(char **))474 setapath(LList *list, vvec *vvpath, void (*setfunc)(char **))
475 {
476 #define MAXDIRS 100
477 int i, k;
478 char *td, *dirs[MAXDIRS+1];
479 for(i = 0; i < MAXDIRS && list; list = list->cdr) {
480 if (list->car == NULL)
481 continue;
482 td = LSTRINGVAL(list->car);
483 if (strcmp(td, "+") == 0) {
484 for(k = 0; k < VVCOUNT(*vvpath) && i < MAXDIRS; k++) {
485 td = VVEC(*vvpath, char *)[k];
486 if (unique(td, dirs, i))
487 dirs[i++] = strdup(td);
488 }
489 } else {
490 if (unique(td, dirs, i))
491 dirs[i++] = td;
492 }
493 }
494 dirs[i] = NULL;
495 (*setfunc)(dirs);
496 #undef MAXDIRS
497 }
498
499 static void
set_vvpath(vvec * vv,char ** ents)500 set_vvpath(vvec *vv, char **ents)
501 {
502 int i;
503 for(i = VVCOUNT(*vv); --i >= 0; )
504 free(VVEC(*vv, char *)[i]);
505
506 VVCOUNT(*vv) = 0;
507 while(ents && *ents)
508 *VVAPPEND(*vv, char *) = strdup(*ents++);
509 vvtrim(vv);
510 }
511
512 LDEFINE(set_load_path, LVOID,
513 "(set-load-path (PATH1 ... PATHN))\n\
514 Sets search path for command, geometry, etc. files. The PATHi\n\
515 are strings giving the pathnames of directories to be searched.\n\
516 The special directory name \"+\" is replaced by the existing path,\n\
517 so e.g. (set-load-path (mydir +)) prepends mydir to the path.")
518 {
519 LList *list;
520 LDECLARE(("set-load-path", LBEGIN,
521 LLITERAL, LLIST, &list,
522 LEND));
523 setapath(list, &vv_load_path, set_load_path);
524 return Lt;
525 }
526
set_load_path(char ** dirs)527 void set_load_path(char **dirs)
528 {
529 int filepanel;
530
531 set_vvpath(&vv_load_path, dirs);
532
533 filedirs(dirs); /* Inform OOGL/refcomm library */
534
535 filepanel = ui_name2panel("Files"); /* Inform UI */
536 if (ui_panelshown(filepanel))
537 ui_showpanel(filepanel, 1);
538 }
539
540 LDEFINE(load_path, LLIST,
541 "(load-path)\n\
542 Returns the current search path for command, geometry, etc. files.\n\
543 Note: to actually see the value returned by this function\n\
544 you should wrap it in a call to echo: (echo (load-path)).\n\
545 See also set-load-path.")
546 {
547 /*
548 For backward compatibility this command takes an optional argument
549 which specifies a new load-path. This will disappear in a future version.
550 */
551 LList *list = NULL;
552 int i;
553 LDECLARE(("load-path", LBEGIN,
554 LOPTIONAL,
555 LLITERAL, LLIST, &list,
556 LEND));
557
558 if (list != NULL) {
559 gv_set_load_path(list);
560 return Lt;
561 }
562
563 for (i=0; i<load_path_count; ++i) {
564 char *dir = strdup(load_path[i]);
565 list = LListAppend(list, LNew(LSTRING, &dir));
566 }
567 return LNew(LLIST, &list);
568 }
569
570 LDEFINE(set_emodule_path, LVOID,
571 "(set-emodule-path (PATH1 ... PATHN))\n\
572 Sets the search path for external modules. The PATHi should\n\
573 be pathnames of directories containing, for each module, the\n\
574 module's executable file and a .geomview-<modulename> file\n\
575 which contains an (emodule-define ...) command for that\n\
576 module. This command implicitly calls (rehash-emodule-path)\n\
577 to rebuild the application brower from the new path setting.\n\
578 The special directory name \"+\" is replaced by the existing path,\n\
579 so e.g. (set-emodule-path (mydir +)) prepends mydir to the path.")
580 {
581 #define MAXDIRS 100
582 LList *list;
583 LDECLARE(("set-emodule-path", LBEGIN,
584 LLITERAL, LLIST, &list,
585 LEND));
586 setapath(list, &vv_emodule_path, set_emodule_path);
587 return Lt;
588 }
589
set_emodule_path(char ** dirs)590 void set_emodule_path(char **dirs)
591 {
592 set_vvpath(&vv_emodule_path, dirs);
593 gv_rehash_emodule_path();
594 }
595
596 LDEFINE(rehash_emodule_path, LLIST,
597 "(rehash-emodule-path)\n\
598 Rebuilds the application (external module) browser by reading\n\
599 all .geomview-* files in all directories on the emodule-path.\n\
600 Primarily intended for internal use; any applications defined\n\
601 by (emodule-define ...) commands outside of the .geomview-*\n\
602 files on the emodule-path will be lost. Does not sort the\n\
603 entries in the brower; see (emodule-sort) for that.")
604 {
605 int i;
606 char pat[512], **files, **fp;
607 LDECLARE(("rehash-emodule-path", LBEGIN,
608 LEND));
609
610 gv_emodule_clear();
611 for (i=0; i<emodule_path_count; ++i) {
612 sprintf(pat, "%s/.geomview-*", emodule_path[i]);
613 fp = files = ooglglob(pat);
614 while (fp && *fp) {
615 if (access(*fp, R_OK) == 0) {
616 char *emod_dir_saved = uistate.emod_dir;
617 uistate.emod_dir = emodule_path[i];
618 loadfile(*fp, &CommandOps, 0);
619 uistate.emod_dir = emod_dir_saved;
620 }
621 ++fp;
622 }
623 ooglblkfree(files);
624 OOGLFree(files);
625 }
626 return Lt;
627 }
628
629 LDEFINE(emodule_path, LLIST,
630 "(emodule-path)\n\
631 Returns the current search path for external modules.\n\
632 Note: to actually see the value returned by this function\n\
633 you should wrap it in a call to echo: (echo (emodule-path)).\n\
634 See also set-emodule-path.")
635 {
636 LList *list = NULL;
637 int i;
638 LDECLARE(("emodule-path", LBEGIN,
639 LEND));
640
641 for (i=0; i<emodule_path_count; ++i) {
642 char *dir = strdup(emodule_path[i]);
643 list = LListAppend(list, LNew(LSTRING, &dir));
644 }
645 return LNew(LLIST, &list);
646 }
647
648 LDEFINE(emodule_defined, LSTRING,
649 "(emodule-defined \"modulename\")\n\
650 If the given external-module name is known, returns the name of\n\
651 the program invoked when it's run as a quoted string; otherwise\n\
652 returns nil. ``(echo (emodule-defined \"name\"))'' prints the string.")
653 {
654 char *name;
655 emodule *em;
656 LDECLARE(("emodule-defined", LBEGIN,
657 LSTRING, &name,
658 LEND));
659 return ui_emodule_index(name, &em) < 0 ? Lnil : LTOOBJ(LSTRING)(&em->text);
660 }
661
662 LDEFINE(all, LLIST,
663 "(all geometry) returns a list of names of all geometry objects.\n\
664 (all camera) returns a list of names of all cameras.\n\
665 (all emodule defined) returns a list of all defined external modules.\n\
666 (all emodule running) returns a list of all running external modules.\n\
667 Use e.g. ``(echo (all geometry))'' to print such a list.")
668 {
669 char *type, *subtype = "";
670 int i, wantrunning;
671 DGeom *dg;
672 DView *dv;
673 emodule *em;
674 LList *l = NULL;
675
676 LDECLARE(("all", LBEGIN,
677 LSTRING, &type,
678 LOPTIONAL,
679 LSTRING, &subtype,
680 LEND));
681
682 if (strncmp(type, "geom", 4) == 0) {
683 LOOPGEOMS(i, dg)
684 l = LListAppend(l, LTOOBJ(LSTRING)(&dg->name[1]));
685 } else if (strncmp(type, "cam", 3) == 0) {
686 LOOPVIEWS(i, dv)
687 l = LListAppend(l, LTOOBJ(LSTRING)(&dv->name[1]));
688 } else if (strncmp(type, "emodule", 7) == 0) {
689 wantrunning = (strncmp(subtype, "run", 3) == 0);
690 em = VVEC(uistate.emod, emodule);
691 for(i = VVCOUNT(uistate.emod); --i >= 0; em++)
692 if ((em->pid != 0) == wantrunning)
693 l = LListAppend(l, LTOOBJ(LSTRING)(&em->name));
694 } else {
695 OOGLError(0, "all: expected \"geometry\" or \"camera\" or \"emodule defined\" or \"emodule running\", got: %s %s\n",
696 type, subtype);
697 return Lnil;
698 }
699 return LNew(LLIST, &l);
700 }
701
702 LDEFINE(camera_prop, LVOID,
703 "(camera-prop { geometry object } [projective])\n\
704 Specify the object to be shown when drawing other cameras.\n\
705 By default, this object is drawn with its origin at the camera,\n\
706 and with the camera looking toward the object's -Z axis.\n\
707 With the \"projective\" keyword, the camera's viewing projection is\n\
708 also applied to the object; this places the object's Z=-1 and Z=+1 at\n\
709 near and far clipping planes, with the viewing area -1<={X,Y}<=+1.\n\
710 Example: (camera-prop { < cube } projective)")
711 {
712 Keyword proj = NO_KEYWORD;
713 GeomStruct *gs = NULL;
714
715 LDECLARE(("camera-prop", LBEGIN,
716 LGEOM, &gs,
717 LOPTIONAL,
718 LKEYWORD, &proj,
719 LEND));
720
721 if (proj != NO_KEYWORD && proj != PROJECTIVE_KEYWORD) {
722 OOGLError(1, "Expected either \"%s\" or nothing, but got \"%s\".\n",
723 keywordname(PROJECTIVE_KEYWORD), keywordname(proj));
724 return Lnil;
725 }
726
727 if (gs && gs->geom) {
728 REFINCR(gs->geom);
729 REFINCR(gs->h);
730 GeomDelete(drawerstate.camgeom);
731 drawerstate.camgeom = gs->geom;
732 drawerstate.camproj = (proj == PROJECTIVE_KEYWORD);
733 drawerstate.changed = true;
734 return Lt;
735 }
736
737 return Lnil;
738 }
739
740 LDEFINE(write, LVOID,
741 "(write {command|geometry|camera|transform|ntransform|window|bbox} FILENAME [ID|(ID ...)] [self|world|universe|otherID])\n\
742 write description of ID in given format to FILENAME. Last\n\
743 parameter chooses coordinate system for geometry & transform:\n\
744 self: just the object, no transformation or appearance (geometry only)\n\
745 world: the object as positioned within the World.\n\
746 universe: object's position in universal coordinates;\n\
747 includes Worldtransform\n\
748 other ID: the object transformed to otherID's coordinate system.\n\
749 \n\
750 A filename of \"-\" is a special case: data are written to the\n\
751 stream from which the 'write' command was read. For external\n\
752 modules, the data are sent to the module's standard input.\n\
753 For commands not read from an external program, \"-\" means\n\
754 geomview's standard output. (See also the \"command\"\n\
755 command.)\n\
756 \n\
757 The ID can either be a single id or a parenthesized list of\n\
758 ids, like \"g0\" or \"(g2 g1 dodec.off)\".")
759 {
760 Pool *op, *p;
761 HandleOps *ops;
762 char *opsname;
763 LObject *idobj;
764 char *fname;
765 Lake *hiawatha;
766 int coords = UNIVERSE, val = 1, id;
767 bool temppool = false;
768 bool do_bbox = false;
769
770 LDECLARE(("write", LBEGIN,
771 LLAKE, &hiawatha,
772 LSTRING, &opsname,
773 LSTRING, &fname,
774 LHOLD, LLOBJECT, &idobj,
775 LOPTIONAL,
776 LID, &coords,
777 LEND));
778
779 p = POOL(hiawatha);
780
781 if (strcmp(opsname, "bbox") == 0) {
782 do_bbox = true;
783 ops = &GeomOps;
784 }
785 if (!do_bbox && (ops = str2ops(opsname)) == NULL) {
786 OOGLError(0, "write: expected one of command|geometry|camera|transform|ntransform|window, got \"%s\"", opsname);
787 return Lnil;
788 }
789
790 if (fname[0] == '-') {
791 if (PoolOutputFile(p)) {
792 op = p;
793 } else {
794 op = PoolStreamTemp(fname, NULL, stdout, 1, &CommandOps);
795 temppool = true;
796 }
797 } else {
798 op = PoolStreamTemp(fname, NULL, NULL, 1, &CommandOps);
799 temppool = true;
800 }
801
802 if (op == NULL || PoolOutputFile(op) == NULL) {
803 fprintf(stderr, "write: cannot open \"%s\": %s\n", fname, sperror());
804 return Lnil;
805 }
806
807 if (idobj->type == LSTRING || idobj->type == LSYMBOL) {
808 if (!LFROMOBJ(LID)(idobj, &id)) {
809 fprintf(stderr, "write: expects ID or list of IDs in arg position 4\n");
810 return Lnil;
811 }
812 if (do_bbox) {
813 id = ID(T_NONE, INDEXOF(id));
814 }
815 val = (worldio(ops, op, coords, id) == 1);
816 } else if (idobj->type == LLIST) {
817 LList *list;
818 for( list = LLISTVAL(idobj); list && list->car; list = list->cdr ) {
819 if (!LFROMOBJ(LID)(list->car, &id)) {
820 fprintf(stderr, "write: expects ID or list of IDs in arg position 2\n");
821 return Lnil;
822 }
823 if (do_bbox) {
824 id = ID(T_NONE, INDEXOF(id));
825 }
826 val &= (worldio(ops, op, coords, id) == 1);
827 }
828 }
829 if (temppool) {
830 PoolClose(op);
831 PoolDelete(op);
832 }
833 if (!val) {
834 fprintf(stderr, "write failed\n");
835 return Lnil;
836 } else
837 return Lt;
838 }
839
840
841 /***************************************************/
842 /* Snapshot code ***********************************/
843 /***************************************************/
844
845 extern snap_entry snapshot_table[];
846
847 int
general_snapshot(char * fname,int id,DView * view,WnWindow * win,WnPosition * wp,int (* function)(void))848 general_snapshot(char *fname, int id, DView *view,
849 WnWindow *win, WnPosition *wp,
850 int (*function)(void))
851 {
852 Camera *cam = NULL;
853 const Appearance *ap;
854 mgcontext *ctx;
855 FILE *f = NULL;
856 WnPosition vp;
857 int mgspace;
858 int failed;
859
860 f = (fname[0] == '|') ? popen(fname+1, "wb") : fopen(fname, "wb");
861 if (f == NULL)
862 return -1;
863
864 mgctxget(MG_CAMERA, &cam);
865 mgctxget(MG_SPACE, &mgspace);
866 #if 0
867 /* Copy so that changed flags are set */
868 ap = ApCopy(mggetappearance(NULL), NULL);
869 #else
870 ap = mggetappearance();
871 #endif
872 mgctxget(MG_WINDOW, &win);
873 win = WnCopy(win);
874 vp.xmin = vp.ymin = 0;
875 vp.xmax = wp->xmax - wp->xmin + 1;
876 vp.ymax = wp->ymax - wp->ymin + 1;
877 WnSet(win, WN_CURPOS, wp, WN_VIEWPORT, &vp, WN_END);
878
879 function();
880 ctx = mgctxcreate(MG_CAMERA, cam,
881 MG_APPEAR, ap,
882 MG_WINDOW, win,
883 MG_BACKGROUND, &view->backcolor,
884 MG_BUFFILE, f,
885 MG_SPACE, mgspace,
886 MG_END);
887 mgreshapeviewport(); /* Make camera aspect match window */
888 {
889 /* Try to be as realistic as possible when dumping a snapshot.
890 * Create a drawing context, install (hopefully) all the
891 * attributes that draw_view() doesn't reset on its own,
892 * plug the ctx into the camera, and force a redraw.
893 * Then undo the subterfuge.
894 */
895 bool oldredraw = view->redraw;
896 bool oldchanged = view->changed;
897 mgcontext *oldctx = view->mgctx;
898 view->mgctx = ctx;
899 view->redraw = true;
900 gv_draw(view->id);
901 view->redraw = oldredraw, view->changed = oldchanged;
902 view->mgctx = oldctx;
903 }
904 fflush(f);
905
906 mgctxdelete(ctx);
907 #if 0
908 ApDelete(ap);
909 #endif
910 mgctxselect(view->mgctx); /* Revert to previous device */
911
912 failed = (fname[0] != '|') ? fclose(f) : pclose(f);
913 return failed;
914 }
915
916
917 LDEFINE(snapshot, LINT,
918 "(snapshot CAM-ID FILENAME [FORMAT [XSIZE [YSIZE]]])\n"
919 "Save a snapshot of CAM-ID in the FILENAME (a string).")
920 {
921 DView *dv;
922 WnWindow *wn = NULL;
923 WnPosition wp;
924 int id, xsize = 0, ysize = 0;
925 char *fname, *format;
926 int i, failed = -1;
927
928 if (snapshot_table[0].name)
929 format = snapshot_table[0].name;
930 else
931 format = "ppm";
932 LDECLARE(("snapshot", LBEGIN,
933 LID, &id,
934 LSTRING, &fname,
935 LOPTIONAL, LSTRING, &format,
936 LINT, &xsize,
937 LINT, &ysize,
938 LEND));
939
940 if (!ISCAM(id) ||
941 (dv = (DView *)drawer_get_object(id)) == NULL
942 || dv->mgctx == NULL)
943 {
944 OOGLError(0, "snapshot: id %d: no such camera", id);
945 return LCopy(L1);
946 }
947 mgctxselect(dv->mgctx);
948 mgctxget(MG_WINDOW, &wn);
949 wp.xmin = wp.ymin = 0; wp.xmax = wp.ymax = 399; /* Avoid catastrophe */
950 WnGet(wn, WN_CURPOS, &wp);
951
952 if (xsize > 0 || ysize > 0) {
953 int dx = wp.xmax - wp.xmin + 1;
954 int dy = wp.ymax - wp.ymin + 1;
955 if (xsize <= 0)
956 xsize = (dx * ysize + dy/2) / dy;
957 else if (ysize <= 0)
958 ysize = (dy * xsize + dx/2) / dx;
959 wp.xmax = wp.xmin + xsize - 1;
960 wp.ymax = wp.ymin + ysize - 1;
961 }
962
963 for (i=0; snapshot_table[i].function!=NULL; i++)
964 if (!strcmp(format, snapshot_table[i].name))
965 {
966 failed = snapshot_table[i].function(fname, id, dv, wn, &wp);
967 break;
968 }
969 if (!strcmp(format, "ppm")) {
970 failed = general_snapshot(fname, id, dv, wn, &wp, mgdevice_BUF);
971 } else if (!strcmp(format, "ps")) {
972 failed = general_snapshot(fname, id, dv, wn, &wp, mgdevice_PS);
973 }
974 if (failed == -1)
975 OOGLError(0, "snapshot: unknown file format %s", format);
976 return failed ? Lnil : Lt;
977 }
978
979 /*
980 * Local Variables: ***
981 * mode: c ***
982 * c-basic-offset: 2 ***
983 * End: ***
984 */
985