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