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 /* Authors: Stuart Levy, Tamara Munzner, Mark Phillips */
32 
33 #include "mg.h"
34 #include "fsa.h"
35 #include "drawer.h"
36 #include "event.h"
37 #include "ui.h"
38 #include "comm.h"
39 #include "transform.h"
40 #include "transformn.h"
41 #include "lang.h"
42 #include "ooglutil.h"
43 #include "geom.h"
44 #include "camera.h"
45 #include "space.h"
46 #include "list.h"		/* for ListRemove(), ListAppend() */
47 #include "comment.h"
48 #include "transobj.h"
49 #include "ntransobj.h"
50 #include "lang.h"
51 #include "lispext.h"
52 #include "lights.h"
53 #include "cmodel.h"
54 #include "mouse.h"
55 #include <stdlib.h>
56 #include <string.h>
57 #include <sys/stat.h>
58 #include <math.h>
59 
60 /* Use sys/file.h since unistd.h doesn't exist on the NeXT, and
61    hopefully sys/file.h is sufficient on both NeXT and IRIS [ needed
62    for use of access() ]. */
63 /*#include <unistd.h>*/
64 #include <sys/file.h>
65 
66 static Fsa name_fsa = NULL;
67 
68 DrawerState drawerstate;
69 
70 static int drawer_dgeom(int id, GeomStruct *gs);
71 
72 static int spaceval(char *s, int val);
73 static HModelValue hmodelval(char *s, int val);
74 static int normalval(char *s, int val);
75 extern Appearance *GeomAppearance( Geom * );
76 extern mgshadefunc softshader(int id);
77 
78 TransformStruct ts_identity;
79 
80 #define	DGEOM_MAX	16
81 #define	DVIEW_MAX	8
82 
83 int dgeom_max = DGEOM_MAX;
84 int dview_max = DVIEW_MAX;
85 DGeom **dgeom = NULL;
86 DView **dview = NULL;
87 
88 static int nwins = 1;
89 
90 static Appearance *base_defaultap = NULL;
91 static Appearance *worldap = NULL;
92 extern Color initial_defaultbackcolor;
93 
94 static GeomStruct nullgs = {NULL, NULL};    /* Substitute for optional arg */
95 
96 extern HandleOps GeomOps, WindowOps;
97 
98 #define DGobj(obj) ((DGeom *)obj)
99 #define DVobj(obj) ((DView *)obj)
100 
101 #define name_object(obj, ni, name) _name_object((DObject*)(obj),ni,name)
102 
103 static char *	_name_object(DObject *obj, int ni, char *name);
104 static void	track_changes();
105 static void	winmoved(int camid);
106 static bool     nonidentity(Transform T);
107 static DGeom *	new_dgeom(char *from, enum citizenship citizenship);
108 static DView *	new_dview();
109 static void	updateit(float dt);
110 static void	draw_view(DView *dv);
111 static void	really_draw_view(DView *dv);
112 static void	reconfigure_view(DView *dv);
113 static char *	new_object_name(int type);
114 static void	object_changed(Handle **hp, DObject *obj, void *seqno);
115 static int	object_register(Handle **hp, Ref *ignored, DObject *obj);
116 static void	update_dgeom(DGeom *);
117 static void	update_view(DView *);
118 static void	normalize(DGeom *dg, int normalization);
119 static char *	unique_name(char *name, int id);
120 static void	delete_geometry(DGeom *dg);
121 static void	delete_camera(DView *dv);
122 static void	dview_set_hmodel_camspace(DView *dv, int space);
123 
124 #ifdef MANIFOLD
125 static int verbose_manifold = 0;
126 static int trivial_visibility = 0;
127 #endif
128 
129 /*
130  * Pure inquiry functions -- these do not change any state.
131  */
132 
133 int
drawer_geom_count(void)134 drawer_geom_count(void)
135 {
136   int i, n;
137   DGeom *dg;
138 
139   n = 1;
140   LOOPSOMEGEOMS(i,dg,ORDINARY) ++n;
141   return n;
142 }
143 
144 int
drawer_cam_count(void)145 drawer_cam_count(void)
146 {
147   int i, n;
148   DView *dv;
149 
150   n = 0;
151   LOOPVIEWS(i,dv) ++n;
152   return n;
153 }
154 
155 int
drawer_idbyctx(mgcontext * ctx)156 drawer_idbyctx( mgcontext *ctx )
157 {
158   int i;
159   DView *dv;
160 
161   LOOPVIEWS(i,dv) {
162     if(dv->mgctx == ctx)
163       return CAMID(i);
164   }
165   return NOID;
166 }
167 
168 DObject *
drawer_get_object(int id)169 drawer_get_object( int id )
170 {
171  recheck:
172   if(ISGEOM(id)) {
173     switch(id) {
174     case ALLGEOMS: id = WORLDGEOM; goto recheck;
175     case TARGETID: id = uistate.targetid; goto recheck;
176     case SELF:
177     case TARGETGEOMID: id = GEOMID(uistate.targetgeom); goto recheck;
178     case CENTERID: id = uistate.centerid; goto recheck;
179     default:
180       if(INDEXOF(id) < 0 || INDEXOF(id) >= dgeom_max) return NULL;
181       return (DObject *)dgeom[INDEXOF(id)];
182     }
183   } else if(ISCAM(id)) {
184     switch(id) {
185     case DEFAULTCAMID: return (DObject *)&drawerstate.defview;
186     case FOCUSID: case ALLCAMS: id = CAMID(uistate.mousefocus); goto recheck;
187     case TARGETCAMID: id = CAMID(uistate.targetcam); goto recheck;
188     default:
189       if(INDEXOF(id) < 0 || INDEXOF(id) >= dview_max) return NULL;
190       return (DObject *)dview[INDEXOF(id)];
191     }
192   }
193   return NULL;
194 }
195 
196 int
drawer_idmatch(int id1,int id2)197 drawer_idmatch(int id1, int id2)
198 {
199   switch (id1) {
200   case ALLGEOMS:
201     switch (id2) {
202     case ALLGEOMS:		return 1;
203     case ALLCAMS:		return 0;
204     case FOCUSID:		return 0;
205     case TARGETGEOMID:		return 1;
206     case TARGETCAMID:		return 0;
207     case TARGETID:		return ISGEOM(uistate.targetid);
208     case CENTERID:		return ISGEOM(uistate.centerid);
209     case NOID:			return 0;
210     case DEFAULTCAMID:		return 0;
211     case SELF:			return 0;
212     case UNIVERSE:		return 0;
213     case PRIMITIVE:		return 0;
214     default:			return ISGEOM(id2) && drawer_get_object(id2);
215     }
216     break;
217   case ALLCAMS:
218     switch (id2) {
219     case ALLGEOMS:		return 0;
220     case ALLCAMS:		return 1;
221     case FOCUSID:		return 1;
222     case TARGETGEOMID:		return 0;
223     case TARGETCAMID:		return 1;
224     case TARGETID:		return ISCAM(uistate.targetid);
225     case CENTERID:		return ISCAM(uistate.targetid);
226     case NOID:			return 0;
227     case DEFAULTCAMID:		return 1;
228     case SELF:			return 0;
229     case UNIVERSE:		return 0;
230     case PRIMITIVE:		return 0;
231     default:			return ISCAM(id2) && drawer_get_object(id2);
232     }
233     break;
234   case FOCUSID:
235     switch (id2) {
236     case ALLGEOMS:		return 0;
237     case ALLCAMS:		return 1;
238     case FOCUSID:		return 1;
239     case TARGETGEOMID:		return 0;
240     case TARGETCAMID:		return uistate.mousefocus==CAMID(uistate.targetcam);
241     case TARGETID:		return uistate.mousefocus==uistate.targetid;
242     case CENTERID:		return uistate.mousefocus==uistate.centerid;
243     case NOID:			return 0;
244     case DEFAULTCAMID:		return 0;
245     case SELF:			return 0;
246     case UNIVERSE:		return 0;
247     case PRIMITIVE:		return 0;
248     default:			return uistate.mousefocus==id2;
249     }
250     break;
251   case TARGETGEOMID:
252     switch (id2) {
253     case ALLGEOMS:		return 1;
254     case ALLCAMS:		return 0;
255     case FOCUSID:		return 0;
256     case TARGETGEOMID:		return 1;
257     case TARGETCAMID:		return 0;
258     case TARGETID:		return ISGEOM(uistate.targetid);
259     case CENTERID:		return uistate.centerid==uistate.targetid;
260     case NOID:			return 0;
261     case DEFAULTCAMID:		return 0;
262     case SELF:			return 0;
263     case UNIVERSE:		return 0;
264     case PRIMITIVE:		return 0;
265     default:			return GEOMID(uistate.targetgeom)==id2;
266     }
267     break;
268   case TARGETCAMID:
269     switch (id2) {
270     case ALLGEOMS:		return 0;
271     case ALLCAMS:		return 1;
272     case FOCUSID:		return uistate.mousefocus==uistate.targetid;
273     case TARGETGEOMID:		return 0;
274     case TARGETCAMID:		return 1;
275     case TARGETID:		return ISCAM(uistate.targetid);
276     case CENTERID:		return ISCAM(uistate.centerid);
277     case NOID:			return 0;
278     case DEFAULTCAMID:		return 0;
279     case SELF:			return 0;
280     case UNIVERSE:		return 0;
281     case PRIMITIVE:		return 0;
282     default:			return CAMID(uistate.targetcam)==id2;
283     }
284     break;
285   case TARGETID:
286     switch (id2) {
287     case ALLGEOMS:		return ISGEOM(uistate.targetid);
288     case ALLCAMS:		return ISCAM(uistate.targetid);
289     case FOCUSID:		return ISCAM(uistate.targetid);
290     case TARGETGEOMID:		return ISGEOM(uistate.targetid);
291     case TARGETCAMID:		return ISCAM(uistate.targetid);
292     case TARGETID:		return 1;
293     case CENTERID:		return uistate.targetid==uistate.centerid;
294     case NOID:			return 0;
295     case DEFAULTCAMID:		return 0;
296     case SELF:			return 0;
297     case UNIVERSE:		return 0;
298     case PRIMITIVE:		return 0;
299     default:			return uistate.targetid==id2;
300     }
301     break;
302   case CENTERID:
303     switch (id2) {
304     case ALLGEOMS:		return ISGEOM(uistate.centerid);
305     case ALLCAMS:		return ISCAM(uistate.centerid);
306     case FOCUSID:		return uistate.centerid==uistate.mousefocus;
307     case TARGETGEOMID:		return uistate.centerid==GEOMID(uistate.targetgeom);
308     case TARGETCAMID:		return uistate.centerid==CAMID(uistate.targetcam);
309     case TARGETID:		return uistate.centerid==uistate.targetid;
310     case CENTERID:		return 1;
311     case NOID:			return 0;
312     case DEFAULTCAMID:		return 0;
313     case SELF:			return 0;
314     case UNIVERSE:		return 0;
315     case PRIMITIVE:		return 0;
316     default:			return uistate.centerid==id2;
317     }
318     break;
319   case NOID:
320     switch (id2) {
321     case ALLGEOMS:		return 0;
322     case ALLCAMS:		return 0;
323     case FOCUSID:		return 0;
324     case TARGETGEOMID:		return 0;
325     case TARGETCAMID:		return 0;
326     case TARGETID:		return 0;
327     case CENTERID:		return 0;
328     case NOID:			return 1;
329     case DEFAULTCAMID:		return 0;
330     case SELF:			return 0;
331     case UNIVERSE:		return 0;
332     case PRIMITIVE:		return 0;
333     default:			return 0;
334     }
335     break;
336   case DEFAULTCAMID:
337     switch (id2) {
338     case ALLGEOMS:		return 0;
339     case ALLCAMS:		return 1;
340     case FOCUSID:		return 0;
341     case TARGETGEOMID:		return 0;
342     case TARGETCAMID:		return 0;
343     case TARGETID:		return 0;
344     case CENTERID:		return 0;
345     case NOID:			return 0;
346     case DEFAULTCAMID:		return 1;
347     case SELF:			return 0;
348     case UNIVERSE:		return 0;
349     case PRIMITIVE:		return 0;
350     default:			return 0;
351     }
352     break;
353   case SELF:
354     switch (id2) {
355     case ALLGEOMS:		return 0;
356     case ALLCAMS:		return 0;
357     case FOCUSID:		return 0;
358     case TARGETGEOMID:		return 0;
359     case TARGETCAMID:		return 0;
360     case TARGETID:		return 0;
361     case CENTERID:		return 0;
362     case NOID:			return 0;
363     case DEFAULTCAMID:		return 0;
364     case SELF:			return 1;
365     case UNIVERSE:		return 0;
366     case PRIMITIVE:		return 0;
367     default:			return 0;
368     }
369     break;
370   case UNIVERSE:
371     switch (id2) {
372     case ALLGEOMS:		return 0;
373     case ALLCAMS:		return 0;
374     case FOCUSID:		return 0;
375     case TARGETGEOMID:		return 0;
376     case TARGETCAMID:		return 0;
377     case TARGETID:		return 0;
378     case CENTERID:		return 0;
379     case NOID:			return 0;
380     case DEFAULTCAMID:		return 0;
381     case SELF:			return 0;
382     case UNIVERSE:		return 1;
383     case PRIMITIVE:		return 0;
384     default:			return 0;
385     }
386     break;
387   case PRIMITIVE:
388     switch (id2) {
389     case ALLGEOMS:		return 0;
390     case ALLCAMS:		return 0;
391     case FOCUSID:		return 0;
392     case TARGETGEOMID:		return 0;
393     case TARGETCAMID:		return 0;
394     case TARGETID:		return 0;
395     case CENTERID:		return 0;
396     case NOID:			return 0;
397     case DEFAULTCAMID:		return 0;
398     case SELF:			return 0;
399     case UNIVERSE:		return 0;
400     case PRIMITIVE:		return 1;
401     default:			return 0;
402     }
403     break;
404   default:
405     switch (id2) {
406     case ALLGEOMS:		return ISGEOM(id1) && drawer_get_object(id1);
407     case ALLCAMS:		return ISCAM(id1) && drawer_get_object(id1);
408     case FOCUSID:		return id1==uistate.mousefocus;
409     case TARGETGEOMID:		return id1==GEOMID(uistate.targetgeom);
410     case TARGETCAMID:		return id1==CAMID(uistate.targetcam);
411     case TARGETID:		return id1==uistate.targetid;
412     case CENTERID:		return id1==uistate.centerid;
413     case NOID:			return 0;
414     case DEFAULTCAMID:		return 0;
415     case SELF:			return 0;
416     case UNIVERSE:		return 0;
417     case PRIMITIVE:		return 0;
418     default:			return id1==id2;
419     }
420     break;
421   }
422 }
423 
424 
425 
426 /*
427  * Map an id to a short name
428  */
429 char *
drawer_id2name(int id)430 drawer_id2name( int id )
431 {
432   switch(id) {
433   case ALLGEOMS:  return "g*";
434   case ALLCAMS:   return "c*";
435   case FOCUSID:	  return "focus";
436   case TARGETGEOMID:return "g.";
437   case TARGETCAMID: return "c.";
438   case SELF:	  return "self";
439   case UNIVERSE:  return "universe";
440   case DEFAULTCAMID:return "defaultcam";
441   case TARGETID:   return "target";
442   case CENTERID:   return "center";
443   case BBOXCENTERID: return "bbox-center";
444   case PRIMITIVE:  return "primitive";
445   case NOID:  return "none";
446   default:
447     if(id > 0) {
448       DObject *obj = drawer_get_object(id);
449       if(obj) return obj->name[1];
450     }
451   }
452   OOGLError(1, "drawer_id2name unknown id %d", id);
453   return "?";
454 }
455 
456 /*
457  * Utility routine for MAYBE_LOOP_ALL macro.
458  * Finds object(s) of the given type (any type if T_NONE),
459  * and returns a DObject * each time called.
460  * Returns NULL after all appropriate objects have been returned.
461  * Assumes that the iterator variable *indexp is incr'd by one between calls.
462  * *indexp does double duty: for id's which refer to a single object, it's
463  * used as a flag so that the object is returned exactly once (*indexp==0).
464  * For broadcast id's (ALLGEOMS, ALLCAMS), *indexp is the current table index.
465  * Note that MAYBE_LOOP_ALL does *not* treat WORLDGEOM specially.
466  */
467 DObject *
drawer_next_object(int id,int * indexp,int type)468 drawer_next_object( int id, int *indexp, int type )
469 {
470   int index = *indexp;
471   int limit;
472   DObject **objs;
473 
474   if(id == DEFAULTCAMID && type != T_GEOM)
475     return index > 0 ? NULL : (DObject *)&drawerstate.defview;
476 
477   /* Calling real_id() twice handles TARGETCAMID which might
478    * yield FOCUSID */
479   if(INDEXOF(id) < 0 && INDEXOF(id) != ALLINDEX)
480     id = real_id(real_id(id));
481 
482   if(type != T_NONE && type != TYPEOF(id)) return NULL;
483 
484   switch(TYPEOF(id)) {
485   case T_CAM:  limit = dview_max; objs = (DObject **)dview; break;
486   case T_GEOM: limit = dgeom_max; objs = (DObject **)dgeom; break;
487   default: return NULL;
488   }
489 
490   if(INDEXOF(id) == ALLINDEX) {
491     while(index < limit && objs[index] == NULL)
492       index++;
493     *indexp = index;
494   } else if(index == 0)
495     index = INDEXOF(id);
496   else
497     return NULL;
498 
499   return (index < 0 || index >= limit) ? NULL : objs[index];
500 }
501 
502 /*
503  * Utility routine for MAYBE_LOOP macro.
504  * Like drawer_next_object() above, but arranges that id==WORLDGEOM
505  * is mapped to a broadcast to all ordinary (non-World, non-alien) DGeom's.
506  */
507 DObject *
drawer_next_bcast(int id,int * indexp,int type)508 drawer_next_bcast( int id, int *indexp, int type )
509 {
510   DObject *obj;
511 
512   if(INDEXOF(id) < 0 && INDEXOF(id) != ALLINDEX)
513     id = real_id(real_id(id));
514   if(id == WORLDGEOM)
515     while((obj = drawer_next_object(ALLGEOMS, indexp, type)) != NULL
516 	  && DGobj(obj)->citizenship == ALIEN)
517       (*indexp)++;
518   else
519     obj = drawer_next_object(id, indexp, type);
520   return obj;
521 }
522 
523 
524 /*
525  * Get the indicated object's net Appearance, merging everything from the
526  * backstop appearance down.
527  * For use by the UI, to tell what current settings are.
528  */
529 Appearance *
drawer_get_ap(int id)530 drawer_get_ap( int id )
531 {
532   DGeom *dg;
533   Appearance *ap, *nap;
534 
535   ap = drawerstate.ap;
536   nap = worldap;
537   if(nap)
538     ap = ApMerge(nap, ap, 0);
539 
540   dg = (!ISGEOM(id) || id == GEOMID(ALLINDEX))
541     ? NULL : (DGeom *)drawer_get_object(id);
542   if(dg && dg != dgeom[0] && (nap = GeomAppearance(dg->Item)) != NULL) {
543     nap = ApMerge(nap, ap, 0);
544     if(ap != drawerstate.ap) ApDelete(ap);
545     ap = nap;
546   }
547   if(ap == drawerstate.ap)
548     RefIncr((Ref *)ap);
549   return ap;
550 }
551 
552 
553 /*
554  * Checks whether any object is moving (so that the main loop shouldn't block).
555  * Returns 1 if so, 0 otherwise.
556  */
557 bool
drawer_moving(void)558 drawer_moving(void)
559 {
560   int i;
561   DObject *o;
562 
563   if (motions_exist())
564     return true;
565   for(i = 0; i < dgeom_max; i++)
566     if((o = (DObject *)dgeom[i]) != NULL && (o->moving || o->changed))
567       return true;
568   for(i = 0; i < dview_max; i++)
569     if((o = (DObject *)dview[i]) != NULL && (o->moving || o->changed))
570       return true;
571   return false;
572 }
573 
574 
575 /*
576  * Public functions
577  */
578 
579 /*-----------------------------------------------------------------------
580  * Function:	drawer_idbyname
581  * Description:	parse a name and return the corresponding id
582  * Args:	*name: name to parse
583  * Returns:	id of corresponding dgeom or view
584  * Author:	mbp
585  * Date:	Fri Nov 29 13:42:43 1991
586  * Notes:	returns NOID if there is no object with the given name
587  */
588 int
drawer_idbyname(char * name)589 drawer_idbyname(char *name)
590 {
591   int id;
592 
593   switch (id=(int)(long)fsa_parse(name_fsa, name)) {
594   case TARGETID:
595     id = uistate.targetid;
596     break;
597   case TARGETGEOMID:
598     id = GEOMID(uistate.targetgeom);
599     break;
600   case TARGETCAMID:
601     id = CAMID(uistate.targetcam);
602     break;
603   default:	/* including FOCUSID -- keep literal */
604     break;
605   }
606   return id;
607 }
608 
609 int
drawer_name2metaid(char * name)610 drawer_name2metaid(char *name)
611 {
612   return (int)(long)fsa_parse(name_fsa, name);
613 }
614 
615 
616 
617 LDEFINE(name_object, LVOID,
618 	"(name-object ID NAME)\n"
619 	"Assign a new NAME (a string) to ID.  A number is appended if "
620 	"that name is in use (for example, \"foo\" -> \"foo<2>\").  The new "
621 	"name, possibly with number appended, may be used as object's "
622 	"id thereafter.")
623 {
624   int id;
625   char *name;
626   LDECLARE(("name-object", LBEGIN,
627 	    LID, &id,
628 	    LSTRING, &name,
629 	    LEND));
630   drawer_name_object( id, 1, name );
631   return Lt;
632 }
633 
634 void
drawer_name_object(int id,int ni,char * name)635 drawer_name_object(int id, int ni, char *name)
636 {
637   DObject *obj = drawer_get_object(id);
638 
639   if (obj)
640     name_object( obj, ni, name );
641   ui_objectchange();
642 }
643 
644 LDEFINE(update_draw, LVOID,
645 	"(update-draw CAM-ID  [timestep_in_seconds])\n"
646 	"Apply each incremental motion once and then draw CAM-ID. "
647 	"Applies \"timestep\" seconds' worth of motion, or uses elapsed real "
648 	"time if \"timestep\" is absent or zero.")
649 {
650   int id;
651   float dt = 0;
652   LDECLARE(("update-draw", LBEGIN,
653 	    LID, &id,
654 	    LOPTIONAL, LFLOAT, &dt,
655 	    LEND));
656   gv_update(dt);
657   gv_draw(id);
658   return Lt;
659 }
660 
661 LDEFINE(draw, LVOID,
662 	"(draw CAM-ID)\n"
663 	"Draw the view in CAM-ID, if it needs redrawing. See also \"redraw\".")
664 {
665   DView *dv;
666   int index;
667   int id;
668 
669   LDECLARE(("draw", LBEGIN,
670 	    LID, &id,
671 	    LEND));
672 
MAYBE_LOOP_ALL(id,index,T_CAM,DView,dv)673   MAYBE_LOOP_ALL(id, index, T_CAM, DView, dv) {
674     draw_view(dv);
675   }
676   return Lt;
677 }
678 
679 LDEFINE(update, LVOID,
680 	"(update [timestep_in_seconds])\n"
681 	"Apply each incremental motion once. Uses timestep if it's present and "
682 	"nonzero; otherwise motions are proportional to elapsed real time.")
683 {
684   float realdt, dt = 0;
685   LDECLARE(("update", LBEGIN,
686 	    LOPTIONAL, LFLOAT, &dt,
687 	    LEND));
688   realdt = elapsed(&drawerstate.lastupdate, &drawerstate.lastupdate);
689   if(realdt > uistate.longwhile)
690     realdt = uistate.longwhile;
691   if(dt != 0 || realdt != 0)
692     updateit(dt == 0 ? realdt : dt);
693   track_changes();
694   return Lt;
695 }
696 
697 void
drawer_updateproc(int id,PFI func)698 drawer_updateproc(int id, PFI func)
699 {
700   DObject *obj;
701   int index;
702 
703   MAYBE_LOOP_ALL(id, index, T_NONE, DObject, obj) {
704     obj->updateproc = func;
705     obj->moving = ((obj->updateproc!=NULL) || nonidentity(obj->Incr));
706   }
707 }
708 
709 LDEFINE(redraw, LVOID,
710 	"(redraw CAM-ID)\n"
711 	"States that the view in CAM-ID should be redrawn on the "
712 	"next pass through the main loop or the next invocation of \"draw\".")
713 {
714   DView *dv;
715   int index;
716   int id;
717   LDECLARE(("redraw", LBEGIN,
718 	    LID, &id,
719 	    LEND));
720 
MAYBE_LOOP_ALL(id,index,T_CAM,DView,dv)721   MAYBE_LOOP_ALL(id, index, T_CAM, DView, dv) {
722     dv->newcam = true;
723     dv->frozen &= ~SOFT_FROZEN;
724   }
725   track_changes();
726   return Lt;
727 }
728 
729 LDEFINE(freeze, LVOID,
730 	"(freeze         CAM-ID  [hard-freeze])\n"
731 	"Freeze CAM-ID; drawing in this camera's window is turned off "
732 	"until it is explicitly redrawn with \"(redraw CAM-ID)\", after "
733 	"which time drawing resumes as normal.  hard-freeze (default false) "
734 	"is intended to be \"true\" only when windows are iconified, "
735  	"making them immune even to \"redraw\" until another "
736 	"\"(freeze CAM-ID false)\".")
737 {
738   DView *dv;
739   int index;
740   int id;
741   Keyword hard = NO_KEYWORD;
742   int freeze;
743 
744   LDECLARE(("freeze", LBEGIN,
745 	    LID, &id,
746 	    LOPTIONAL,
747 	    LKEYWORD, &hard,
748 	    LEND));
749 
750   freeze = boolval("freeze", hard) ? HARD_FROZEN : SOFT_FROZEN;
751 
MAYBE_LOOP_ALL(id,index,T_CAM,DView,dv)752   MAYBE_LOOP_ALL(id, index, T_CAM, DView, dv) {
753     dv->frozen = freeze;
754   }
755   return Lt;
756 }
757 
758 void
drawer_pause(int pause)759 drawer_pause(int pause)
760 {
761   drawerstate.pause = pause;
762   ui_action(ACTION_PAUSE);
763 }
764 
765 
766 void
drawer_stop(int id)767 drawer_stop(int id)
768 {
769   if (id == NOID) {
770     gv_xform_incr(ALLCAMS,  &ts_identity);
771     gv_xform_incr(ALLGEOMS, &ts_identity);
772     gv_xform_incr(WORLDGEOM,&ts_identity);
773   } else {
774     gv_xform_incr(id, &ts_identity);
775   }
776 }
777 
center_geom(int id,int center)778 static void center_geom(int id, int center)
779 {
780   int index;
781   DGeom *dg;
782 
783   MAYBE_LOOP_ALL(id, index, T_GEOM, DGeom, dg) {
784     gv_transform_set(dg->id, center, center,
785 		     TRANSLATE_KEYWORD, 0, 0, 0);
786     drawer_stop(dg->id);
787   }
788 }
789 
drawer_center(int id)790 void drawer_center(int id)
791 {
792   if (TYPEOF(id) == T_NONE) {
793     drawer_stop(NOID);
794     center_geom(ALLGEOMS, WORLDGEOM);
795     center_geom(WORLDGEOM, UNIVERSE);
796     gv_camera_reset(ALLCAMS);
797     gv_ui_center_origin(ORIGIN_KEYWORD);
798   } else if (TYPEOF(id) == T_CAM) {
799     gv_camera_reset(id);
800   } else if (TYPEOF(id) == T_GEOM) {
801     center_geom(id, id==WORLDGEOM ? UNIVERSE : WORLDGEOM);
802   }
803 }
804 
805 
806 LDEFINE(xform_set, LVOID,
807 	"(xform-set ID TRANSFORM)\n"
808 	"Overwrite the current object transform with TRANSFORM (set "
809 	"object ID's transform to TRANSFORM).")
810 {
811   DObject *obj;
812   int index;
813   TransformStruct *ts;
814   int id;
815 
816   LDECLARE(("xform-set", LBEGIN,
817 	    LID, &id,
818 	    LTRANSFORM, &ts,
819 	    LEND));
820 
MAYBE_LOOP_ALL(id,index,T_NONE,DObject,obj)821   MAYBE_LOOP_ALL(id, index, T_NONE, DObject, obj) {
822     if (id == ALLGEOMS && DGobj(obj)->citizenship == ALIEN)
823       continue;
824     if (ts->tm == NULL) {
825       /*
826        * Special case for switching externally-controlled motion on & off
827        */
828       if(ISGEOM(id))
829 	GeomSet(obj->Item, CR_AXISHANDLE, ts->h, CR_END);
830       else
831 	CamSet(DVobj(obj)->cam, CAM_C2WHANDLE, ts->h, CAM_END);
832     } else {
833       if (ISGEOM(id))
834 	GeomSet(DGobj(obj)->Item,
835 		CR_AXIS, ts->tm, CR_AXISHANDLE, ts->h, CR_END);
836       else
837 	CamSet(DVobj(obj)->cam,
838 	       CAM_C2W, ts->tm, CAM_C2WHANDLE, ts->h, CAM_END);
839     }
840     TmIdentity(obj->Incr);
841     obj->redraw = true;
842     if (ts->h) {
843       obj->changed |= CH_TRANSFORM;
844     }
845     obj->moving = (obj->updateproc != NULL);
846   }
847   return Lt;
848 }
849 
850 LDEFINE(xform, LVOID,
851 	"(xform ID TRANSFORM)\n"
852 	"Apply TRANSFORM to object ID, as opposed to simply setting its "
853 	"transform, so the effective position "
854 	"of the object will be the concatenation of TRANSFORM with the "
855 	"current object transform.")
856 {
857   int id;
858   TransformStruct *ts;
859   DObject *obj;
860   int index;
861 
862   LDECLARE(("xform", LBEGIN,
863 	    LID, &id,
864 	    LTRANSFORM, &ts,
865 	    LEND));
866 
MAYBE_LOOP_ALL(id,index,T_NONE,DObject,obj)867   MAYBE_LOOP_ALL(id, index, T_NONE, DObject, obj) {
868     if (id == ALLGEOMS && DGobj(obj)->citizenship==ALIEN) {
869       continue;
870     }
871     if (TYPEOF(id) == T_GEOM) {
872       GeomTransform(DGobj(obj)->Item, ts->tm, NULL);
873     } else {
874       CamTransform(DVobj(obj)->cam, ts->tm);
875     }
876     TmIdentity(obj->Incr);
877     obj->redraw = true;
878     obj->moving = (obj->updateproc!=NULL);
879   }
880   return Lt;
881 }
882 
883 LDEFINE(xform_incr, LVOID,
884 	"(xform-incr     ID TRANSFORM)\n"
885 	"Apply continual motion: concatenate TRANSFORM with the current "
886 	"transform of the object every refresh (set object ID's "
887 	"incremental transform to TRANSFORM).")
888 {
889   DObject *obj;
890   int index;
891   TransformStruct *ts;
892   int id;
893   LDECLARE(("xform-incr", LBEGIN,
894 	    LID, &id,
895 	    LTRANSFORM, &ts,
896 	    LEND));
897 
MAYBE_LOOP_ALL(id,index,T_NONE,DObject,obj)898   MAYBE_LOOP_ALL(id, index, T_NONE, DObject, obj) {
899     stop_motions(obj->id);
900     if (id == ALLGEOMS && DGobj(obj)->citizenship == ALIEN) {
901       continue;
902     }
903     if(obj->incrhandle != NULL) {
904       HandlePDelete(&obj->incrhandle);
905     }
906     if((obj->incrhandle = ts->h) != NULL) {
907       HandleRegister(&obj->incrhandle, NULL, obj->Incr, TransUpdate);
908       object_register(&obj->incrhandle, NULL, obj);
909     } else {
910       TmCopy(ts->tm, obj->Incr);
911     }
912     obj->moving = ( nonidentity(obj->Incr) || (obj->updateproc!=NULL) );
913   }
914   return Lt;
915 }
916 
917 LDEFINE(new_camera, LID,
918 	"(new-camera     name [CAMERA])\n"
919 	"Create a new camera with the given name (a string).  If a "
920 	"camera with that name already exists, the new object is given "
921 	"a unique name.  If CAMERA is omitted a default camera is used.")
922 {
923   DView *dv;
924   int id;
925   CameraStruct *cs=NULL;
926   char *name;
927   LDECLARE(("new-camera", LBEGIN,
928 	    LSTRING, &name,
929 	    LOPTIONAL,
930 	    LCAMERA, &cs,
931 	    LEND));
932 
933   dv = new_dview();
934   if (!name) name = "Camera";
935   name_object(dv, 1, name );
936   mgctxset(MG_WnSet, WN_NAME, dv->name[1], WN_END, MG_END);
937 
938   ui_windowWillOpen(dv);
939   mgctxset(MG_SHOW, 1, MG_END);
940   ui_windowDidOpen(dv);
941 
942   ui_objectchange();
943   id=gv_camera(dv->name[0], cs);
944   camera_space_relabel(id);
945   return LNew( LID, &id );
946 }
947 
948 LDEFINE(camera, LINT,
949 	"(camera CAM-ID [CAMERA])\n"
950 	"Specify data for CAM-ID; CAMERA is a string giving an OOGL "
951 	"camera specification.  If no camera CAM-ID exists, "
952 	"it is created; in this case, the second argument is optional, "
953 	"and if omitted, a default camera is used.  See also: new-camera.")
954 {
955   DView *dv;
956   int index;
957   CameraStruct *cs=NULL;
958   char *name;
959   int id;
960   LDECLARE(("camera", LBEGIN,
961 	    LSTRING, &name,
962 	    LOPTIONAL,
963 	    LCAMERA, &cs,
964 	    LEND));
965   if((id = drawer_idbyname(name)) == NOID) {
966     id=gv_new_camera( name, cs );
967   } else {
MAYBE_LOOP(id,index,T_CAM,DView,dv)968     MAYBE_LOOP(id, index, T_CAM, DView, dv) {
969       /* if cam is NULL stick with whatever camera id already has */
970       if (cs->cam) {
971 	RefIncr((Ref*)cs->cam);
972 	RefIncr((Ref*)cs->h);
973 	CamDelete(dv->cam);
974 	HandlePDelete(&dv->camhandle);
975 	dv->cam = cs->cam;
976 	dv->camhandle = cs->h;
977 	if(dv->camhandle) {
978 	  HandleRegister(&dv->camhandle, (Ref *)dv, &dv->cam, HandleUpdRef);
979 	  object_register(&dv->camhandle, NULL, (DObject *)dv);
980 	}
981       }
982       if(dv->cam == NULL) {
983 	dv->cam = CamCopy(drawerstate.defview.cam, NULL);
984       }
985       TmIdentity(dv->Incr);
986       dv->moving = (dv->updateproc!=NULL);
987       dv->newcam = true;
988     }
989     /*  gv_camera_reset(id); */
990   }
991   return LNew( LINT, &id );
992 }
993 
994 LDEFINE(camera_reset, LVOID,
995 	"(camera-reset CAM-ID)\n"
996 	"Reset CAM-ID to its default value.")
997 {
998   DView *dv;
999   int index;
1000   int id;
1001   LDECLARE(("camera-reset", LBEGIN,
1002 	    LID, &id,
1003 	    LEND));
1004 
MAYBE_LOOP(id,index,T_CAM,DView,dv)1005   MAYBE_LOOP(id, index, T_CAM, DView, dv) {
1006     CamReset(dv->cam);
1007     drawer_stop(dv->id);
1008     if ((spaceof(WORLDGEOM) == TM_SPHERICAL) && (dv->hmodel == CONFORMALBALL)) {
1009       CamTranslate( dv->cam, 0.0, 0.0, 10.0 );
1010     }
1011 
1012     if (drawerstate.NDim > 0) {
1013       /* Note: the following resets the *entire* N-D xform to the
1014        * identity, not just that in the subspace we're looking
1015        * through.  Might want to have finer control.  XXX - 7/28/93,
1016        * slevy & holt
1017        */
1018       drawer_set_ND_xform(dv->id, NULL);
1019     }
1020     TmIdentity(dv->Incr);
1021     dv->moving = (dv->updateproc!=NULL);
1022     dv->redraw = true;
1023   }
1024   ui_maybe_refresh(id);
1025   return Lt;
1026 }
1027 
1028 static int
drawer_new_dgeom(char * name,GeomStruct * gs,enum citizenship citizenship)1029 drawer_new_dgeom(char *name, GeomStruct *gs, enum citizenship citizenship)
1030 {
1031   int id;
1032   DGeom *dg;
1033 
1034   dg = new_dgeom( NULL, citizenship );
1035   if (name)
1036     name_object(dg, 1, name);
1037   ui_objectchange();
1038   id = drawer_dgeom(dg->id, gs);
1039   if (citizenship==ALIEN) {
1040     drawer_int( id, DRAWER_NORMALIZATION, NONE );
1041     drawer_int( id, DRAWER_BBOXDRAW, 0 );
1042   }
1043   GeomDice(dg->Item, dg->bezdice, dg->bezdice);
1044   return id;
1045 }
1046 
1047 LDEFINE(new_alien, LID,
1048 	"(new-alien      name [GEOMETRY])\n"
1049 	"Create a new alien (geom not in the world) with the given name "
1050 	"(a string).  GEOMETRY is a string giving an OOGL geometry "
1051 	"specification.  If GEOMETRY is omitted, the new alien "
1052 	"is given an empty geometry.  If an object with that name "
1053 	"already exists, the new alien is given a unique name.  The "
1054 	"light beams that are used to move around the lights are an "
1055 	"example of aliens. They're drawn but are not controllable the "
1056 	"way ordinary objects are: they don't appear in the object "
1057 	"browser and the user can't move them with the normal motion "
1058 	"modes.")
1059 {
1060   GeomStruct *gs = &nullgs;
1061   char *name;
1062   int id;
1063 
1064   LDECLARE(("new-alien", LBEGIN,
1065 	    LSTRING, &name,
1066 	    LOPTIONAL,
1067 	    LGEOM, &gs,
1068 	    LEND));
1069   if (gs->geom) RefIncr((Ref*)(gs->geom));
1070   if (gs->h) RefIncr((Ref*)(gs->h));
1071   id=drawer_new_dgeom(name, gs, ALIEN);
1072   return LNew( LID, &id );
1073 }
1074 
1075 LDEFINE(new_geometry, LID,
1076 	"(new-geometry   name [GEOMETRY])\n\
1077 	Create a new geom with the given name (a string).  GEOMETRY is\n\
1078 	a string giving an OOGL geometry specification.  If\n\
1079 	GEOMETRY is omitted, the new object is given an empty geometry.\n\
1080 	If an object with that name already exists, the new object is\n\
1081 	given a unique name.")
1082 {
1083   int id;
1084   GeomStruct *gs = &nullgs;
1085   char *name;
1086   LDECLARE(("new-geometry", LBEGIN,
1087 	    LSTRING, &name,
1088 	    LOPTIONAL,
1089 	    LGEOM, &gs,
1090 	    LEND));
1091   if (gs->geom) RefIncr((Ref*)(gs->geom));
1092   if (gs->h) RefIncr((Ref*)(gs->h));
1093   id = drawer_new_dgeom(name, gs, ORDINARY);
1094   return LNew( LID, &id );
1095 }
1096 
1097 static int
drawer_dgeom(int id,GeomStruct * gs)1098 drawer_dgeom(int id, GeomStruct *gs)
1099 {
1100   DGeom *dg;
1101   int index;
1102 
1103   if (id == WORLDGEOM) {
1104     OOGLError(1,"drawer_dgeom: Mere mortals cannot change the World Geom!");
1105   } else {
1106     MAYBE_LOOP(id, index, T_GEOM, DGeom, dg) {
1107       GeomSet(dg->Lgeom, CR_HANDLE_GEOM, gs->h, gs->geom, CR_END);
1108       dg->changed = CH_GEOMETRY;
1109 
1110       /* (re-)generate the BSP-tree for this object
1111        *
1112        * We could also hang the BSP-tree to dg->Item, but that would
1113        * require special handling of BSP-trees in InstTransform[To]().
1114        */
1115       GeomBSPTree(dg->Lgeom, NULL, BSPTREE_CREATE);
1116     }
1117   }
1118 
1119   /* Caller expects us to consume the objects */
1120   GeomDelete(gs->geom);
1121   HandleDelete(gs->h);
1122 
1123   return id;
1124 }
1125 
1126 LDEFINE(geometry, LVOID,
1127 	"(geometry       GEOM-ID [GEOMETRY])\n\
1128 	Specify the geometry for GEOM-ID.  GEOMETRY is a string\n\
1129 	giving an OOGL geometry specification.  If no object\n\
1130 	called GEOM-ID exists, it is created; in this case the\n\
1131 	GEOMETRY argument is optional, and if omitted, the new\n\
1132 	object GEOM-ID is given an empty geometry.")
1133 {
1134   char *name;
1135   GeomStruct *gs = &nullgs;
1136   int id;
1137 
1138   LDECLARE(("geometry", LBEGIN,
1139 	    LSTRING, &name,
1140 	    LOPTIONAL,
1141 	    LGEOM, &gs,
1142 	    LEND));
1143   /* we RefIncr() because gv_geometry() (& gv_new_geometry())
1144      expect to own the geom and handle passed in, but the lisp
1145      interpreter actually owns these
1146 
1147      cH: this is wrong, only drawer[_new]_dgeom() expects to own the
1148      object. As gv_new_geometry() increases the ref-count itself we
1149      must not do it here.
1150   */
1151   if((id = drawer_idbyname(name)) == NOID) {
1152     gv_new_geometry( name, gs );
1153   } else {
1154     if (gs->geom) RefIncr((Ref*)(gs->geom));
1155     if (gs->h) RefIncr((Ref*)(gs->h));
1156     drawer_dgeom(id, gs);
1157   }
1158   return Lt;
1159 }
1160 
1161 
1162 static int
drawer_replace_geometry(int id,int * p,int pn,GeomStruct * gs)1163 drawer_replace_geometry(int id, int *p, int pn, GeomStruct *gs)
1164 {
1165   DObject *obj;
1166   int count;
1167   int ind;
1168   Geom *where;
1169 
1170   MAYBE_LOOP(id, ind, T_GEOM, DObject, obj) {
1171     for (GeomGet(DGobj(obj)->Lgeom,
1172 		 CR_GEOM, &where); where && pn>0; pn--, p++) {
1173       for (count = 0; count < *p; count++) {
1174 	GeomGet(where, CR_CDR, &where);
1175       }
1176       if (pn > 1) {
1177 	GeomGet(where, CR_GEOM, &where);
1178       }
1179     }
1180     if (where) {
1181       GeomCCreate(where,
1182 		  ListMethods(), CR_HANDLE_GEOM, gs->h, gs->geom, CR_END);
1183       obj->changed = CH_GEOMETRY;
1184     }
1185   }
1186   return id;
1187 }
1188 
1189 LDEFINE(replace_geometry, LVOID,
1190 	"(replace-geometry GEOM-ID PART-SPECIFICATION GEOMETRY)\n\
1191 	Replace a part of the geometry for GEOM-ID.")
1192 {
1193   char *name;
1194   int p[40];
1195   int pn = 40;
1196   GeomStruct *gs=NULL;
1197   int id;
1198 
1199   LDECLARE(("replace-geometry", LBEGIN,
1200 	    LSTRING, &name,
1201 	    LHOLD, LARRAY, LINT, p, &pn,
1202 	    LGEOM, &gs,
1203 	    LEND));
1204   if ((id = drawer_idbyname(name)) != NOID)
1205     drawer_replace_geometry(id, p, pn, gs);
1206   return Lt;
1207 }
1208 
1209 
1210 LDEFINE(copy, LVOID,
1211 	"(copy [ID] [name])\n\
1212 	Copies an object or camera.  If ID is not specified, it \n\
1213 	is assumed to be targetgeom.  If name is not specified, it \n\
1214 	is assumed to be the same as the name of ID.")
1215 {
1216   DObject *obj;
1217   int id = TARGETGEOMID, newid=0;
1218   int i;
1219   char *name = NULL;
1220   GeomStruct geomstruct;
1221   CameraStruct camstruct;
1222   TransformStruct transformstruct;
1223   ApStruct apstruct;
1224   /* If the user copies the world, this will crash unless we remember not
1225    * to copy things that are copies of something else.  That is what this
1226    * array is for. */
1227   VARARRAY(iscopy, char, 2*dgeom_max);
1228 
1229   LDECLARE(("copy", LBEGIN,
1230 	    LOPTIONAL,
1231 	    LID, &id,
1232 	    LSTRING, &name,
1233 	    LEND));
1234 
1235   id = real_id(id);
1236   geomstruct.h = camstruct.h = transformstruct.h = apstruct.h = NULL;
1237 
1238   memset(iscopy, 0, 2*dgeom_max * sizeof(char));
1239 
MAYBE_LOOP(id,i,T_NONE,DObject,obj)1240   MAYBE_LOOP(id, i, T_NONE, DObject, obj) {
1241     switch(TYPEOF(obj->id)) {
1242     case T_GEOM:
1243       /* Copying the world = copying each thing in the world */
1244       if (((DGeom *)obj)->citizenship == THEWORLD || iscopy[i]) break;
1245       GeomGet(((DGeom *)obj)->Lgeom, CR_GEOM, &geomstruct.geom);
1246       if (((DGeom *)obj)->citizenship == ALIEN)
1247 	newid = gv_new_alien(name == NULL ? obj->name[1] : name, &geomstruct);
1248       else
1249 	newid = gv_new_geometry(name == NULL ? obj->name[1] : name,
1250 				&geomstruct);
1251       apstruct.ap = GeomAppearance(obj->Item);
1252       gv_merge_ap(newid, &apstruct);
1253 
1254       GeomGet(((DGeom *)obj)->Item, CR_AXIS, transformstruct.tm);
1255       iscopy[INDEXOF(newid)] = 1;
1256       break;
1257     case T_CAM:
1258       camstruct.cam = ((DView *)obj)->cam;
1259       newid = gv_new_camera(name == NULL ? obj->name[1] : name,
1260 			    &camstruct);
1261       drawer_get_transform(obj->id, transformstruct.tm, WORLDGEOM);
1262       break;
1263     }
1264     gv_xform_set(newid, &transformstruct);
1265   }
1266 
1267   return Lt;
1268 }
1269 
1270 LDEFINE(delete, LVOID,
1271 	"(delete ID)\n\
1272 	Delete object or camera ID.")
1273 {
1274   DObject *obj;
1275   int index;
1276   int id;
1277   unsigned int wasfrozen = UNFROZEN;
1278 
1279   LDECLARE(("delete", LBEGIN,
1280 	    LID, &id,
1281 	    LEND));
1282 
1283   /* Real-ize the given id.  In case id == TARGETGEOMID, remember we
1284    * may change the target geom during deletion.  Don't let this change
1285    * what's being deleted!
1286    */
1287   id = real_id(id);
1288   wasfrozen = uistate.freeze;
1289   gv_ui_freeze(1);
MAYBE_LOOP(id,index,T_NONE,DObject,obj)1290   MAYBE_LOOP(id, index, T_NONE, DObject, obj) {
1291     int obj_id = obj->id; /* remember, must not access obj->id after
1292 			   * obj has gone.
1293 			   */
1294     if(obj_id == WORLDGEOM || obj_id == DEFAULTCAMID)
1295       continue;
1296     if (obj->name[0])
1297       fsa_install( name_fsa, obj->name[0], NOID );
1298     if (obj->name[1])
1299       fsa_install( name_fsa, obj->name[1], NOID );
1300 
1301     HandleUnregisterAll((Ref *)obj,
1302 			(void *)(long)(int)obj->seqno, object_changed);
1303     stop_motions(obj_id);
1304     switch(TYPEOF(obj_id)) {
1305     case T_GEOM:
1306       delete_geometry((DGeom *)obj); break;
1307     case T_CAM:
1308       delete_camera((DView *)obj); break;
1309     }
1310     if (uistate.centerid==obj_id)
1311       gv_ui_center(TARGETID);
1312   }
1313   gv_ui_freeze(wasfrozen);
1314   return Lt;
1315 }
1316 
1317 LDEFINE(scene, LVOID,
1318 	"(scene          CAM-ID [GEOMETRY])\n\
1319 	Make CAM-ID look at GEOMETRY instead of at the universe.")
1320 {
1321   DView *dv;
1322   NDcam *cluster;
1323   int index;
1324   GeomStruct *gs = &nullgs;
1325   int id;
1326 
1327   LDECLARE(("scene", LBEGIN,
1328 	    LID, &id,
1329 	    LOPTIONAL,
1330 	    LGEOM, &gs,
1331 	    LEND));
1332 
1333   if (drawerstate.NDim > 0 && ISCAM(id) &&
1334       id != ALLCAMS && (cluster = ((DView *)drawer_get_object(id))->cluster)) {
MAYBE_LOOP(ALLCAMS,index,T_CAM,DView,dv)1335     MAYBE_LOOP(ALLCAMS, index, T_CAM, DView, dv) {
1336       if (dv->cluster == cluster) {
1337 	RefIncr((Ref *)gs->geom);
1338 	GeomDelete(dv->Item);
1339 	dv->Item = gs->geom;
1340 	dv->changed = CH_GEOMETRY;
1341       }
1342     }
1343     return Lt;
1344   }
1345 
MAYBE_LOOP(id,index,T_CAM,DView,dv)1346   MAYBE_LOOP(id, index, T_CAM, DView, dv) {
1347     RefIncr((Ref *)gs->geom);
1348     GeomDelete(dv->Item);
1349     dv->Item = gs->geom;
1350     dv->changed = CH_GEOMETRY;
1351   }
1352 
1353   return Lt;
1354 }
1355 
1356 LDEFINE(winenter, LVOID,
1357 	"(winenter       CAM-ID)\n\
1358 	Tell geomview that the mouse cursor is in the window\n\
1359 	of CAM-ID.  This function is for development purposes\n\
1360 	and is not intended for general use.")
1361 {
1362   int id;
1363   LDECLARE(("winenter", LBEGIN,
1364 	    LID, &id,
1365 	    LEND));
1366   winmoved(id);
1367   ui_mousefocus(INDEXOF(id));
1368   return Lt;
1369 }
1370 
1371 LDEFINE(merge_ap, LVOID,
1372 	"(merge-ap       GEOM-ID APPEARANCE)\n\
1373 	Merge in some appearance characteristics to GEOM-ID.\n\
1374 	Appearance parameters include surface and line color, shading\n\
1375 	style, line width, and lighting.")
1376 {
1377   DObject *obj;
1378   int index;
1379   ApStruct *as;
1380   int id;
1381   LDECLARE(("merge-ap", LBEGIN,
1382 	    LID, &id,
1383 	    LAP, &as,
1384 	    LEND));
1385 
1386   if (id == WORLDGEOM)
1387     worldap = ApMerge(as->ap, worldap, APF_INPLACE|APF_OVEROVERRIDE);
MAYBE_LOOP(id,index,T_NONE,DObject,obj)1388   MAYBE_LOOP(id, index, T_NONE, DObject, obj) {
1389     Appearance *thisap = GeomAppearance(obj->Item);
1390     GeomSet(obj->Item, CR_NOCOPY, CR_APPEAR,
1391 	    ApMerge(as->ap, thisap, APF_INPLACE|APF_OVEROVERRIDE), CR_END);
1392     obj->redraw = true;
1393   }
1394   ui_maybe_refresh(id);
1395   return Lt;
1396 }
1397 
1398 void
drawer_set_ap(int id,Handle * h,Appearance * ap)1399 drawer_set_ap(int id, Handle *h, Appearance *ap)
1400 {
1401   DObject *obj;
1402   int index;
1403 
1404   if(id == WORLDGEOM || id == ALLGEOMS) {
1405     ApDelete(worldap);
1406     worldap = ap;
1407   }
1408   MAYBE_LOOP(id, index, T_NONE, DObject, obj) {
1409     GeomSet(obj->Item, CR_NOCOPY, /* CR_APHANDLE, h */
1410 	    CR_APPEAR, ap ? ApCopy(ap,NULL) : NULL, CR_END );
1411     obj->redraw = true;
1412   }
1413   ui_maybe_refresh(id);
1414 }
1415 
1416 LDEFINE(dice, LVOID,
1417 	"(dice           GEOM-ID N)\n\
1418 	Dice any Bezier patches within GEOM-ID into NxN meshes; default 10.")
1419 {
1420   int id, dice;
1421   LDECLARE(("dice", LBEGIN,
1422 	    LID, &id,
1423 	    LINT, &dice,
1424 	    LEND));
1425   drawer_int( id, DRAWER_BEZDICE, dice );
1426   return Lt;
1427 }
1428 
1429 LDEFINE(bbox_draw, LVOID,
1430 	"(bbox-draw      GEOM-ID [yes|no])\n\
1431 	Say whether GEOM-ID's bounding-box should be drawn; \"yes\" if omitted.")
1432 {
1433   int id;
1434   Keyword yes = YES_KEYWORD;
1435 
1436   LDECLARE(("bbox-draw", LBEGIN,
1437 	    LID, &id,
1438 	    LOPTIONAL,
1439 	    LKEYWORD, &yes,
1440 	    LEND));
1441   drawer_int( id, DRAWER_BBOXDRAW, boolval("bbox-draw", yes) );
1442   return Lt;
1443 }
1444 
1445 LDEFINE(camera_draw, LVOID,
1446 	"(camera-draw    CAM-ID [yes|no])\n\
1447 	Say whether or not cameras should be drawn in CAM-ID; \"yes\" if omitted.")
1448 {
1449   int id;
1450   Keyword yes = YES_KEYWORD;
1451 
1452   LDECLARE(("camera-draw", LBEGIN,
1453 	    LID, &id,
1454 	    LOPTIONAL,
1455 	    LKEYWORD, &yes,
1456 	    LEND));
1457   drawer_int( id, DRAWER_CAMERADRAW, boolval("camera-draw", yes) );
1458   return Lt;
1459 }
1460 
1461 LDEFINE(evert, LVOID,
1462 	"(evert          GEOM-ID [yes|no])\n\
1463 	Set the normal eversion state of GEOM-ID.  If the second argument\n\
1464 	is omitted, toggle the eversion state.")
1465 {
1466   int id, val = -1;
1467   LDECLARE(("evert", LBEGIN,
1468 	    LID, &id,
1469 	    LOPTIONAL,
1470 	    LKEYWORD, &val,
1471 	    LEND));
1472   drawer_int( id, DRAWER_EVERT, val );
1473   return Lt;
1474 }
1475 
1476 LDEFINE(soft_shader, LVOID,
1477 	"(soft-shader  CAM-ID  {on|off|toggle})\n\
1478 	Select whether to use software or hardware shading in that camera.")
1479 {
1480   DView *dv;
1481   int id, i, on = -1;
1482 
1483   LDECLARE(("soft-shader", LBEGIN,
1484 	    LID, &id,
1485 	    LOPTIONAL,
1486 	    LKEYWORD, &on,
1487 	    LEND));
1488 
MAYBE_LOOP(id,i,T_CAM,DView,dv)1489   MAYBE_LOOP(id, i, T_CAM, DView, dv) {
1490     if(on == TOGGLE_KEYWORD)
1491       on = dv->shader ? OFF_KEYWORD : ON_KEYWORD;
1492     dv->shader = (on == ON_KEYWORD) ? softshader(dv->id) : NULL;
1493     if(dv->mgctx) {
1494       mgctxselect(dv->mgctx);
1495       mgctxset(MG_SHADER, dv->shader, MG_END);
1496       gv_redraw(dv->id);
1497     }
1498     ui_maybe_refresh(dv->id);
1499   }
1500   return Lt;
1501 }
1502 
1503 
1504 
1505 LDEFINE(stereowin, LLIST,
1506 	"(stereowin	CAM-ID  [no|horizontal|vertical|colored] [gapsize])\n\
1507 	Configure CAM-ID as a stereo window.\n\
1508 	no: entire window is a single pane, stereo disabled\n\
1509 	horizontal: split left/right: left is stereo eye#0, right is #1.\n\
1510 	vertical: split top/bottom: bottom is eye#0, top is #1.\n\
1511 	colored: panes overlap, red is stereo eye#0, cyan is #1.\n\
1512 	A gap of \"gapsize\" pixels is left between subwindows;\n\
1513 	if omitted, subwindows are adjacent.\n\
1514 	If both layout and gapsize are omitted, e.g. (stereowin CAM-ID),\n\
1515 	returns current settings as a ``(stereowin ...)'' command list.\n\
1516 	This command doesn't set stereo projection; use ``merge camera'' or\n\
1517 	``camera'' to set the stereyes transforms, and ``merge window'' or\n\
1518 	``window'' to set the pixel aspect ratio & window position if needed.")
1519 {
1520   int id;
1521   Keyword kw = NOT_A_KEYWORD;
1522   DView *dv;
1523   int gapsize = 0;
1524 
1525   LDECLARE(("stereowin", LBEGIN,
1526 	    LID, &id,
1527 	    LOPTIONAL,
1528 	    LKEYWORD, &kw,
1529 	    LINT, &gapsize,
1530 	    LEND));
1531 
1532   if(!ISCAM(id) || (dv = (DView *)drawer_get_object(id)) == NULL) {
1533     OOGLError(0, "stereowin: expected camera, got %s", drawer_id2name(id));
1534     return Lnil;
1535   }
1536   if(kw == NOT_A_KEYWORD) {
1537     return LLISTTOOBJ
1538       ( LListAppend
1539 	( LListAppend
1540 	  ( LListAppend
1541 	    ( LListAppend(NULL, LSYMBOLTOOBJ("stereowin")),
1542 	      LTOOBJ(LID)(&id)),
1543 	    LTOOBJ(LKEYWORD)(&dv->stereo)),
1544 	  LTOOBJ(LINT)(&dv->stereogap)));
1545   }
1546   if(kw != NO_KEYWORD && kw != HORIZONTAL_KEYWORD && kw != VERTICAL_KEYWORD
1547      && kw != COLORED_KEYWORD) {
1548     OOGLError(0, "stereowin: expected "
1549 	      "\"no\" or "
1550 	      "\"horizontal\" or "
1551 	      "\"vertical\" or "
1552 	      "\"colored\", not \"%s\"", keywordname(kw));
1553     return Lnil;
1554   }
1555   dv->stereo = kw;
1556   dv->stereogap = gapsize;
1557   gv_redraw(id);		/* Schedule redraw/window reconfiguration */
1558   return Lt;
1559 }
1560 
1561 
1562 LDEFINE(space, LVOID,
1563 	"(space {euclidean|hyperbolic|spherical})\n\
1564 	Set the space associated with the world.")
1565 {
1566   int space_keyword, space, id, index;
1567   DView *dv;
1568 
1569   LDECLARE(("space", LBEGIN,
1570 	    LKEYWORD, &space_keyword,
1571 	    LEND));
1572 
1573   space = spaceval("space", space_keyword);
1574   drawerstate.space = space;
1575 
1576   switch (space) {
1577   default:
1578   case TM_EUCLIDEAN:
1579     break;
1580   case TM_HYPERBOLIC:
1581   case TM_SPHERICAL:
1582     drawer_int( ALLGEOMS, DRAWER_BBOXDRAW, 0 );
1583     drawer_int( ALLGEOMS, DRAWER_NORMALIZATION, NONE );
1584     break;
1585   }
1586 
1587   id = ALLCAMS;
MAYBE_LOOP(id,index,T_CAM,DView,dv)1588   MAYBE_LOOP(id, index, T_CAM, DView, dv) {
1589     int mgspace = space;
1590 
1591 #if 0
1592     if (space == TM_HYPERBOLIC || space == TM_SPHERICAL) {
1593       switch (dv->hmodel) {
1594       default:
1595       case VIRTUAL: mgspace |= TM_VIRTUAL; break;
1596       case PROJECTIVE: mgspace |= TM_PROJECTIVE; break;
1597       case CONFORMALBALL: mgspace |= TM_CONFORMAL_BALL; break;
1598       }
1599     }
1600 #endif
1601 
1602     mgspace |= TM_VIRTUAL;
1603     mgctxselect(dv->mgctx);
1604     mgctxset(MG_SPACE, mgspace, MG_END);
1605 
1606     gv_hmodel(dv->id, VIRTUAL_KEYWORD);
1607 
1608     if(dv->shader)
1609       mgctxset(MG_SHADER, dv->shader = softshader(dv->id), MG_END);
1610 
1611   }
1612 
1613   gv_xform_set(ALLGEOMS, &ts_identity);
1614   gv_xform_set(WORLDGEOM, &ts_identity);
1615   drawer_stop(NOID);
1616 
1617 #if 0
1618   /* why are these here ????  mbp Wed Jun  9 14:21:34 1993 */
1619   filepanel = ui_name2panel("Files");
1620   if(ui_panelshown(filepanel)) ui_showpanel(filepanel, 1);	/* Reselect */
1621 #endif
1622 
1623   return Lt;
1624 }
1625 
1626 LDEFINE(hmodel, LVOID,
1627 	"(hmodel CAMID {virtual|projective|conformal})\n\
1628 	Set the model used to display geometry in\n\
1629 	this camera; see also \"space\".")
1630 {
1631   int id, index, model_keyword;
1632   DView *dv;
1633 
1634   LDECLARE(("hmodel", LBEGIN,
1635 	    LID, &id,
1636 	    LKEYWORD, &model_keyword,
1637 	    LEND));
1638 
MAYBE_LOOP(id,index,T_CAM,DView,dv)1639   MAYBE_LOOP(id, index, T_CAM, DView, dv) {
1640     int mgspace;
1641     mgctxselect(dv->mgctx);
1642     mgctxget(MG_SPACE, &mgspace);
1643     mgspace = TM_SPACE(mgspace);
1644     dv->hmodel = hmodelval("hmodel", model_keyword);
1645     dview_set_hmodel_camspace(dv,mgspace);
1646     /*    if (mgspace == TM_HYPERBOLIC) */
1647     switch (dv->hmodel) {
1648     default:
1649     case VIRTUAL:
1650       gv_hsphere_draw(id, 0);
1651       break;
1652     case PROJECTIVE:
1653     case CONFORMALBALL:
1654       gv_hsphere_draw(id, 1);
1655       break;
1656     }
1657     switch (dv->hmodel) {
1658     default:
1659     case VIRTUAL: mgspace |= TM_VIRTUAL; break;
1660     case PROJECTIVE: mgspace |= TM_PROJECTIVE; break;
1661     case CONFORMALBALL: mgspace |= TM_CONFORMAL_BALL;
1662 
1663 
1664       break;
1665 
1666       break;
1667     }
1668     mgctxset(MG_SPACE, mgspace, MG_END);
1669     gv_camera_reset(id);
1670     camera_space_relabel(id);
1671   }
1672   return Lt;
1673 }
1674 
1675 LDEFINE(inhibit_warning, LVOID,
1676 	"(inhibit-warning STRING)\n\
1677 	Inhibit warning inhbits geomview from displaying a\n\
1678 	particular warning message determined by STRING.\n\
1679 	At present there are no warning messages that this\n\
1680 	applies to, so this command is rather useless.")
1681 {
1682   char *s=NULL;
1683 
1684   LDECLARE(("inhibit-warning", LBEGIN,
1685 	    LSTRING, &s,
1686 	    LEND));
1687 
1688   return Lt;
1689 }
1690 
1691 static void
dview_set_hmodel_camspace(DView * dv,int space)1692 dview_set_hmodel_camspace(DView *dv, int space)
1693 {
1694   int camspace;
1695 
1696   switch (dv->hmodel) {
1697   default:
1698   case VIRTUAL:
1699     camspace = space;
1700     break;
1701   case PROJECTIVE:
1702   case CONFORMALBALL:
1703     camspace = TM_EUCLIDEAN;
1704     break;
1705   }
1706   CamSet(dv->cam, CAM_SPACE, camspace, CAM_END);
1707 }
1708 
1709 
1710 LDEFINE(hsphere_draw, LVOID,
1711 	"(hsphere-draw   CAMID [yes|no])\n\
1712 	Say whether to draw a unit sphere: the sphere at infinity in\n\
1713 	hyperbolic space, and a reference sphere in Euclidean and spherical\n\
1714 	spaces.  If the second argument is omitted, \"yes\" is assumed.")
1715 {
1716   int id;
1717   Keyword yes = YES_KEYWORD;
1718 
1719   LDECLARE(("hsphere-draw", LBEGIN,
1720 	    LID, &id,
1721 	    LOPTIONAL,
1722 	    LKEYWORD, &yes,
1723 	    LEND));
1724   drawer_int( id, DRAWER_HSPHERE, boolval("hsphere-draw", yes) );
1725   return Lt;
1726 }
1727 
1728 LDEFINE(pickable, LVOID,
1729 	"(pickable       GEOM-ID {yes|no})\n\
1730 	Say whether or not GEOM-ID is included in the pool of objects\n\
1731 	that could be returned from the pick command.")
1732 {
1733   int id;
1734   Keyword yes = YES_KEYWORD;
1735 
1736   LDECLARE(("pickable", LBEGIN,
1737 	    LID, &id,
1738 	    LOPTIONAL,
1739 	    LKEYWORD, &yes,
1740 	    LEND));
1741   drawer_int( id, DRAWER_PICKABLE, boolval("pickable", yes) );
1742   return Lt;
1743 }
1744 
1745 LDEFINE(normalization, LVOID,
1746 	"(normalization  GEOM-ID {each|none|all|keep})\n\
1747 	Set the normalization status of GEOM-ID.\n\
1748 	\"none\" suppresses all normalization.\n\
1749 	\"each\" normalizes the object's bounding box to fit into the unit\n\
1750 	   sphere, with the center of its bounding box translated\n\
1751 	   to the origin.  The box is scaled such that its long diagonal,\n\
1752 	   sqrt((xmax-xmin)^2 + (ymax-ymin)^2 + (zmax-zmin)^2), is 2.\n\
1753 	\"all\" resembles \"each\", except when an object is changing\n\
1754 	   (e.g. when its geometry is being changed by an external program).\n\
1755 \n\
1756 	   Then, \"each\" tightly fits the bounding box around the\n\
1757 	   object whenever it changes and normalizes accordingly,\n\
1758 	   while \"all\" normalizes the union of all variants of the object\n\
1759 	   and normalizes accordingly.\n\
1760 \n\
1761 	\"keep\" leaves the current normalization transform unchanged\n\
1762 	when the object changes.  It may be useful to apply \"each\" or\n\
1763 	\"all\" normalization apply to the first version of a changing\n\
1764 	object to bring it in view, then switch to \"keep\"")
1765 {
1766   int id, val;
1767   LDECLARE(("normalization", LBEGIN,
1768 	    LID, &id,
1769 	    LKEYWORD, &val,
1770 	    LEND));
1771   val = normalval("normalization", val);
1772 
1773   drawer_int( id, DRAWER_NORMALIZATION, val );
1774   return Lt;
1775 }
1776 
1777 void
drawer_int(int id,DrawerKeyword key,int ival)1778 drawer_int(int id, DrawerKeyword key, int ival)
1779 {
1780   DObject *obj;
1781   int index;
1782   int flag, override;
1783   ApStruct as;
1784 
1785   as.h = NULL;
1786 
1787   switch (key) {
1788   case DRAWER_FACEDRAW: flag = override = APF_FACEDRAW; goto setflag;
1789   case DRAWER_EDGEDRAW: flag = override = APF_EDGEDRAW; goto setflag;
1790   case DRAWER_VECTDRAW: flag = override = APF_VECTDRAW; goto setflag;
1791   case DRAWER_NORMALDRAW: flag = override = APF_NORMALDRAW; goto setflag;
1792   case DRAWER_TEXTUREDRAW: flag = override = APF_TEXTURE; goto setflag;
1793   case DRAWER_TEXTUREQUAL: flag = override = APF_TXMIPMAP|APF_TXMIPINTERP|APF_TXLINEAR; goto setflag;
1794   case DRAWER_BACKCULL: flag = override = APF_BACKCULL; goto setflag;
1795   case DRAWER_EVERT: flag = override = APF_EVERT; goto setflag;
1796   case DRAWER_SHADELINES: flag = override = APF_SHADELINES; goto setflag;
1797   case DRAWER_CONCAVE: flag = override = APF_CONCAVE; goto setflag;
1798   setflag:
1799     as.ap = ApCreate(ival ? AP_DO : AP_DONT, flag,
1800 		     AP_OVERRIDE, override & uistate.apoverride, AP_END);
1801     goto mergeap;
1802 
1803   case DRAWER_TRANSLUCENCY:
1804     flag = override = APF_TRANSP;
1805     as.ap = ApCreate(ival < 0 ? AP_DONT : AP_DO, flag,
1806 		     AP_TRANSLUCENCY, abs(ival) - 1,
1807 		     AP_OVERRIDE, APF_TRANSP & uistate.apoverride, AP_END);
1808     goto mergeap;
1809 
1810   case DRAWER_SHADING:
1811     as.ap = ApCreate(AP_SHADING, ival,
1812 		     AP_OVERRIDE, APF_SHADING & uistate.apoverride, AP_END);
1813     goto mergeap;
1814 
1815   case DRAWER_LINEWIDTH:
1816     as.ap = ApCreate(AP_LINEWIDTH, ival,
1817 		     AP_OVERRIDE, APF_LINEWIDTH & uistate.apoverride, AP_END);
1818     goto mergeap;
1819 
1820   case DRAWER_BEZDICE:
1821     as.ap = ApCreate(AP_DICE, ival, ival,
1822 		     AP_OVERRIDE, APF_DICE & uistate.apoverride, AP_END);
1823     gv_merge_ap(id, &as);
1824     ApDelete(as.ap);
1825 #if 0
1826     MAYBE_LOOP(id, index, T_NONE, DObject, obj) {
1827       DGobj(obj)->bezdice = ival;
1828       GeomDice(DGobj(obj)->Item, ival, ival);
1829       obj->changed = CH_GEOMETRY;
1830     }
1831 #endif
1832     break;
1833 
1834   mergeap:
1835     gv_merge_ap(id, &as);
1836     ApDelete(as.ap);
1837     return;		/* No need to ui_maybe_refresh(), merge_ap does it. */
1838 
1839   case DRAWER_PROJECTION:
1840     MAYBE_LOOP(id, index, T_CAM, DObject, obj) {
1841       CamSet(DVobj(obj)->cam, CAM_PERSPECTIVE, ival, CAM_END);
1842       obj->changed = CH_GEOMETRY;
1843     }
1844     break;
1845 
1846   case DRAWER_BBOXDRAW:
1847     MAYBE_LOOP(id, index, T_GEOM, DObject, obj) {
1848       DGobj(obj)->bboxdraw = ival;
1849       obj->redraw = true;
1850     }
1851     break;
1852 
1853 
1854   case DRAWER_NORMALIZATION:
1855     MAYBE_LOOP(id, index, T_GEOM, DObject, obj) {
1856       normalize(DGobj(obj), ival);
1857     }
1858     break;
1859 
1860   case DRAWER_PICKABLE:
1861     MAYBE_LOOP(id, index, T_GEOM, DObject, obj) {
1862       DGobj(obj)->pickable = ival != 0;
1863     }
1864     break;
1865 
1866   case DRAWER_HSPHERE:
1867     MAYBE_LOOP(id, index, T_CAM, DObject, obj) {
1868       set_hsphere_draw( id, ival );
1869       obj->redraw = true;
1870     }
1871     break;
1872 
1873   case DRAWER_CAMERADRAW:
1874     MAYBE_LOOP(id, index, T_CAM, DObject, obj) {
1875       DVobj(obj)->cameradraw = ival != 0;
1876       obj->redraw = true;
1877     }
1878     break;
1879 
1880   case DRAWER_DOUBLEBUFFER:
1881     MAYBE_LOOP(id, index, T_CAM, DObject, obj) {
1882       int opts;
1883       if(DVobj(obj)->mgctx == NULL) continue;
1884       mgctxselect(DVobj(obj)->mgctx);
1885       mgctxget(MG_SETOPTIONS, &opts);
1886       mgctxset(ival==0 || (ival<0 && opts&MGO_DOUBLEBUFFER) ?
1887 	       MG_UNSETOPTIONS : MG_SETOPTIONS,
1888 	       MGO_DOUBLEBUFFER, MG_END);
1889       obj->redraw = true;
1890     }
1891     break;
1892 
1893   case DRAWER_APOVERRIDE:
1894     /* Select whether appearance controls use the 'override' feature
1895      * to supplant user settings.  Might be nice if this were settable
1896      * per feature rather than across the board.
1897      */
1898     uistate.apoverride = ival ? ~0 : 0;
1899     MAYBE_LOOP(id, index, T_GEOM, DObject, obj) {
1900       ApUseOverrides( GeomAppearance(obj->Item), uistate.apoverride );
1901       if(obj->id == WORLDGEOM) {
1902 	ApUseOverrides( worldap, uistate.apoverride );
1903       }
1904       if(obj->id != WORLDGEOM && !uistate.apoverride) {
1905 	/* Allow pre-existing World settings to win over those in children */
1906 	ApLetPropagate( worldap, GeomAppearance(obj->Item) );
1907       }
1908       obj->redraw = true;
1909     }
1910     break;
1911 
1912   case DRAWER_INERTIA:
1913     uistate.inertia = ival<0 ? !uistate.inertia : ival;	break;
1914   case DRAWER_CONSTRAIN:
1915     uistate.constrained = ival<0 ? !uistate.constrained : ival; break;
1916   case DRAWER_OWNMOTION:
1917     uistate.ownmotion = ival<0 ? !uistate.ownmotion : ival;	break;
1918 
1919   default: ; /* This should never happen */
1920 
1921   }
1922   ui_maybe_refresh(id);
1923 }
1924 
1925 LDEFINE(lines_closer, LVOID,
1926 	"(lines-closer   CAM-ID DIST)\n\
1927 	Draw lines (including edges) closer to the camera than polygons\n\
1928 	by DIST / 10^5  of the Z-buffer range.  DIST = 3.0 by default.\n\
1929 	If DIST is too small, a line lying on a surface may be\n\
1930 	dotted or invisible, depending on the viewpoint.\n\
1931 	If DIST is too large, lines may appear in front of surfaces\n\
1932 	that they actually lie behind.  Good values for DIST vary with\n\
1933 	the scene, viewpoint, and distance between near and far clipping\n\
1934 	planes.  This feature is a kludge, but can be helpful.")
1935 {
1936   float dist;
1937   int id;
1938   LDECLARE(("lines-closer", LBEGIN,
1939 	    LID, &id,
1940 	    LFLOAT, &dist,
1941 	    LEND));
1942   drawer_float(id, DRAWER_LINE_ZNUDGE, dist);
1943   return Lt;
1944 }
1945 
1946 void
drawer_float(int id,DrawerKeyword key,float fval)1947 drawer_float(int id, DrawerKeyword key, float fval)
1948 {
1949   DObject *obj;
1950   ApStruct as;
1951   int index;
1952   int attr, over;
1953   LInterest *interested;
1954 
1955   as.h = NULL;
1956 
1957   switch (key) {
1958 
1959   case DRAWER_NEAR: attr = CAM_NEAR; goto setcam;
1960   case DRAWER_FAR: attr = CAM_FAR; goto setcam;
1961   case DRAWER_FOV: attr = CAM_FOV; goto setcam;
1962   case DRAWER_FOCALLENGTH: attr = CAM_FOCUS; goto setcam;
1963   setcam:
1964     interested = LInterestList("merge");
1965     MAYBE_LOOP(id, index, T_CAM, DObject, obj) {
1966       DVobj(obj)->cam = CamSet(DVobj(obj)->cam, attr, fval, CAM_END);
1967       DVobj(obj)->redraw = true;
1968       if(interested)
1969 	gv_merge(&CamOps, DVobj(obj)->id, (Ref *)DVobj(obj)->cam);
1970     }
1971     break;
1972 
1973   case DRAWER_KA: attr = MT_Ka; over = MTF_Ka; goto domt;
1974   case DRAWER_KD: attr = MT_Kd; over = MTF_Kd; goto domt;
1975   case DRAWER_KS: attr = MT_Ks; over = MTF_Ks; goto domt;
1976   case DRAWER_SHININESS: attr = MT_SHININESS; over = MTF_SHININESS; goto domt;
1977   case DRAWER_ALPHA: attr = MT_ALPHA; over = MTF_ALPHA; goto domt;
1978   domt:
1979     as.ap = ApCreate(AP_MtSet, attr, fval,
1980 		     MT_OVERRIDE, over & uistate.apoverride,
1981 		     MT_END, AP_END);
1982     goto mergeap;
1983 
1984   case DRAWER_NORMSCALE:
1985     as.ap = ApCreate(AP_NORMSCALE, fval,
1986 		     AP_OVERRIDE, APF_NORMSCALE & uistate.apoverride, AP_END);
1987     goto mergeap;
1988   mergeap:
1989     gv_merge_ap(id, &as);
1990     ApDelete(as.ap);
1991     return;
1992 
1993   case DRAWER_LIGHT_INTENSITY:
1994     set_light_intensity( fval );
1995     drawerstate.changed = true;
1996     break;
1997 
1998   case DRAWER_LINE_ZNUDGE:
1999     MAYBE_LOOP(id, index, T_CAM, DObject, obj) {
2000       DVobj(obj)->lineznudge = fval;
2001       DVobj(obj)->redraw = true;
2002     }
2003     drawerstate.defview.lineznudge = fval;
2004     break;
2005   default: {
2006     /* report some kind of error? */
2007   }
2008 
2009   }
2010   ui_maybe_refresh(id);
2011 }
2012 
2013 LDEFINE(backcolor, LVOID,
2014 	"(backcolor      CAM-ID R G B)\n\
2015 	Set the background color of CAM-ID; R G B are numbers\n\
2016 	between 0 and 1.")
2017 {
2018   int id;
2019   Color color;
2020   LDECLARE(("backcolor", LBEGIN,
2021 	    LID, &id,
2022 	    LFLOAT, &(color.r),
2023 	    LFLOAT, &(color.g),
2024 	    LFLOAT, &(color.b),
2025 	    LEND));
2026   drawer_color( id, DRAWER_BACKCOLOR, &color );
2027   return Lt;
2028 }
2029 
2030 LDEFINE(bbox_color, LVOID,
2031 	"(bbox-color     GEOM-ID R G B)\n\
2032 	Set the bounding-box color of GEOM-ID; R G B are numbers\n\
2033 	between 0 and 1.")
2034 {
2035   int id;
2036   Color color;
2037   LDECLARE(("bbox-color", LBEGIN,
2038 	    LID, &id,
2039 	    LFLOAT, &(color.r),
2040 	    LFLOAT, &(color.g),
2041 	    LFLOAT, &(color.b),
2042 	    LEND));
2043   drawer_color( id, DRAWER_BBOXCOLOR, &color );
2044   return Lt;
2045 }
2046 
2047 void
drawer_color(int id,DrawerKeyword key,Color * col)2048 drawer_color(int id, DrawerKeyword key, Color *col)
2049 {
2050   int index;
2051   ApStruct as;
2052   DObject *obj;
2053 
2054   as.h = NULL;
2055 
2056   switch (key) {
2057 
2058   case DRAWER_DIFFUSE:
2059     as.ap = ApCreate(AP_MtSet, MT_DIFFUSE, col, MT_AMBIENT, col,
2060 		     MT_OVERRIDE, MTF_DIFFUSE & uistate.apoverride, MT_END, AP_END);
2061     goto doap;
2062   case DRAWER_EDGECOLOR:
2063     as.ap = ApCreate(AP_MtSet, MT_EDGECOLOR, col,
2064 		     MT_OVERRIDE, MTF_EDGECOLOR & uistate.apoverride, MT_END, AP_END);
2065     goto doap;
2066   case DRAWER_NORMALCOLOR:
2067     as.ap = ApCreate(AP_MtSet, MT_NORMALCOLOR, col,
2068 		     MT_OVERRIDE, MTF_NORMALCOLOR & uistate.apoverride, MT_END, AP_END);
2069     goto doap;
2070   doap:
2071     gv_merge_ap(id, &as);
2072     ApDelete(as.ap);
2073     break;
2074 
2075   case DRAWER_BBOXCOLOR:
2076     MAYBE_LOOP(id, index, T_GEOM, DObject, obj) {
2077       if (DGobj(obj)->bboxap && DGobj(obj)->bboxap->mat) {
2078 	MtSet(DGobj(obj)->bboxap->mat, MT_EDGECOLOR, col,
2079 	      MT_OVERRIDE, MTF_EDGECOLOR & uistate.apoverride, MT_END);
2080 	obj->redraw = true;
2081       }
2082       else {
2083 	OOGLError(0,"object with %1d has no bboxap!");
2084       }
2085     }
2086     uistate.pickedbboxcolor = *col;
2087     break;
2088 
2089   case DRAWER_BACKCOLOR:
2090     drawerstate.defview.backcolor = *col;
2091     MAYBE_LOOP(id, index, T_CAM, DObject, obj) {
2092       DVobj(obj)->backcolor = *col;
2093       DVobj(obj)->redraw = true;
2094     }
2095     break;
2096 
2097   case DRAWER_LIGHTCOLOR:
2098     set_light_color(col);
2099     break;
2100 
2101   default: {
2102     /* report some kind of error? */
2103   }
2104 
2105   }
2106   ui_maybe_refresh(id);
2107 }
2108 
2109 LDEFINE(window, LVOID,
2110 	"(window         CAM-ID  WINDOW)\n\
2111 	Specify attributes for the window of CAM-ID, e.g. its size\n\
2112 	or initial position, in the OOGL Window syntax.\n\
2113 	The special CAM-ID \"default\" specifies\n\
2114 	properties of future windows (created by \"camera\" or\n\
2115 	\"new-camera\").")
2116 {
2117   int index;
2118   DView *dv;
2119   int id;
2120   WindowStruct *ws;
2121 
2122   LDECLARE(("window", LBEGIN,
2123 	    LID, &id,
2124 	    LWINDOW, &ws,
2125 	    LEND));
2126 
MAYBE_LOOP(id,index,T_CAM,DView,dv)2127   MAYBE_LOOP(id, index, T_CAM, DView, dv) {
2128     WnDelete(dv->win);
2129     dv->win = WnCopy(ws->wn);
2130     if(dv->mgctx) {
2131       mgctxselect(dv->mgctx);
2132       mgctxset(MG_WINDOW, dv->win, MG_END);
2133     }
2134   }
2135   return Lt;
2136 }
2137 
2138 /*
2139  * Everything below here is internal to the drawer module (and should
2140  * be declared static to enforce this).
2141  */
2142 
2143 /*
2144  * Track all changes made to dgeoms and views, by checking their
2145  * "changed", "moving" and "redraw" flags.
2146  * Effect:
2147  *	For each changed ordinary DGeom, calls update_dgeom()
2148  *		to possibly recompute the bounding box and/or renormalize.
2149  *	Set the "redraw" flags on all DViews which:
2150  *		are marked as "changed" or "moving", or
2151  *	Also set "redraw" on all DViews which view the world,
2152  *		if any DGeoms are "changed" or "moving".
2153  *
2154  * Environment:
2155  *	"changed" flags are set by gv_geometry(), etc.
2156  *		and by callback routines from external changes.
2157  *		They're cleared by this routine.
2158  *	"redraw" flags are cleared by draw_view().
2159  *	"moving" is set by drawer_xform_incr() and drawer_updateproc().
2160  */
2161 static void
track_changes()2162 track_changes()
2163 {
2164   bool worldchange = drawerstate.changed;
2165   bool viewchange = 0;
2166   int i;
2167   DObject *o;
2168 
2169   for(i = 0; i < dgeom_max; i++) {
2170     if((o = (DObject *)dgeom[i]) != NULL) {
2171       if(o->moving)
2172 	worldchange = true;
2173       if(o->changed || o->redraw) {
2174 	worldchange = true;
2175 	update_dgeom((DGeom*)o);
2176 	o->changed = CH_NOTHING;
2177 	o->redraw = false;
2178       }
2179     }
2180   }
2181   for(i = 0; i < dview_max; i++) {
2182     if((o = (DObject *)dview[i]) != NULL) {
2183       if(o->moving || ((DView *)o)->newcam
2184 	 || (worldchange && o->Item == drawerstate.universe))
2185 	o->changed = true;
2186       if(o->changed) {
2187 	update_view((DView *)o);
2188 	viewchange = true;
2189       }
2190       if(!((DView*)o)->frozen) {
2191 	viewchange = viewchange || o->redraw;
2192       }
2193     }
2194   }
2195   if(viewchange) {
2196     for(i = 0; i < dview_max; i++)
2197       if((o = (DObject *)dview[i]) != NULL)
2198 	if(((DView *)o)->cameradraw)
2199 	  o->redraw = true;
2200   }
2201   drawerstate.changed = false;
2202 }
2203 
2204 LDEFINE(merge_baseap, LVOID,
2205 	"(merge-baseap   APPEARANCE)\n\
2206 	Merge in some appearance characteristics to the base default\n\
2207 	appearance (applied to every geom before its own apperance).\n\
2208 	Lighting is typically included in the base appearance.")
2209 {
2210   ApStruct *as;
2211   LDECLARE(("merge-baseap", LBEGIN,
2212 	    LAP, &as,
2213 	    LEND));
2214   drawerstate.ap = ApMerge(as->ap,
2215 			   drawerstate.ap ? drawerstate.ap : base_defaultap,
2216 			   APF_OVEROVERRIDE);
2217   drawerstate.apseq++;
2218   lights_changed_check();
2219   return Lt;
2220 }
2221 
2222 void
drawer_merge_camera(int id,Camera * cam)2223 drawer_merge_camera( int id, Camera *cam )
2224 {
2225   DView *dv;
2226   int index;
2227 
2228   MAYBE_LOOP(id, index, T_CAM, DView, dv) {
2229     CamMerge(cam, dv->cam);
2230     dv->newcam = true;
2231   }
2232 }
2233 
2234 void
drawer_merge_window(int id,WnWindow * win)2235 drawer_merge_window( int id, WnWindow *win )
2236 {
2237   DView *dv;
2238   int index;
2239 
2240   MAYBE_LOOP(id, index, T_CAM, DView, dv) {
2241     WnMerge(win, dv->win);
2242     if(dv->mgctx) {
2243       mgctxselect(dv->mgctx);
2244       mgctxset(MG_WINDOW, dv->win, MG_END);
2245       dv->newcam = true;
2246     }
2247   }
2248 }
2249 
delete_geometry(DGeom * dg)2250 static void delete_geometry(DGeom *dg)
2251 {
2252   int i, index = INDEXOF(dg->id);
2253 
2254   if(dgeom[index] == NULL)	/* Already (being) deleted -- forget it */
2255     return;
2256 
2257   if (dg->bbox_center != NOID) {
2258     /* Delete the bbox-center object */
2259     gv_delete(dg->bbox_center);
2260     dg->bbox_center = NOID;
2261   }
2262 
2263   dgeom[index] = NULL;
2264   switch (dg->citizenship) {
2265   case ORDINARY:
2266     dgeom[0]->Lgeom = ListRemove( dgeom[0]->Lgeom, dg->Item );
2267     GeomReplace( dgeom[0]->Inorm, dgeom[0]->Lgeom );
2268     ui_objectchange();
2269     if (uistate.targetgeom==index) {
2270       /* We deleted the target -- find next lowest one */
2271       for (i=index; --i>=0 && dgeom[i]==NULL; );
2272       gv_ui_target( GEOMID(i),
2273 		    TYPEOF(uistate.targetid) == T_GEOM ? IMMEDIATE : NOIMMEDIATE );
2274     }
2275     break;
2276   case ALIEN:
2277     if (ListRemove(drawerstate.universe, dg->Item) != drawerstate.universe) {
2278       OOGLError(0, "\
2279 head of the universe changed --- this shouldn't happen; please report this bug!");
2280     }
2281     break;
2282   default: ; /* This should never happen */
2283   }
2284   HandlePDelete(&dg->incrhandle);
2285   OOGLFree(dg);
2286   drawerstate.changed = true;
2287 }
2288 
delete_camera(DView * dv)2289 static void delete_camera(DView *dv)
2290 {
2291   int index = INDEXOF(dv->id);
2292   int i;
2293 
2294   if(dview[index] == NULL)	/* Already (being) deleted -- forget it */
2295     return;
2296 
2297   dview[index] = NULL;
2298   mgctxdelete(dv->mgctx);
2299   CamDelete(dv->cam);
2300   HandlePDelete(&dv->camhandle);
2301   GeomDelete(dv->Item);
2302   OOGLFree(dv);
2303   ui_objectchange();
2304   if (index == uistate.mousefocus) {
2305     for(i = dview_max; --i > 0 && (dview[i] == NULL ||
2306 				   dview[i]->frozen != UNFROZEN); )
2307       ;
2308     ui_mousefocus(i);
2309   }
2310   if (drawer_get_object(TARGETCAMID) == NULL) {
2311     if (drawer_get_object(FOCUSID) == NULL) {
2312       gv_ui_target( WORLDGEOM, IMMEDIATE);
2313     } else {
2314       gv_ui_target( FOCUSID,
2315 		    TYPEOF(uistate.targetid) == T_CAM ? IMMEDIATE : NOIMMEDIATE);
2316     }
2317   }
2318 }
2319 
2320 void
drawer_init(char * apdefault,char * defaultcam,char * windefault)2321 drawer_init(char *apdefault, char *defaultcam, char *windefault)
2322 {
2323   int i;
2324   DGeom *dg;
2325   IOBFILE *f;
2326 
2327   ts_identity.h = NULL;
2328   TmIdentity(ts_identity.tm);
2329 
2330   f = iobfileopen(fmemopen(apdefault, strlen(apdefault), "rb"));
2331   if(f)
2332     drawerstate.ap =
2333       base_defaultap =
2334       ApFLoad(f, "built-in appearance");
2335   iobfclose(f);
2336   RefIncr((Ref *)drawerstate.ap);
2337   drawerstate.apseq++;
2338 
2339   /* Install default window position if not already set */
2340   if(drawerstate.defview.win == NULL)
2341     comm_object(windefault,
2342 		&WindowOps, NULL, (Ref **)(void *)&drawerstate.defview.win,
2343 		COMM_NOW);
2344   drawerstate.defview.backcolor = initial_defaultbackcolor;
2345   if(drawerstate.defview.cam == NULL)
2346     drawerstate.defview.cam =
2347       CamCreate(CAM_FOV, 40.0, CAM_NEAR, 0.1, CAM_FAR, 100.0, CAM_END);
2348 
2349   /* Create a not-quite-instantiated window */
2350   drawerstate.defview.mgctx =
2351     mgctxcreate(MG_CAMERA, drawerstate.defview.cam,
2352 		MG_BACKGROUND, &drawerstate.defview.backcolor,
2353 		MG_APPEAR, drawerstate.ap,
2354 		MG_WINDOW, drawerstate.defview.win,
2355 		MG_SHOW, 0,
2356 		MG_END );
2357 
2358   drawerstate.defview.id = DEFAULTCAMID;
2359   drawerstate.defview.lineznudge = 3.;
2360   drawerstate.space = TM_EUCLIDEAN;
2361   drawerstate.pause = 0;
2362   /* drawerstate.motionscale controls the minimum rate of scaled translations;
2363      it should be small, for accurate mouse tracking, but not quite zero,
2364      lest scaled motions do nothing when a moving object coincides with the
2365      center of motion.  (At present, the only scaled translation is Z-motion
2366      in [o]rbit mode.) slevy 93.10.15.
2367   */
2368   drawerstate.motionscale = 0.03;
2369   name_fsa = fsa_initialize(name_fsa, NOID);
2370   fsa_install(name_fsa, "focus",	(void *)FOCUSID);
2371   fsa_install(name_fsa, "default",	(void *)DEFAULTCAMID);
2372   fsa_install(name_fsa, "defaultcam",	(void *)DEFAULTCAMID);
2373   fsa_install(name_fsa, "target",	(void *)TARGETID);
2374   fsa_install(name_fsa, "targetgeom",	(void *)TARGETGEOMID);
2375   fsa_install(name_fsa, "g.",		(void *)TARGETGEOMID);
2376   fsa_install(name_fsa, "targetcam",	(void *)TARGETCAMID);
2377   fsa_install(name_fsa, "targetcamera",	(void *)TARGETCAMID);
2378   fsa_install(name_fsa, "c.",		(void *)TARGETCAMID);
2379   fsa_install(name_fsa, "center",	(void *)CENTERID);
2380   fsa_install(name_fsa, "bbox-center",  (void *)BBOXCENTERID);
2381   fsa_install(name_fsa, "allgeoms",	(void *)ALLGEOMS);
2382   fsa_install(name_fsa, "g*",		(void *)ALLGEOMS);
2383   fsa_install(name_fsa, "allcams",	(void *)ALLCAMS);
2384   fsa_install(name_fsa, "c*",		(void *)ALLCAMS);
2385   fsa_install(name_fsa, "worldgeom",	(void *)WORLDGEOM);
2386   fsa_install(name_fsa, "world",	(void *)WORLDGEOM);
2387   fsa_install(name_fsa, "self",		(void *)SELF);
2388   fsa_install(name_fsa, ".",		(void *)SELF);
2389   fsa_install(name_fsa, "universe",	(void *)UNIVERSE);
2390   fsa_install(name_fsa, "Universe",	(void *)UNIVERSE);
2391   fsa_install(name_fsa, "primitive",	(void *)PRIMITIVE);
2392   /* The following four names are here for backward compatibility
2393    * of the 'write' command with the midsummer '92 version.
2394    */
2395   fsa_install(name_fsa, "bare",		(void *)SELF);
2396   fsa_install(name_fsa, "wrap",		(void *)WORLDGEOM);
2397   fsa_install(name_fsa, "cumulative",	(void *)UNIVERSE);
2398   fsa_install(name_fsa, "cum",		(void *)UNIVERSE);
2399 
2400   dgeom = OOGLNewNE(DGeom *, dgeom_max, "dgeom array");
2401   dview = OOGLNewNE(DView *, dview_max, "view array");
2402   for (i=0; i<dgeom_max; ++i) dgeom[i] = NULL;
2403   for (i=0; i<dview_max; ++i) dview[i] = NULL;
2404 
2405   /*
2406    * Build the structure:
2407    *	drawerstate.world -->	INST  [world modelling & appearance]
2408    *	  == dgeom[0]->Item	  |
2409    *				  |
2410    *	dgeom[0]->Inorm		INST  [world normalizing]
2411    *				  |
2412    *	dgeom[0]->Lgeom --> LIST  --  LIST -- LIST ... [all objects]
2413    */
2414 
2415   dgeom[0] = dg = OOGLNewE(DGeom, "world DGeom");
2416   dg->normalization = EACH;
2417   dg->bboxdraw = 1;
2418   dg->bezdice = 10;
2419   drawer_init_dgeom(dg, GEOMID(0), THEWORLD);
2420   dg->Inorm = GeomCreate("inst", CR_END);
2421   dg->bboxap = ApCreate(AP_DONT, APF_FACEDRAW, AP_DO, APF_EDGEDRAW,
2422 			AP_OVERRIDE, APF_EDGEDRAW,
2423 			AP_MtSet,
2424 			MT_EDGECOLOR,&uistate.pickedbboxcolor,
2425 			MT_OVERRIDE, MTF_EDGECOLOR,
2426 			MT_END,
2427 			AP_END);
2428   drawerstate.world = GeomCreate("inst", CR_GEOM, dg->Inorm, CR_END);
2429   dg->Item = drawerstate.world;
2430   dg->Lgeom = NULL;
2431   TmIdentity(dg->Incr);
2432   name_object(dg, 1, "World");
2433   dg->bboxvalid = false;
2434 
2435   drawerstate.universe = GeomCreate("list", CR_GEOM, drawerstate.world, CR_END);
2436   drawerstate.defview.Item = drawerstate.universe;
2437   GeomDice(dg->Item, dg->bezdice, dg->bezdice);
2438   f = iobfileopen(fmemopen(defaultcam, strlen(defaultcam), "rb"));
2439   drawerstate.camgeom = GeomFLoad(f, "built-in camera geometry");
2440   iobfclose(f);
2441   drawerstate.camproj = 0;
2442 
2443 #ifdef MANIFOLD
2444   drawerstate.nDV = 0;
2445   drawerstate.nMT = 0;
2446   drawerstate.d1 = 1.0;
2447   drawerstate.i1 = 0.5;
2448   verbose_manifold = ( getenv("VERBOSE_MANIFOLD") != NULL );
2449   trivial_visibility = ( getenv("TRIVIAL_VISIBILITY") != NULL );
2450 #endif /* MANIFOLD */
2451 
2452 }
2453 
2454 static void
winmoved(int camid)2455 winmoved(int camid)
2456 {
2457   WnWindow *wn;
2458   DView *dv = (DView *)drawer_get_object(camid);
2459 
2460   if(dv != NULL && dv->mgctx != NULL) {
2461     mgctxselect(dv->mgctx);
2462     mgctxget(MG_WINDOW, &wn);
2463     WnGet(wn, WN_CURPOS, &drawerstate.winpos);
2464   }
2465 }
2466 
2467 
nonidentity(Transform T)2468 static bool nonidentity(Transform T)
2469 {
2470   return memcmp(T, TM_IDENTITY, sizeof(Transform)) != 0;
2471 }
2472 
2473 static DGeom *
new_dgeom(char * from,enum citizenship citizenship)2474 new_dgeom(char *from, enum citizenship citizenship)
2475 {
2476   DGeom *dg;
2477   Geom *g = NULL;
2478   Handle *h = NULL;
2479   int i;
2480 
2481   if(from != NULL)
2482     (void) comm_object(from, &GeomOps, &h, (Ref **)(void *)&g, COMM_LATER);
2483 
2484   for (i=0; i<dgeom_max && dgeom[i]!=NULL; ++i);
2485   if (i==dgeom_max) {
2486     int j;
2487     dgeom = OOGLRenewNE(DGeom *, dgeom, (dgeom_max += dgeom_max-1), "enlarge DGeom array");
2488     for (j=i; j<dgeom_max; ++j) dgeom[j] = NULL;
2489   }
2490   dg = dgeom[i] = OOGLNewE(DGeom, "new_dgeom dg");
2491 
2492   drawer_init_dgeom(dg, GEOMID(i), citizenship);
2493 
2494   dg->bboxdraw = dgeom[0]->bboxdraw;
2495   dg->Lgeom = GeomCreate("list", CR_HANDLE_GEOM, h, g, CR_END);
2496   dg->Lbbox = GeomCreate("list", CR_NOCOPY, CR_APPEAR,
2497 			 dg->bboxap=ApCopy(dgeom[0]->bboxap, NULL), CR_END);
2498   ListAppend(dg->Lgeom, dg->Lbbox);
2499   dg->Inorm = GeomCreate("inst", CR_NOCOPY, CR_GEOM, dg->Lgeom, CR_END);
2500   dg->Item = GeomCreate("inst", CR_NOCOPY, CR_GEOM, dg->Inorm, CR_END);
2501   dg->changed = CH_GEOMETRY;
2502   dg->pickable = true;
2503   switch (citizenship) {
2504   case ORDINARY:
2505     if(dgeom[0]->Lgeom == NULL)
2506       dgeom[0]->Lgeom = GeomCreate("list", CR_NOCOPY,
2507 				   CR_GEOM, dg->Item, CR_END);
2508     else
2509       ListAppend( dgeom[0]->Lgeom, dg->Item );
2510     GeomReplace( dgeom[0]->Inorm, dgeom[0]->Lgeom );
2511     break;
2512   case ALIEN:
2513     ListAppend(drawerstate.universe, dg->Item);
2514     break;
2515   default:
2516     break; /* cannot happen, shut-off a compiler warning */
2517   }
2518 
2519   return dg;
2520 }
2521 
2522 void
drawer_init_dgeom(DGeom * dg,int id,enum citizenship citizenship)2523 drawer_init_dgeom(DGeom *dg, int id, enum citizenship citizenship)
2524 {
2525   dg->id = id;
2526   dg->citizenship = citizenship;
2527   TmIdentity(dg->Incr);
2528   dg->incrhandle = NULL;
2529   dg->updateproc = NULL;
2530   dg->moving = 0;
2531   dg->bezdice = dgeom[0]->bezdice;
2532   dg->name[0] = NULL;
2533   dg->name[1] = NULL;
2534   name_object(dg, 0, new_object_name(T_GEOM));
2535   dg->bboxdraw = dgeom[0]->bboxdraw;
2536   dg->normalization = dgeom[0]->normalization;
2537   dg->bboxvalid = false;
2538   dg->changed = CH_NOTHING;
2539   dg->redraw = false;
2540   dg->seqno = 0;
2541   dg->NDT = NULL;
2542   dg->NDTinv = NULL;
2543   dg->bbox_center = NOID;
2544 #if 0
2545   GeomSet(dg->Item, CR_NDAXIS, dg->NDT, CR_END);
2546 #endif
2547 }
2548 
2549 /*-----------------------------------------------------------------------
2550  * Function:	drawer_make_bbox
2551  * Description:	Make a new bounding box.
2552  * Args:	*dg:   parent DGeom
2553  *		combine: flag; if non-zero, takes union of old & new bboxes;
2554  *			     if zero, fits bbox tightly about object.
2555  * Returns:
2556  * Author:	munzner
2557  * Date:	Tue Dec 10 20:42:20 1991
2558  * Notes:	Geom must be non-NULL.
2559  *              If old bbox exists is deleted, since new one always created.
2560  *              Default appearance: draw black edges, no faces.
2561  *		Assumes (if 'combine') that Lbbox list includes Lgeom list.
2562  */
2563 void
drawer_make_bbox(DGeom * dg,bool combine)2564 drawer_make_bbox(DGeom *dg, bool combine)
2565 {
2566   Geom *bbox;
2567 
2568   /* Don't create bbox for world dgeom */
2569   if (dg->id == WORLDGEOM) {
2570     return;
2571   }
2572 
2573   if (!dg->bboxvalid) {
2574     if(!combine) {
2575       GeomReplace(dg->Lbbox, NULL);
2576     }
2577     bbox =
2578       GeomBound(dg->Lgeom, drawerstate.NDim > 0 ? NULL : TM_IDENTITY, NULL);
2579     if (bbox) {
2580       GeomReplace(dg->Lbbox, bbox);
2581       GeomDelete(bbox);		/* Only Lbbox needs it now */
2582       dg->bboxvalid = true;
2583       if (dg->bbox_center != NOID) {
2584 	/* Delete the old center object */
2585 	gv_delete(dg->bbox_center);
2586 	dg->bbox_center = NOID;
2587       }
2588     }
2589   }
2590 }
2591 
2592 /*-----------------------------------------------------------------------
2593  * Function:	_name_object
2594  * Description:	name an object
2595  * Args:	*obj: the object to be named
2596  *		ni: name index --- 0 or 1
2597  *		*name: object's name name
2598  * Returns:	nothing
2599  * Author:	mbp
2600  * Date:	Thu Dec  5 18:12:22 1991
2601  * Notes:	Don't call this procedure directly.  Use the
2602  *		'name_object'
2603  *		macro defined above; it casts
2604  *		its first argument (a DGeom* or a DView*) to Object*.
2605  */
2606 static char *
_name_object(DObject * obj,int ni,char * name)2607 _name_object(DObject *obj, int ni, char *name)
2608 {
2609   char *tail;
2610 
2611   /* uninstall the old name, if any */
2612   if (obj->name[ni] != NULL) {
2613     fsa_install( name_fsa, obj->name[ni], NOID );
2614     OOGLFree(obj->name[ni]);
2615     obj->name[ni] = NULL;
2616   }
2617 
2618   /* get tail if full pathname */
2619   tail = strrchr(name, '/');
2620   if (tail) tail++;
2621   else tail = name;
2622 
2623 
2624   /* install the new name */
2625   obj->name[ni] = unique_name(tail, obj->id);
2626   if ( ni==0 && strcmp(obj->name[ni], tail)!=0 ) {
2627     OOGLError(0,"warning: attempt to use existing first name: \"%s\"\n\
2628   using \"%s\" instead", tail, obj->name[ni]);
2629   }
2630   fsa_install( name_fsa, obj->name[ni], (void *)(long)obj->id );
2631   return obj->name[ni];
2632 }
2633 
2634 static DView *
new_dview()2635 new_dview()
2636 {
2637   DView *dv;
2638   char window_name[64], *view_name;
2639   int i;
2640   WnPosition wp;
2641 
2642   for (i=0; i<dview_max && dview[i]!=NULL; ++i);
2643   if (i==dview_max) {
2644     int j;
2645     dview = OOGLRenewNE(DView *, dview, (dview_max += dview_max-1),
2646 			"enlarge DView array");
2647     for (j=i; j<dview_max; ++j) dview[j] = NULL;
2648   }
2649   dv = dview[i] = OOGLNewE(DView, "new view");
2650   dv->id = CAMID(i);
2651   view_name = new_object_name(T_CAM);
2652   dv->cam = CamCopy(drawerstate.defview.cam, NULL);
2653   CamSet(dv->cam, CAM_SPACE, drawerstate.space, CAM_END);
2654   dv->win = WnCopy(drawerstate.defview.win);
2655   dv->backcolor = drawerstate.defview.backcolor;
2656   dv->camhandle = NULL;
2657   RefIncr((Ref*)(drawerstate.universe));
2658   dv->Item = drawerstate.universe;
2659   dv->extradraw = NULL;
2660   dv->apseq = drawerstate.apseq;
2661   TmIdentity(dv->Incr);
2662   dv->incrhandle = NULL;
2663   dv->updateproc = NULL;
2664   dv->moving = false;
2665   dv->changed = CH_NOTHING;
2666   dv->frozen = UNFROZEN;
2667   dv->seqno = 0;
2668   dv->lineznudge = drawerstate.defview.lineznudge;
2669   dv->newcam = true;
2670   dv->name[0] = NULL;
2671   dv->name[1] = NULL;
2672   name_object(dv, 0, view_name);
2673   sprintf(window_name, "geomview %s", view_name);
2674   dv->cameradraw = false;
2675   dv->hsphere = NULL;
2676   dv->hmodel = VIRTUAL;
2677   dv->stereo = NO_KEYWORD;
2678   dv->stereogap = 0;
2679   dv->shader = NULL;
2680   dv->mgctx =
2681     mgctxcreate(MG_CAMERA, dv->cam,
2682 		MG_BACKGROUND, &dv->backcolor,
2683 		MG_APPEAR, drawerstate.ap,
2684 		MG_WINDOW, dv->win,
2685 		MG_SHOW, 0,	/* Don't show window until we set its name */
2686 		MG_END );
2687   if(WnGet(dv->win, WN_PREFPOS, &wp) > 0) {
2688     wp.xmin += 40; wp.ymin -= 40; wp.xmax += 40; wp.ymax -= 40;
2689     WnSet(drawerstate.defview.win, WN_PREFPOS, &wp, WN_END);
2690   }
2691   if(i >= nwins)
2692     nwins = i+1;
2693   for(i = 0; i < 3; i++) dv->NDPerm[i] = i+1;
2694   dv->NDPerm[3] = 0;
2695   dv->nNDcmap = 0;
2696   dv->cluster = NULL;
2697   for(i = 0; i < MAXCMAP; i++) {
2698     dv->NDcmap[i].axis = NULL;
2699     VVINIT(dv->NDcmap[i].cents, cent, 5);
2700   }
2701   return dv;
2702 }
2703 
2704 static void
updateit(float dt)2705 updateit(float dt)
2706 {
2707   int i;
2708   DView *dv;
2709   DGeom *dg;
2710   Geom *g;
2711 
2712   do_motion(dt);
2713   for(i = 0; i < dgeom_max; i++) {
2714     if((dg = dgeom[i]) == NULL)
2715       continue;
2716     if (dg->updateproc) {
2717       GeomGet(dg->Lgeom, CR_GEOM, &g);
2718       (*(dg->updateproc))(g);
2719     } else {
2720       GeomTransform(dg->Item, dg->Incr, NULL);
2721     }
2722   }
2723   LOOPVIEWS(i,dv) {
2724     if (dv->updateproc)
2725       (*(dv->updateproc))(dv->cam);
2726     else
2727       CamTransform(dv->cam, dv->Incr);
2728   }
2729 }
2730 
2731 static void
draw_view(DView * dv)2732 draw_view(DView *dv)
2733 {
2734   /* Colors for red/cyan stereo */
2735   static int lefteyemask = MGO_NOGREEN|MGO_NOBLUE;	/* Red */
2736   static int righteyemask = MGO_NORED;			/* Green+Blue */
2737   Appearance *ap;
2738 
2739   if((dv->frozen == UNFROZEN) &&
2740      (dv->redraw || (dv->changed != CH_NOTHING) || dv->newcam)) {
2741     mgctxselect(dv->mgctx);
2742     mgctxset(MG_CAMERA, dv->cam, MG_BACKGROUND, &dv->backcolor,
2743 	     MG_ZNUDGE, dv->lineznudge * 1.0e-5,
2744 	     MG_UNSETOPTIONS,
2745 	     MGO_NORED|MGO_NOGREEN|MGO_NOBLUE|MGO_INHIBITCLEAR,
2746 	     MG_END);
2747     if(drawerstate.apseq > dv->apseq) {
2748       mgsetappearance(drawerstate.ap, MG_SET);
2749       dv->apseq = drawerstate.apseq;
2750     }
2751     ap = GeomAppearance(dv->Item);
2752     if(ap)
2753       mgsetappearance(ap, MG_MERGE);
2754 
2755     if(dv->newcam)
2756       reconfigure_view(dv);
2757     if(dv->stereo != NO_KEYWORD) {
2758       mgctxset(MG_WnSet, WN_VIEWPORT, &dv->vp[1], WN_END,
2759 	       MG_CamSet, CAM_STEREOEYE, 1, CAM_END,
2760 	       MG_SETOPTIONS, MGO_INHIBITSWAP, MG_END);
2761       if(dv->stereo == COLORED_KEYWORD)
2762 	mgctxset(MG_SETOPTIONS, lefteyemask, MG_END);
2763       really_draw_view(dv);
2764       mgctxset(MG_WnSet, WN_VIEWPORT, &dv->vp[0], WN_END,
2765 	       MG_CamSet, CAM_STEREOEYE, 0, CAM_END,
2766 	       MG_UNSETOPTIONS, MGO_INHIBITSWAP|lefteyemask,
2767 	       MG_END);
2768       if(dv->stereo == COLORED_KEYWORD) {
2769 	/* In Iris GL, clearing clears everything, including the
2770 	 * left-eye image we just drew.  In OpenGL, it doesn't.
2771 	 * So *only* if we're doing GL drawing, select MGO_INHIBITCLEAR
2772 	 * along with the right-eye image planes.
2773 	 */
2774 	mgctxset(MG_SETOPTIONS,
2775 		 _mgf.mg_devno == MGD_GL ? righteyemask|MGO_INHIBITCLEAR
2776 		 : righteyemask,
2777 		 MG_END);
2778       }
2779     }
2780     really_draw_view(dv);
2781     dv->changed = CH_NOTHING;
2782     dv->redraw = false;
2783   }
2784 }
2785 
2786 /*
2787  * Reconfigure view.  Assumes the proper window is already selected.
2788  */
2789 static void
reconfigure_view(DView * dv)2790 reconfigure_view(DView *dv)
2791 {
2792   WnWindow *win = NULL;
2793   WnPosition wp, gap;
2794   int xsize, ysize;
2795   int isstereo = 1;
2796 
2797   mgctxget(MG_WINDOW, &win);
2798   WnGet(win, WN_CURPOS, &wp);
2799 
2800   xsize = wp.xmax - wp.xmin + 1;
2801   ysize = wp.ymax - wp.ymin + 1;
2802   dv->vp[0].xmin = dv->vp[0].ymin = 0;
2803   dv->vp[0].xmax = xsize-1;
2804   dv->vp[0].ymax = ysize-1;
2805   dv->vp[1] = dv->vp[0];
2806   gap = dv->vp[0];
2807 
2808   switch(dv->stereo) {
2809   case NO_KEYWORD:
2810     isstereo = 0;
2811     break;
2812   case HORIZONTAL_KEYWORD:
2813     dv->vp[0].xmax = (xsize - dv->stereogap) / 2;
2814     dv->vp[1].xmin = dv->vp[0].xmax + dv->stereogap;
2815     gap.xmin = dv->vp[0].xmax + 1;
2816     gap.xmax = dv->vp[1].xmin - 1;
2817     break;
2818   case VERTICAL_KEYWORD:
2819     dv->vp[0].ymax = (ysize - dv->stereogap) / 2;
2820     dv->vp[1].ymin = dv->vp[0].ymax + dv->stereogap;
2821     gap.ymin = dv->vp[0].ymax + 1;
2822     gap.ymax = dv->vp[1].ymin - 1;
2823     break;
2824   case COLORED_KEYWORD:
2825     /* Viewports identical, stereo enabled. */
2826     break;
2827   default:
2828     break;
2829   }
2830   if(dv->stereogap > 0) {
2831     mgctxset(MG_WnSet, WN_VIEWPORT, &gap, WN_END, MG_END);
2832     mgworldbegin(); mgworldend();	/* Force clear */
2833     mgworldbegin(); mgworldend();	/* ... of both buffers */
2834   }
2835   mgctxset(MG_WnSet, WN_VIEWPORT, &dv->vp[0], WN_END,
2836 	   MG_CamSet, CAM_STEREO, isstereo, CAM_END, MG_END);
2837   mgreshapeviewport();
2838   dv->newcam = false;
2839   if(dv->id == CAMID(uistate.mousefocus))
2840     winmoved(dv->id);
2841 }
2842 
2843 static void
really_draw_view(DView * dv)2844 really_draw_view(DView *dv)
2845 {
2846 #if 0
2847 #include <sys/time.h>
2848   static int cnt;
2849   static struct timeval tv_old[1];
2850 
2851   if (cnt % 100 == 0) {
2852     struct timeval tv_new[1];
2853     double elapsed;
2854 
2855     gettimeofday(tv_new, NULL);
2856 
2857     if (cnt > 0) {
2858       elapsed  = (double)(tv_new->tv_sec - tv_old->tv_sec);
2859       elapsed += (double)(tv_new->tv_usec - tv_old->tv_usec) / 1e6;
2860       fprintf(stderr, "%d frames in %e seconds, fps = %e\n",
2861 	      cnt, elapsed, (double)cnt/elapsed);
2862       cnt = 0;
2863     }
2864     *tv_old = *tv_new;
2865   }
2866 
2867   cnt++;
2868 #endif
2869 
2870   mgpushappearance();
2871   mgworldbegin();
2872 
2873   if(dv->cluster == NULL && drawerstate.NDim > 0) {
2874     /* If we're in N-D mode but this camera isn't equipped,
2875      * give it a default N-D => 3-D projection now.
2876      */
2877     gv_ND_axes(dv->name[0],
2878 	       drawerstate.NDcams ? drawerstate.NDcams->name : "default",
2879 	       1, 2, 3, 0);
2880   }
2881 
2882   if(dv->cluster != NULL) {
2883     /* ND-drawing, works also with scenes; the (scene ...) command
2884      * takes care of attaching all cameras of a cluster to the same
2885      * scene.
2886      */
2887     NDcam *cluster = dv->cluster;
2888     int dim = TmNGetSize(cluster->C2W, NULL,NULL);
2889     NDstuff *nds;
2890     TransformN *W2C = NULL, *W2U = NULL;
2891 
2892     cluster->W2C = TmNInvert(cluster->C2W, cluster->W2C);
2893     W2C = TmNCopy(cluster->W2C, NULL);
2894     W2U = TmNIdentity(TmNCreate(dim, dim, NULL));
2895 
2896     nds = drawer_init_ndstuff(dv, W2C, W2U);
2897     drawer_transform_ndstuff(nds, NULL);
2898     mgctxset(MG_NDCTX, nds, MG_END);
2899 
2900     GeomDraw(dv->Item);
2901 
2902     /* draw other camera's if requested; take their respective
2903      * ND-transform into account.
2904      */
2905     if (drawerstate.camgeom != NULL && dv->cameradraw) {
2906       DView *otherv;
2907       int otheri;
2908       Transform T3;
2909       TransformN *ovC2W = NULL;
2910 
2911       LOOPVIEWS(otheri, otherv) {
2912 	if (otherv != dv && otherv->Item == dv->Item) {
2913 
2914 	  if(drawerstate.camproj) {
2915 	    /* Mmmh. ND?? */
2916 	    Transform Tt;
2917 	    CamView(otherv->cam, Tt);
2918 	    TmInvert(Tt, T3);
2919 	  } else {
2920 	    CamGet(otherv->cam, CAM_C2W, T3);
2921 	  }
2922 
2923 	  if (otherv->cluster && otherv->cluster->C2W) {
2924 	    ovC2W = TmNCopy(otherv->cluster->C2W, ovC2W);
2925 	  } else {
2926 	    ovC2W = ovC2W
2927 	      ? TmNIdentity(ovC2W)
2928 	      : TmNIdentity(TmNCreate(drawerstate.NDim,
2929 				      drawerstate.NDim, NULL));
2930 	  }
2931 	  TmNApplyT3TN(T3, otherv->NDPerm, ovC2W);
2932 	  TmNMap(ovC2W, otherv->NDPerm, ovC2W);
2933 
2934 	  drawer_transform_ndstuff(nds, ovC2W);
2935 	  GeomDraw(drawerstate.camgeom);
2936 
2937 	  /* we assume that cameras have no translucent component */
2938 	}
2939       }
2940 
2941       TmNDelete(ovC2W);
2942     }
2943 
2944     TmNDelete(W2U);
2945     TmNDelete(W2C);
2946     drawer_destroy_ndstuff(nds);
2947 
2948     mgctxset(MG_NDCTX, NULL, MG_END);
2949   } else {
2950     /* Normal case.  Just draw whatever's attached to this camera */
2951     GeomDraw(dv->Item);
2952 
2953     /* new */
2954     if (dv->hsphere != NULL) {
2955       /* if we're in conformal ball model, switch to projective
2956 	 model for drawing the sphere at infinity, then switch back */
2957 
2958       int mgspace;
2959 
2960       mgctxget(MG_SPACE, &mgspace);
2961       if (dv->hmodel == CONFORMALBALL) {
2962 	mgctxset(MG_SPACE, TM_SPACE(mgspace) | TM_PROJECTIVE, MG_END);
2963 	GeomDraw(dv->hsphere);
2964 	mgctxset(MG_SPACE, mgspace, MG_END);
2965       } else {
2966 	GeomDraw(dv->hsphere);
2967       }
2968     }
2969 
2970     /* drawing of other cameras in ND has been handled above */
2971     if (drawerstate.camgeom &&
2972 	dv->cameradraw && dv->Item == drawerstate.universe) {
2973       DView *otherv;
2974       int otheri;
2975       Transform T, Tt;
2976       LOOPVIEWS(otheri,otherv) {
2977 	if (otherv != dv && otherv->Item == drawerstate.universe) {
2978 	  mgpushtransform();
2979 	  if(drawerstate.camproj) {
2980 	    CamView(otherv->cam, Tt);
2981 	    TmInvert(Tt, T);
2982 	  } else
2983 	    CamGet(otherv->cam, CAM_C2W, T);
2984 	  mgtransform(T);
2985 	  GeomDraw(drawerstate.camgeom);
2986 	  mgpoptransform();
2987 	}
2988       }
2989     }
2990   }
2991 
2992   if (dv->extradraw) (dv->extradraw)(dv);
2993 
2994   mgworldend();
2995   mgpopappearance();
2996 }
2997 
2998 static char *
new_object_name(int type)2999 new_object_name(int type)
3000 {
3001   static char name[16];
3002   int i;
3003 
3004   for(i = 0; ; i++) {
3005     sprintf(name, "%c%d", type == T_GEOM ? 'g' : 'c', i);
3006     if(drawer_idbyname(name) == NOID)
3007       return name;
3008   }
3009 }
3010 
3011 /*-----------------------------------------------------------------------
3012  * Function:	ordinal
3013  * Description:	extract the ordinal number of a DView or DGeom from its name
3014  * Args:	*name: DView or DGeom's name --- assumed to be of the form
3015  *		  "x<number>", where x is any char
3016  * Returns:	<number>
3017  * Author:	mbp
3018  * Date:	Sat Nov 30 22:42:54 1991
3019  */
3020 /*
3021   static int
3022   ordinal(char *name)
3023   {
3024   return( atoi( &name[1] ) );
3025   }
3026 */
3027 
3028 static void
object_changed(Handle ** hp,DObject * obj,void * seqno)3029 object_changed(Handle **hp, DObject *obj, void *seqno)
3030 {
3031   if(obj->seqno != (long)seqno) {
3032     /* Obsolete reference -- remove this Handle callback */
3033     HandleUnregisterJust(hp, (Ref*)obj, seqno, object_changed);
3034   } else {
3035     obj->changed = CH_GEOMETRY;
3036   }
3037 }
3038 
3039 static int
object_register(Handle ** hp,Ref * thing,DObject * o)3040 object_register(Handle **hp, Ref *thing, DObject *o)
3041 {
3042   HandleRegister(hp, (Ref *)o, (void *)(long)(int)o->seqno, object_changed);
3043   return 0;
3044 }
3045 
3046 /*
3047  * Something has changed in this dgeom's geometry.
3048  * Discard and (if necessary) recompute bounding box.
3049  * In any case, scan for Handles.
3050  */
3051 static void
update_dgeom(DGeom * dg)3052 update_dgeom(DGeom *dg)
3053 {
3054   if(dg->id == WORLDGEOM) {
3055     return;
3056   }
3057   if(dg->changed/* & CH_GEOMETRY*/) {
3058     dg->bboxvalid = false;
3059     HandleUnregisterAll((Ref *)dg, (void *)(long)(int)dg->seqno, object_changed);
3060     dg->seqno++;
3061     /*
3062      * Find all Handles in this DGeom, and a callback for each.
3063      */
3064     GeomHandleScan(dg->Item, object_register, dg);
3065   }
3066   if(dg->bboxdraw || dg->normalization != NONE) {
3067     drawer_make_bbox(dg, dg->normalization == ALL);
3068   }
3069   ApSet(dg->bboxap, dg->bboxdraw ? AP_DO : AP_DONT, APF_EDGEDRAW,
3070 	AP_OVERRIDE, APF_EDGEDRAW & uistate.apoverride, AP_END);
3071   GeomDice(dg->Item, dg->bezdice, dg->bezdice);
3072   normalize(dg, dg->normalization);
3073 }
3074 
3075 static void
update_view(DView * v)3076 update_view(DView *v)
3077 {
3078   v->seqno++;
3079   if(v->Item != drawerstate.universe)
3080     GeomHandleScan(v->Item, object_register, v);
3081   CamHandleScan(v->cam, object_register, v);
3082 }
3083 
3084 static void
normalize(DGeom * dg,int normalization)3085 normalize(DGeom *dg, int normalization)
3086 {
3087   float cx,cy,cz,cs,r,dx,dy,dz;
3088   HPoint3 min,max;
3089   Geom *bbox = NULL;
3090   Transform tmp;
3091 
3092   dg->normalization = normalization;
3093   if(dg->id == WORLDGEOM) return;
3094   switch(normalization) {
3095   case NONE:
3096     GeomTransformTo(dg->Inorm, NULL, NULL);
3097     break;
3098 
3099   case EACH:
3100   case ALL:
3101     if (!dg->bboxvalid) {
3102       drawer_make_bbox(dg, normalization == ALL);
3103     }
3104     GeomGet(dg->Lbbox, CR_GEOM, &bbox);
3105     if (bbox != NULL) {
3106       BBoxMinMax((BBox*)bbox, &min, &max);
3107       cx = (max.x + min.x) * .5;
3108       cy = (max.y + min.y) * .5;
3109       cz = (max.z + min.z) * .5;
3110       dx = max.x - min.x;
3111       dy = max.y - min.y;
3112       dz = max.z - min.z;
3113       r = sqrt(dx*dx + dy*dy + dz*dz);
3114       cs = (r == 0) ? 1 : 2.0/r;
3115       TmScale(tmp,cs,cs,cs);
3116       CtmTranslate(tmp,-cx,-cy,-cz);
3117       GeomTransformTo(dg->Inorm, tmp, NULL);
3118     }
3119     break;
3120     /* case KEEP: Leave normalization transform alone */
3121   }
3122   dg->redraw = true;
3123 }
3124 
3125 void
drawer_nuke_world()3126 drawer_nuke_world()
3127 {
3128   gv_delete( ALLGEOMS );
3129 }
3130 
3131 void
drawer_nuke_cameras(int keepzero)3132 drawer_nuke_cameras(int keepzero)
3133 {
3134   int i,i0;
3135 
3136   i0 = keepzero ? 1 : 0;
3137   for (i=i0; i<dview_max;  ++i)
3138     gv_delete( CAMID(i) );
3139 }
3140 
3141 LDEFINE(exit, LVOID,
3142 	"(exit)\n\
3143 	Terminates geomview.")
3144 {
3145   LDECLARE(("exit", LBEGIN,
3146 	    LEND));
3147   ui_cleanup();
3148   exit(0);
3149   return Lt;
3150 }
3151 
3152 /*-----------------------------------------------------------------------
3153  * Function:	unique_name
3154  * Description:	return a unique object name with a given prefix
3155  * Args:	*name: the prefix
3156  * Returns:
3157  * Author:	mbp
3158  * Date:	Wed Jan 22 18:05:26 1992
3159  * Notes:
3160  */
3161 static char *
unique_name(char * name,int id)3162 unique_name(char *name, int id)
3163 {
3164   char newname[256];
3165   int i;
3166 
3167   if ( fsa_parse( name_fsa, name ) == NOID )
3168     return strdup(name);
3169 
3170   i = INDEXOF(id);
3171   do {
3172     sprintf(newname, "%.240s<%d>", name, i++);
3173   } while(fsa_parse(name_fsa, newname) != NOID);
3174   return strdup(newname);
3175 }
3176 
3177 #ifdef MANIFOLD
3178 
3179 int
drawer_read_manifold(char * file)3180 drawer_read_manifold(char *file)
3181 {
3182   FILE *fp = fopen(file, "rb");
3183   int i,n;
3184 
3185   setlinebuf(stderr);
3186 
3187   if (fp == NULL) return 0;
3188 
3189   n = fgetni(fp, 1, &(drawerstate.nMT), 0);
3190   if (n != 1) {
3191     OOGLError(0,"%1d", n);
3192     return 0;
3193   }
3194   if (verbose_manifold)
3195     fprintf(stderr, "mfd: nMT = %1d\n", drawerstate.nMT);
3196 
3197   n = fgettransform(fp, drawerstate.nMT, (float*)(drawerstate.MT), 0);
3198   if (n != drawerstate.nMT) {
3199     OOGLError(0,"%1d", n);
3200     return 0;
3201   }
3202   if (verbose_manifold) {
3203     fprintf(stderr, "mfd: MT[] = ");
3204     fputtransform(stderr, drawerstate.nMT, (float*)(drawerstate.MT), 0);
3205   }
3206 
3207   n = fgetni(fp, 1, &(drawerstate.nDV), 0);
3208   if (n != 1) {
3209     OOGLError(0,"%1d", n);
3210     return 0;
3211   }
3212   if (verbose_manifold) {
3213     fprintf(stderr, "mfd: nDV = %1d\n", drawerstate.nDV);
3214   }
3215 
3216   n = fgetnf(fp, drawerstate.nDV*4, (float*)(drawerstate.DV), 0);
3217   if (n != drawerstate.nDV*4) {
3218     OOGLError(0,"%1d", n);
3219     return 0;
3220   }
3221   if (verbose_manifold) {
3222     fprintf(stderr, "mfd: DV[] = ");
3223     for (i=0; i<drawerstate.nDV; ++i) {
3224       fprintf(stderr, "  %f  %f  %f  %f\n",
3225 	      drawerstate.DV[i].x,
3226 	      drawerstate.DV[i].y,
3227 	      drawerstate.DV[i].z,
3228 	      drawerstate.DV[i].w);
3229     }
3230   }
3231 
3232   fclose(fp);
3233   return 1;
3234 }
3235 
3236 /*-----------------------------------------------------------------------
3237  * Function:	visible
3238  * Description:	test whether a convex polyhedron is visible
3239  * Args:	T: complete world->NDC transform
3240  *		v: list of vertices of polyhedron
3241  *		n: number of vertices in list v
3242  * Returns:	0 or 1
3243  * Author:	mbp
3244  * Date:	Thu Aug  6 13:43:02 1992
3245  * Notes:	Uses an axial bounding box test: is any part of the
3246  *		the axial bounding box of the projections of the
3247  *		vertices v visible?  This is guaranteed to accept all
3248  *		visible polyhedra, and may accept some invisible ones
3249  *		too.
3250  */
3251 int
visible(Transform T,HPoint3 * v,int n)3252 visible(Transform T, HPoint3 *v, int n)
3253 {
3254 #define MIN -2
3255 #define MAX  2
3256   float x,y,z;
3257   float
3258     xmin = MAX,
3259     xmax = MIN,
3260     ymin = MAX,
3261     ymax = MIN,
3262     zmin = MAX,
3263     zmax = MIN;
3264   int i;
3265   Point3 pv;
3266 
3267   if (trivial_visibility) return 1;
3268 
3269   for (i=0; i<n; ++i) {
3270     HPt3TransPt3( T, &v[i], &pv );
3271     if (pv.x < xmin) xmin = pv.x;
3272     else if (pv.x > xmax) xmax = pv.x;
3273     if (pv.y < ymin) ymin = pv.y;
3274     else if (pv.y > ymax) ymax = pv.y;
3275     if (pv.z < zmin) zmin = pv.z;
3276     else if (pv.z > zmax) zmax = pv.z;
3277   }
3278 
3279 #if 0
3280   printf("x: [%f\t, %f]\n", xmin, xmax);
3281   printf("y: [%f\t, %f]\n", ymin, ymax);
3282   printf("z: [%f\t, %f]\n", zmin, zmax);
3283 #endif
3284 
3285   /* if entire polyhedron is outside clipping region, reject */
3286   if (zmax <= -1 || zmin >= 1) return 0;
3287   if (xmax <= -1 || xmin >= 1) return 0;
3288   if (ymax <= -1 || ymin >= 1) return 0;
3289 
3290   /* otherwise accept */
3291   return 1;
3292 #undef MAX
3293 #undef MIN
3294 }
3295 
intensity(float d,float d1,float i1)3296 float intensity(float d, float d1, float i1)
3297 {
3298   float val, d12;
3299 
3300   d12 = d1*d1;
3301   val = i1 * d12 / ( (1 - i1)*d*d + i1*d12 );
3302   return val;
3303 }
3304 
3305 #endif /* MANIFOLD */
3306 
hmodelval(char * s,int val)3307 static HModelValue hmodelval(char *s, int val)
3308 {
3309   switch (val) {
3310   case VIRTUAL_KEYWORD: return VIRTUAL;
3311   case PROJECTIVE_KEYWORD: return PROJECTIVE;
3312   case CONFORMALBALL_KEYWORD: return CONFORMALBALL;
3313   default:
3314     fprintf(stderr, "%s: invalid model keyword (assuming \"virtual\")\n",s);
3315     return VIRTUAL;
3316   }
3317 }
3318 
3319 
hmodelkeyword(char * s,HModelValue val)3320 Keyword hmodelkeyword(char *s, HModelValue val)
3321 {
3322   switch (val) {
3323   case VIRTUAL: return VIRTUAL_KEYWORD;
3324   case PROJECTIVE: return PROJECTIVE_KEYWORD;
3325   case CONFORMALBALL: return CONFORMALBALL_KEYWORD;
3326   default:
3327     fprintf(stderr, "%s: invalid model (assuming \"virtual\")\n",s);
3328     return VIRTUAL_KEYWORD;
3329   }
3330 }
3331 
3332 
3333 
normalval(char * s,int val)3334 static int normalval(char *s, int val)
3335 {
3336   switch (val) {
3337   case NONE_KEYWORD: return NONE;
3338   case EACH_KEYWORD: return EACH;
3339   case KEEP_KEYWORD: return KEEP;
3340   case ALL_KEYWORD: return ALL;
3341   default:
3342     fprintf(stderr, "%s: invalid normalization type (assuming \"none\")\n",s);
3343     return NONE;
3344   }
3345 }
3346 
3347 /*-----------------------------------------------------------------------
3348  * Function:	spaceof
3349  * Description:	return the space of an object
3350  * Args:	id: the object
3351  * Returns:	TM_EUCLIDEAN, TM_HYPERBOLIC, or TM_SPHERICAL
3352  * Author:	mbp
3353  * Date:	Sun Dec 13 22:28:58 1992
3354  * Notes:	We need to be able to tell what space an object is in,
3355  *		in order to know what kinds of transformations to
3356  *		apply to it (any maybe for other reasons, e.g.
3357  *		shading).  OOGL cameras know about what space they're
3358  *		in.  OOGL geoms do not (at this time, anyway).  The
3359  *		main rationale for this is that the concept of "space"
3360  *		applies to a whole tree of geoms; every geom in the
3361  *		tree is in the same space.  The way geomview currently
3362  *		deals with this is to use "drawerstate.space" to hold
3363  *		the space designation for the world.  All
3364  *		space-sensitive procedures should check the space of
3365  *		an id by calling spaceof(), however, rather than
3366  *		referencing drawerstate.space directly.  This is
3367  *		because at some point we may generalize to having more
3368  *		than one world or some other mechanism for keeping
3369  *		track of what space things are in.
3370  */
3371 int
spaceof(int id)3372 spaceof(int id)
3373 {
3374   DView *dv;
3375   int space;
3376   if (ISCAM(id)) {
3377     dv = (DView*)drawer_get_object(id);
3378     if (dv == NULL) {
3379       OOGLError(0,"spaceof: unknown camera id %1d (assuming TM_EUCLIDEAN)\n",
3380 		id);
3381       return TM_EUCLIDEAN;
3382     }
3383     CamGet(dv->cam, CAM_SPACE, &space);
3384     return space;
3385   }
3386   return drawerstate.space;
3387 }
3388 
3389 
3390 
spaceval(char * s,int val)3391 static int spaceval(char *s, int val)
3392 {
3393   switch (val) {
3394   case EUCLIDEAN_KEYWORD: return TM_EUCLIDEAN;
3395   case HYPERBOLIC_KEYWORD: return TM_HYPERBOLIC;
3396   case SPHERICAL_KEYWORD: return TM_SPHERICAL;
3397   default:
3398     fprintf(stderr, "%s: invalid space keyword (assuming \"euclidean\")\n",s);
3399     return EUCLIDEAN;
3400   }
3401 }
3402 
3403 
3404 /*-----------------------------------------------------------------------
3405  * Function:	scaleof
3406  * Description:	return a number indicating the scale of an object
3407  * Args:	id: the object
3408  * Returns:	the scale number
3409  * Author:	mbp
3410  * Date:	Thu Feb 11 11:10:19 1993
3411  */
scaleof(int id)3412 float scaleof(int id)
3413 {
3414   float scale = 1.0;
3415 
3416   if (ISCAM(id)) {
3417     DView *dv;
3418     dv = (DView*)drawer_get_object(id);
3419     if (dv == NULL) {
3420       OOGLError(0,"scaleof: unknown camera id %1d (returning scale = 1)\n",
3421 		id);
3422     } else {
3423       /* for cameras return the focal length */
3424       CamGet(dv->cam, CAM_FOCUS, &scale);
3425     }
3426   } else {
3427     DGeom *dg;
3428     HPoint3 min, max;
3429     BBox *bbox;
3430 
3431     dg = (DGeom*)drawer_get_object(id);
3432     if (dg == NULL) {
3433       OOGLError(0,"scaleof: unknown geom id %1d (returning scale = 1)\n",
3434 		id);
3435     } else {
3436       /* for geoms return (geometric mean of 1 + bbox side lengths) - 1*/
3437       GeomGet(dg->Lbbox, CR_GEOM, &bbox);
3438       if (bbox != NULL) {
3439 	BBoxMinMax((BBox*)bbox, &min, &max);
3440 	scale = pow( fabs((double)((max.x-min.x+1)
3441 				   *(max.y-min.y+1)
3442 				   *(max.z-min.z+1))), .333 ) - 1;
3443       }
3444     }
3445   }
3446   return scale;
3447 }
3448 
3449 LDEFINE(set_motionscale, LVOID,
3450 	"(set-motionscale X)\n\
3451 	Set the motion scale factor to X (default value 0.5).  These\n\
3452 	commands scale their motion by an amount which depends on the\n\
3453 	distance from the frame to the center and on the size of the\n\
3454 	frame.  Specifically, they scale by\n\
3455 	        dist + scaleof(frame) * motionscale\n\
3456 	where dist is the distance from the center to the frame and\n\
3457 	motionscale is the motion scale factor set by this function.\n\
3458 	Scaleof(frame) measures the size of the frame object.")
3459 {
3460   float scale;
3461   LDECLARE(("set-motionscale", LBEGIN,
3462 	    LFLOAT, &scale,
3463 	    LEND));
3464   drawerstate.motionscale = scale;
3465   return Lt;
3466 }
3467 
3468 
3469 LDEFINE(set_conformal_refine, LVOID,
3470 	"(set-conformal-refine CMX [N [SHOWEDGES]])\n\
3471 	Sets the parameters for the refinement algorithm used in drawing\n\
3472 	in the conformal model.  CMX is the cosine of the maximum angle\n\
3473 	an edge can bend before it is refined.  Its value should be between\n\
3474 	-1 and 1; the default is 0.95; decreasing its value will cause less\n\
3475 	refinement.  N is the maximum number of iterations of refining;\n\
3476 	the default is 6.  SHOWEDGES, which should be \"no\" or \"yes\",\n\
3477 	determines whether interior edges in the refinement are drawn.")
3478 {
3479   float cmb;
3480   int maxsteps = -1;
3481   int showedges = -1;
3482   extern void set_cm_refine(double cm_cmb, int cm_mr, int cm_ss);
3483 
3484   LDECLARE(("set-conformal-refine", LBEGIN,
3485 	    LFLOAT, &cmb,
3486             LOPTIONAL,
3487 	    LINT, &maxsteps,
3488 	    LKEYWORD, &showedges,
3489             LEND));
3490   set_cm_refine((double)cmb,maxsteps,showedges);
3491   return Lt;
3492 }
3493 
3494 static void
traverse(Pool * p,Geom * where,int * pickpath,int * curpath,int * curbase,int pn)3495 traverse(Pool *p, Geom *where, int *pickpath, int *curpath,
3496 	 int *curbase, int pn)
3497 {
3498   Geom *new;
3499   char *name;
3500   int depth;
3501 
3502   if (!where) return;
3503   name = GeomName(where);
3504   if (!strcmp(name, "comment")) {
3505     /* check if our current path matches the pickpath
3506        decrement the depth since we don't match the last level */
3507     depth = curpath - curbase -1;
3508     while (depth >= 0){
3509       if (curbase[depth] != pickpath[depth]) break;
3510       if (depth == 0) {
3511 	/* we're set */
3512 	fprintf(PoolOutputFile(p), "{");
3513 	CommentExport((Comment*)where, p);
3514 	fprintf(PoolOutputFile(p), "}");
3515       }
3516       depth--;
3517     }
3518   } else if (!strcmp(name, "list")) {
3519     /* push down a level */
3520     curpath++;
3521     while (where) {
3522       /* traverse the list.
3523 	 the cdr of a list is a list, but don't descend another level */
3524       GeomGet(where, CR_CAR, &new);
3525       if (new) {
3526 	(*curpath)++;
3527 	traverse(p, new, pickpath, curpath, curbase, pn);
3528       }
3529       GeomGet(where, CR_CDR, &where);
3530     }
3531     /* pop up a level */
3532     *curpath = -1;
3533     curpath--;
3534   } else if (!strcmp(name, "inst")) {
3535     GeomGet(where, CR_GEOM, &new);
3536     if (new) {
3537       /* push down a level */
3538       curpath++;
3539       (*curpath)++;
3540       traverse(p, new, pickpath, curpath, curbase, pn);
3541       /* pop up a level */
3542       *curpath = -1;
3543       curpath--;
3544     }
3545   }
3546 }
3547 
3548 #if 0
3549 static void
3550 drawer_write_comments(char *fname, int id, int *pickpath, int pn)
3551 {
3552 }
3553 #endif
3554 
3555 LDEFINE(write_comments, LVOID,
3556 	"(write-comments FILENAME GEOMID PICKPATH)\n\
3557 	write OOGL COMMENT objects in the GEOMID hierarchy at the\n\
3558  	level of the pick path to FILENAME. Specifically, COMMENTS\n\
3559  	at level (a b c ... f g) will match pick paths of the form\n\
3560  	(a b c ... f *) where * includes any value of g, and also\n\
3561  	any values of possible further indices h,i,j, etc. The pick\n\
3562  	path (returned in the \"pick\" command) is a list of\n\
3563  	integer counters specifying a subpart of a hierarchical\n\
3564  	OOGL object. Descent into a complex object (LIST or INST)\n\
3565  	adds a new integer to the path. Traversal of simple objects\n\
3566  	increments the counter at the current level.\n\
3567  	Individual COMMENTS are enclosed by curly braces, and the\n\
3568  	entire string of zero, one, or more COMMENTS (written in\n\
3569  	the order in which they are encountered during hierarchy\n\
3570  	traversal) is enclosed by parentheses.\n\
3571         \n\
3572         Note that arbitrary data can only be passed through the OOGL\n\
3573  	libraries as full-fledged OOGL COMMENT objects, which can be\n\
3574  	attached to other OOGL objects via the LIST type as described\n\
3575  	above. Ordinary comments in OOGL files (i.e. everything after\n\
3576  	'#' on a line) are ignored at when the file is loaded and\n\
3577  	cannot be returned.")
3578 
3579 {
3580   int id;
3581   int pickpath[40];
3582   int pn = 40;
3583   char *fname;
3584   Pool *p, *op;
3585   Geom *where;
3586   int count;
3587   bool temppool = false;
3588   int curpath[40];
3589   Lake *brownie;
3590 
3591   LDECLARE(("write-comments", LBEGIN,
3592 	    LLAKE, &brownie,
3593 	    LSTRING, &fname,
3594 	    LID, &id,
3595 	    LHOLD, LARRAY, LINT, pickpath, &pn,
3596 	    LEND));
3597 
3598   /* The Lake is a dummy argument that gives us access to the
3599      right output file pointer */
3600   p = POOL(brownie);
3601 
3602   if (fname[0] == '-') {
3603     if (PoolOutputFile(p)) {
3604       op = p;
3605     } else {
3606       op = PoolStreamTemp(fname, NULL, stdout, 1, &GeomOps);
3607       temppool = true;
3608     }
3609   } else {
3610     op = PoolStreamTemp(fname, NULL, NULL, 1, &GeomOps);
3611     temppool = true;
3612   }
3613   if(op == NULL || PoolOutputFile(op) == NULL) {
3614     fprintf(stderr, "write: cannot open \"%s\": %s\n", fname, sperror());
3615     return Lnil;
3616   }
3617 
3618   for (count=0; count < 40; count++) {
3619     curpath[count] = -1;
3620   }
3621 
3622   /* get to the right starting place */
3623   GeomGet(((DGeom *)drawer_get_object(id))->Lgeom, CR_GEOM, &where);
3624   fprintf(PoolOutputFile(op), "( ");
3625   traverse(op, where, pickpath, curpath-1, curpath, pn);
3626   fprintf(PoolOutputFile(op), ")\n");
3627   fflush(PoolOutputFile(op));
3628   if (temppool) {
3629     PoolClose(op);
3630     PoolDelete(op);
3631   }
3632 
3633   return Lt;
3634 }
3635 
3636 /*
3637  * Local Variables: ***
3638  * c-basic-offset: 2 ***
3639  * End: ***
3640  */
3641