1 /* Copyright (C) 1992-1998 The Geometry Center
2 * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3 *
4 * This file is part of Geomview.
5 *
6 * Geomview is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * Geomview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with Geomview; see the file COPYING. If not, write
18 * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
19 * USA, or visit http://www.gnu.org.
20 */
21
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #if 0
27 static char copyright[] = "Copyright (C) 1992-1998 The Geometry Center\n\
28 Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips";
29 #endif
30
31
32 /* Authors: Stuart Levy, Tamara Munzner, Mark Phillips */
33
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <string.h>
38 #include <stdlib.h>
39
40 #include "mg.h"
41
42 #include "drawer.h"
43 #include "event.h"
44 #include "lang.h"
45 #include "ui.h"
46 #include "comm.h"
47 #include "pickP.h"
48 #include "transform.h"
49 #include "streampool.h"
50 #include "lights.h"
51 #include "mouse.h"
52 #include "rman.h"
53
54 #define DOUBLECLICKTIME 333 /* millisecs between double clicks */
55
56 EventState estate; /* External motion state */
57
58 struct button button; /* Shift, etc. button state */
59
60 void emit_pick(int pickedid, Pick *pick);
61 static int view_pick( DView *dv, int x, int y, Pick *pick );
62
63 #define ESC '\033'
64
65 struct num {
66 int has; /* 0 if no number, or -1 (negative) or +1 (positive) */
67 int val;
68 int expon;
69 } number, onum;
70
71 static int keyshorts = 1;
72 static int pickon = 1;
73 static int prefixid = NOID;
74 static float getreal(float defaultvalue);
75 static int getint(int defaultvalue);
76 /*static int toggle(int val);*/
77 static void tog_ap_flag(int id, int flagbit);
78 static int retarget(int defaultid);
79
80 #define SEQ(prefix, ch) ((prefix)<<8 | (ch))
81
82 static enum { KEYGEOM=0, KEYCAM=1, KEYNONE=2 } keymode = KEYNONE;
83 static int prefix = 0;
84
85 static char Help[] = "\
86 Keyboard commands apply while cursor is in any graphics window and most \n\
87 control panels. Most commands allow one of the following selection prefixes \n\
88 (if none is provided the command applies to the current object): \n\
89 g world geom g# #'th geom g* All geoms\n\
90 c current camera c# #'th camera c* All cameras\n\
91 Many allow a numeric prefix: if none they toggle or reset current value.\n\
92 Appearance:\n\
93 Draw: Shading: Other:\n\
94 af Faces 0as Constant av eVert normals: always face viewer\n\
95 ae Edges 1as Flat #aw Line Width (pixels)\n\
96 an Normals 2as Smooth #ac edges Closer than faces(try 5-100)\n\
97 ab Bounding Boxes 3as Smooth, non-lighted al Shade lines\n\
98 aV Vectors aT allow transparency at Texture-mapping\n\
99 Color: aC allow concave polygons aq Texture quality\n\
100 Cf Ce Cn Cb CB face/edge/normal/bbox/backgnd\n\
101 Motions: Viewing:\n\
102 r rotate [ Leftmouse=X-Y plane, 0vp Orthographic view\n"
103 #ifdef NeXT
104 " t translate Alt-Left=Z axis, 1vp Perspective view\n"
105 #else
106 " t translate Middle=Z axis, 1vp Perspective view\n"
107 #endif
108 " z zoom FOV Shift=slow motion, vd Draw other views' cameras\n\
109 f fly in r/t modes. ] #vv field of View\n\
110 o orbit [Left=steer, Middle=speed ] #vn near clip distance\n\
111 s scale #vf far clip distance\n\
112 w/W recenter/all v+ add new camera\n\
113 h/H halt/halt all vx cursor on/off\n\
114 @ select center of motion (e.g. g3@) vb backfacing poly cull on/off\n\
115 #vl focal length\n\
116 L Look At object v~ Software shading on/off\n\
117 show Panel: Pm Pa Pl Po main/appearance/lighting/obscure\n\
118 Pt Pc PC Pf tools/cameras/Commands/file-browser\n\
119 Ps P- saving/read commands from tty\n\
120 Lights: ls le Show lights / Edit Lights\n\
121 Metric: me mh ms Euclidean Hyperbolic Spherical\n\
122 Model: mv mp mc Virtual Projective Conformal\n\
123 Other:\n\
124 N normalization < Pf load geom/command file\n\
125 0N none > Ps save something to file ui motion has inertia\n\
126 1N each TV NTSC mode toggle uc constrained (X/Y) motion\n\
127 2N all uo motion in Own coord system\n\
128 Rightmouse-doubleclick pick as current target object\n\
129 Shift-Rightmouse pick interest (center) point\n"
130 #ifdef NeXT
131 " Alt-Leftmouse is synonym for Rightmouse.\n"
132 #endif
133 ;
134
135 void
print_help()136 print_help()
137 {
138 printf("\n%s", Help);
139 rman_do('?', 0,0);
140 fflush(stdout);
141 }
142
143 void
event_init()144 event_init()
145 {
146 estate.motionproc = NULL;
147 }
148
149 LDEFINE(event_mode, LVOID,
150 "(event-mode MODESTRING)\n\
151 Set the mouse event (motion) mode; MODESTRING should be one of\n\
152 the strings that appears in the motion mode browser (including\n\
153 the keyboard shortcut, e.g. \"[r] Rotate\").")
154 {
155 static Event ev_enter = { EMODEENTER, 0, 0, 0, 0 };
156 static Event ev_exit = { EMODEEXIT, 0, 0, 0, 0 };
157 char *modename;
158
159 LDECLARE(("event-mode", LBEGIN,
160 LSTRING, &modename,
161 LEND));
162
163 if ( estate.motionproc != NULL ) {
164 estate.motionproc(&ev_exit);
165 }
166 estate.motionproc =
167 uistate.modes[uistate.mode_current = ui_mode_index(modename) ];
168 D1PRINT(("gv_event_mode: estate.motionproc <- %1x\n", estate.motionproc));
169 ui_event_mode( modename );
170 if ( estate.motionproc != NULL ) {
171 estate.motionproc(&ev_enter);
172 }
173 return Lt;
174 }
175
176
177
178 /*
179 * Report time elapsed since the epoch (or since the program began if
180 * since == NULL). Possibly remember the current time in "nextepoch".
181 * Time is measured in floating-point seconds.
182 */
183 float
elapsed(float * since,float * nextepoch)184 elapsed(float *since, float *nextepoch)
185 {
186 static struct timeval t0 = { 0, 0 };
187 struct timeval tnow;
188 float now = 0;
189 float sincetime = 0;
190
191 gettimeofday(&tnow, NULL);
192 if(t0.tv_sec == 0) {
193 t0 = tnow;
194 tnow.tv_usec++;
195 }
196 now = tnow.tv_sec - t0.tv_sec + 1e-6*(tnow.tv_usec - t0.tv_usec);
197 if(since) {
198 if((sincetime = *since) == 0)
199 sincetime = *since = now;
200 }
201 if(nextepoch) *nextepoch = now;
202 return now - sincetime;
203 }
204
205 LDEFINE(pick_invisible, LVOID,
206 "(pick-invisible [yes|no])\n\
207 Selects whether picks should be sensitive to objects whose appearance\n\
208 makes them invisible; default yes.\n\
209 With no arguments, returns current status.")
210 {
211 Keyword kw = NOT_A_KEYWORD;
212
213 LDECLARE(("pick-invisible", LBEGIN,
214 LOPTIONAL,
215 LKEYWORD, &kw,
216 LEND));
217
218 if(kw == NOT_A_KEYWORD) {
219 return uistate.pick_invisible ? Lt : Lnil;
220 }
221
222 uistate.pick_invisible = boolval("pick_invisible", kw);
223
224 return Lt;
225 }
226
227 LDEFINE(rawevent, LVOID,
228 "(rawevent dev val x y t)\n\
229 Enter the specified raw event into the event queue. The\n\
230 arguments directly specify the members of the event structure\n\
231 used internally by geomview. This is the lowest level event\n\
232 handler and is not intended for general use.")
233 /*
234 This used to be dispatch_event().
235 */
236 {
237 int id;
238 int apfl;
239 DrawerKeyword k;
240 int err = 0;
241 float v;
242 Appearance *ap;
243 DView *dv;
244 DGeom *dg;
245 char *s;
246
247 Event event;
248 LDECLARE(("rawevent", LBEGIN,
249 LINT, &event.dev,
250 LINT, &event.val,
251 LINT, &event.x,
252 LINT, &event.y,
253 LLONG, &event.t,
254 LEND));
255
256 PRINT_EVENT(("in gv_rawevent", &event));
257
258 /*
259 * Call the current motion proc, if any. This proc returns 1 if it
260 * used the event, in which case we don't do any further processing.
261 */
262 D1PRINT(("gv_rawevent: estate.motionproc = %x\n", estate.motionproc));
263 if ( estate.motionproc != NULL ) {
264 D1PRINT(("gv_rawevent: calling estate.motionproc\n"));
265 if ( estate.motionproc(&event) ) {
266 D1PRINT(("gv_rawevent: returning Lt\n"));
267 return Lt;
268 }
269 D1PRINT(("gv_rawevent: falling through\n"));
270 }
271
272 /* The rightmouse and doubleclick is now hardcoded but should be
273 bindable through lang later, through a control mechanism similar to
274 current motionproc stuff */
275
276 if (event.dev == ERIGHTMOUSE) {
277 if ((event.val > 0) && pickon) {
278 static unsigned long int lastt = 0;
279 Pick *pick = PickSet(NULL, PA_WANT,
280 uistate.pick_invisible ? PW_EDGE|PW_VERT|PW_FACE
281 : PW_VISIBLE|PW_EDGE|PW_VERT|PW_FACE,
282 PA_END);
283 int pickedid = view_pick( (DView *)drawer_get_object(FOCUSID),
284 event.x, event.y, pick );
285
286 if(button.shift) {
287 /* Could change FOCUSID here to ALLCAMS,
288 * to force setting everyone's focal length to
289 * their distance from the pick.
290 */
291 if(pickedid != NOID)
292 make_center_from_pick("CENTER", pick, FOCUSID);
293 else
294 gv_ui_center(TARGETID);
295 } else {
296 if(pickedid != NOID)
297 emit_pick(pickedid, pick);
298 if (event.t - lastt < DOUBLECLICKTIME) {
299 lastt = 0;
300 gv_ui_target( pickedid!=NOID ? pickedid : WORLDGEOM, IMMEDIATE );
301 }
302 }
303 PickDelete(pick);
304 lastt = event.t;
305 }
306 }
307
308 if(!isascii(event.dev))
309 return Lt;
310
311
312 if (!keyshorts) /* are keyboard shortcuts on? :-) */
313 return Lt; /* no? then we don't want to process them... */
314
315 /* Only keyboard events from here on down */
316
317 ui_keyboard(event.dev);
318 if(event.dev >= '0' && event.dev <= '9') {
319 if(!number.has) {
320 number.val = 0;
321 number.has = 1;
322 }
323 number.val = 10*number.val + (event.dev - '0');
324 if(number.expon) number.expon++;
325 prefix = 0;
326 } else {
327
328 id = GEOMID(uistate.targetgeom);
329
330 rescan:
331
332 switch(SEQ(prefix, event.dev)) {
333
334 case '-':
335 case '*':
336 number.has = -1;
337 number.expon = 0;
338 number.val = 0;
339 prefix = 0;
340 goto keepmode;
341
342 case '.':
343 number.expon = 1;
344 prefix = 0;
345 goto keepmode;
346
347
348 case 'g': keymode = KEYGEOM; goto gotmode; /* Select geom: 'g' prefix */
349 case 'c': keymode = KEYCAM; goto gotmode; /* Select camera: 'c' prefix */
350 gotmode:
351 onum = number;
352 number.has = 0;
353 prefix = 0;
354 goto keepmode;
355
356 case 'p':
357 {
358 int id;
359
360 if (pickon) {
361 if (keymode == KEYNONE) {
362 id = gv_rawpick(FOCUSID, event.x, event.y);
363 if (id == NOID) id = WORLDGEOM;
364 } else {
365 id = retarget(NOID);
366 }
367
368 gv_ui_target(id, IMMEDIATE);
369 }
370 }
371
372 case '@':
373 gv_ui_center(retarget(uistate.centerid));
374 break;
375
376 case 'N':
377 id = retarget(GEOMID(uistate.targetgeom));
378 if(!number.has) {
379 dg = (DGeom *)drawer_get_object(id);
380 if(dg) number.val = dg->normalization == NONE ? EACH : NONE;
381 }
382 drawer_int(id, DRAWER_NORMALIZATION, number.val);
383 break;
384
385 case '<':
386 s = "Load"; number.val = 1; goto pickpanel; /* Load file */
387 case '>':
388 s = "Save"; number.val = 1; goto pickpanel; /* Save State */
389
390 /* use bounding box center as CENTER position */
391 case 'B':
392 gv_ui_center_origin(uistate.bbox_center
393 ? ORIGIN_KEYWORD : BBOX_CENTER_KEYWORD);
394 break;
395
396 /* Halt current object */
397 case 'h':
398 drawer_stop(retarget(uistate.targetid)); break;
399
400 case 'H': /* Halt Everything */
401 drawer_stop(NOID); break;
402
403 case 'w': /* Recenter current thing */
404 drawer_center(retarget(uistate.targetid)); break;
405
406 case 'W': /* Recenter (and halt) Everything */
407 drawer_center(NOID); break;
408
409 case 'L':
410 gv_look(retarget(uistate.targetid),FOCUSID);
411 break;
412
413 /*
414 * r/t/z/f/o apply to the currently selected object unless target specified.
415 */
416 case 'f': s = OBJFLY; goto mote;
417 case 'o': s = OBJORBIT; goto mote;
418 case 'r': s = OBJROTATE; goto mote;
419 case 't': s = OBJTRANSLATE; goto mote;
420 case 'z': s = OBJZOOM; goto mote;
421 case 's': s = OBJSCALE; goto mote;
422
423 mote:
424 id = retarget(NOID);
425 if (id) gv_ui_target( id, IMMEDIATE);
426 gv_event_mode( s );
427 break;
428
429 case '?':
430 print_help();
431 break;
432
433 #ifdef sgi
434 case 'T': /* NTSC */
435 #endif
436 case 'v': /* view-related prefix */
437 case 'a': /* appearance-related prefix */
438 case 'm': /* metric (euclidean/hyperbolic/spherical) */
439 case 'l': /* light-related prefix */
440 case 'd': /* delete */
441 case 'R': /* renderman */
442 case 'C': /* color-pick */
443 case 'P': /* panel show */
444 case 'u': /* motion style */
445 case ESC: /* quit prefix */
446 if(keymode != KEYNONE) {
447 prefixid = retarget(NOID);
448 if(ISGEOM(prefixid))
449 gv_ui_target( prefixid, IMMEDIATE);
450 }
451 prefix = event.dev;
452 goto keepnumber;
453
454 case SEQ('P','m'):
455 case SEQ('P','g'): s = "main"; goto pickpanel;
456 case SEQ('P','a'): s = "Appearance"; goto pickpanel;
457 case SEQ('P','o'): s = "Obscure"; goto pickpanel;
458 case SEQ('P','l'): s = "Lighting"; goto pickpanel;
459 case SEQ('P','C'): s = "Command"; goto pickpanel;
460 case SEQ('P','c'): s = "Camera"; goto pickpanel;
461 case SEQ('P','t'): s = "Tools"; goto pickpanel;
462 case SEQ('P','f'): s = "Files"; goto pickpanel;
463 case SEQ('P','s'): s = "Save"; goto pickpanel;
464 case SEQ('P','M'): s = "Materials"; goto pickpanel;
465 case SEQ('P','A'): s = "Credits"; goto pickpanel;
466 pickpanel:
467 ui_showpanel(ui_name2panel(s), getint(-1));
468 break;
469 case SEQ('P','P'):
470 ui_manual_browser("pdf");
471 break;
472 case SEQ('P','H'):
473 ui_manual_browser("html");
474 break;
475 case SEQ('P','-'):
476 comm_object("(read command < -)", &CommandOps, NULL, NULL, COMM_LATER);
477 break;
478
479
480 case SEQ(ESC,ESC):
481 gv_exit();
482 /*NOTREACHED*/
483
484 case SEQ('C','f'): k = DRAWER_DIFFUSE; goto pickcolor;
485 case SEQ('C','e'): k = DRAWER_EDGECOLOR; goto pickcolor;
486 case SEQ('C','n'): k = DRAWER_NORMALCOLOR; goto pickcolor;
487 case SEQ('C','b'): k = DRAWER_BBOXCOLOR; goto pickcolor;
488 case SEQ('C','v'):
489 case SEQ('C','B'): k = DRAWER_BACKCOLOR; goto pickcolor;
490 pickcolor:
491 ui_pickcolor( k );
492 break;
493
494 case SEQ('u','i'): k = DRAWER_INERTIA; goto motstyle;
495 case SEQ('u','c'): k = DRAWER_CONSTRAIN; goto motstyle;
496 case SEQ('u','o'): k = DRAWER_OWNMOTION;
497 motstyle:
498 drawer_int( WORLDGEOM, k, getint(-1) );
499 break;
500
501 case SEQ('v','+'): /* Add camera */
502 { CameraStruct cs;
503 id = retarget(FOCUSID);
504 dv = ISCAM(id) ? (DView *)drawer_get_object(id) : NULL;
505 cs.h = NULL;
506 cs.cam = dv && dv->cam ? CamCopy(dv->cam, NULL) : NULL;
507 gv_new_camera(NULL, &cs);
508 }
509 break;
510
511 case SEQ('v','p'): /* Projection: orthographic or perspective */
512 id = retarget(FOCUSID);
513 if(!number.has) {
514 dv = (DView *)drawer_get_object(id);
515 if(dv) CamGet(dv->cam, CAM_PERSPECTIVE, &number.val);
516 number.val = !number.val;
517 }
518 drawer_int( id, DRAWER_PROJECTION, number.val );
519 break;
520
521 case SEQ('v','d'): /* toggle "Draw other cameras" */
522 id = retarget(FOCUSID);
523 if(!number.has) {
524 dv = (DView *)drawer_get_object(id);
525 number.val = dv ? !dv->cameradraw : true;
526 }
527 drawer_int( id, DRAWER_CAMERADRAW, number.val );
528 break;
529
530 case SEQ('v','D'): /* Toggle dithering */
531 id = retarget(FOCUSID);
532 gv_dither(id, TOGGLE_KEYWORD);
533 break;
534
535 /* stuff for X11 version */
536 case SEQ('v','h'): /* pick hidden surface removal method: */
537 id = retarget(FOCUSID);
538 dv = (DView *)drawer_get_object(id);
539 if(dv == NULL || dv->mgctx == NULL)
540 break;
541 if (!number.has) {
542 mgctxselect(dv->mgctx);
543 mgctxget(MG_DEPTHSORT, &number.val);
544 number.val = (number.val+1) % 3;
545 }
546 mgctxset(MG_DEPTHSORT,
547 number.val>=0&&number.val<=2 ? number.val : 2, MG_END);
548 gv_redraw(dv->id);
549 ui_maybe_refresh(dv->id);
550 break;
551 /* end of stuff for X11 version */
552
553 case SEQ('v','x'): /* Toggle/enable/disable cursor */
554 ui_curson( number.has ? number.val : -1 );
555 break;
556
557 case SEQ('v','b'):
558 tog_ap_flag( id, APF_BACKCULL );
559 break;
560
561 case SEQ('v','s'):
562 id = retarget(FOCUSID);
563 number.val = !number.val; /* "1vs" => single-buffered */
564 drawer_int( id, DRAWER_DOUBLEBUFFER, getint(-1) );
565 break;
566
567 /* For testing software shading */
568 case SEQ('v','~'):
569 id = retarget(FOCUSID);
570 gv_soft_shader(id,
571 number.has ? (number.val?ON_KEYWORD:OFF_KEYWORD) : TOGGLE_KEYWORD);
572 break;
573
574
575 /* Viewing options */
576 case SEQ('a','c'):
577 case SEQ('v','c'): k = DRAWER_LINE_ZNUDGE; v = 10.; goto setcam;
578 case SEQ('v','v'): k = DRAWER_FOV; v = 45.; goto setcam;
579 case SEQ('v','n'): k = DRAWER_NEAR; v = .1; goto setcam;
580 case SEQ('v','f'): k = DRAWER_FAR; v = 100.; goto setcam;
581 case SEQ('v','l'): k = DRAWER_FOCALLENGTH; v = 3.; goto setcam;
582 setcam:
583 drawer_float( retarget(FOCUSID), k, getreal(v) );
584 break;
585
586 /* Might add others here, e.g. a viewfinder mode. */
587
588 /* Metrics / Models */
589 case SEQ('m','e'):
590 gv_space(EUCLIDEAN_KEYWORD);
591 break;
592 case SEQ('m','h'):
593 gv_space(HYPERBOLIC_KEYWORD);
594 break;
595 case SEQ('m','s'):
596 gv_space(SPHERICAL_KEYWORD);
597 break;
598 case SEQ('m','v'):
599 gv_hmodel(retarget(FOCUSID), VIRTUAL_KEYWORD);
600 break;
601 case SEQ('m','p'):
602 gv_hmodel(retarget(FOCUSID), PROJECTIVE_KEYWORD);
603 break;
604 case SEQ('m','c'):
605 gv_hmodel(retarget(FOCUSID), CONFORMALBALL_KEYWORD);
606 break;
607
608 /* Appearance settings */
609 case SEQ('a','f'): apfl = APF_FACEDRAW; goto togapflag;
610 case SEQ('a','e'): apfl = APF_EDGEDRAW; goto togapflag;
611 case SEQ('a','l'): apfl = APF_SHADELINES; goto togapflag;
612 case SEQ('a','n'): apfl = APF_NORMALDRAW; goto togapflag;
613 case SEQ('a','v'): apfl = APF_EVERT; goto togapflag;
614 case SEQ('a','t'): apfl = APF_TEXTURE; goto togapflag;
615 case SEQ('a','T'): apfl = APF_TRANSP; goto togapflag;
616 case SEQ('a','V'): apfl = APF_VECTDRAW; goto togapflag;
617 case SEQ('a','C'): apfl = APF_CONCAVE; goto togapflag;
618 case SEQ('a','q'): apfl = APF_TXMIPMAP|APF_TXMIPINTERP|APF_TXLINEAR; goto togapflag;
619 togapflag:
620 tog_ap_flag( id, apfl );
621 break;
622 case SEQ('a','x'): drawer_set_ap( id, NULL, NULL ); break;
623 case SEQ('a','o'): gv_ap_override( getint( !uistate.apoverride ) ); break;
624
625
626 case SEQ('a','b'): /* Bounding box drawing */
627 if(!number.has) {
628 DGeom *dg = (DGeom *)drawer_get_object( id );
629 if(dg) number.val = !dg->bboxdraw;
630 }
631 drawer_int(id, DRAWER_BBOXDRAW, number.val);
632 break;
633
634 case SEQ('a','s'): /* Shading */
635 if(!number.has) {
636 ap = drawer_get_ap(id);
637 ApGet(ap, AP_SHADING, &number.val);
638 ApDelete(ap);
639 number.val++;
640 }
641 drawer_int(id, DRAWER_SHADING, number.val % 5);
642 break;
643
644 case SEQ('a','w'): /* line width */
645 if(!number.has) {
646 ap = drawer_get_ap(id);
647 ApGet(ap, AP_LINEWIDTH, &number.val);
648 ApDelete(ap);
649 number.val = (number.val > 1) ? 1 : 2;
650 }
651 drawer_int(id, DRAWER_LINEWIDTH, number.val);
652 break;
653
654 /* Scale normals */
655 case SEQ('a','h'): drawer_float(id, DRAWER_NORMSCALE, getreal(1.0)); break;
656
657 /* Patch dicing */
658 case SEQ('a','d'): drawer_int( id, DRAWER_BEZDICE, number.val ); break;
659
660 /* hyperbolic sphere at infinity */
661 case SEQ('a', 'i'): drawer_int( retarget(FOCUSID), DRAWER_HSPHERE,
662 getint(-1) );
663 break;
664
665 /* Delete */
666 case SEQ('d','d'): gv_delete(uistate.targetid); break;
667
668 /* NTSC */
669 #ifdef sgi
670 case SEQ('T','V'): ntsc(getint(-1)); break;
671 #endif
672
673 /* Timing -- ctrl-T
674 * ^T : print accumulated timing status now
675 * <nnn>^T : print timing status now and every <nnn> main-loop cycles
676 * -^T : quit timing
677 */
678 case 'T'&0x1f:
679 timing( number.has<0 ? 0 : number.has ? number.val : 9999999 );
680 break;
681
682 /* Edit Lights */
683 case SEQ('l','e'):
684 if (!(uistate.lights_shown)) light_edit_mode(1);
685 else gv_event_mode( LIGHTEDIT );
686 break;
687
688 /* Toggle Show Lights */
689 case SEQ('l','s'): light_edit_mode(2); break;
690
691 /*
692 * All R* commands moved to rman.c - slevy.
693 */
694 default:
695 err = EOF;
696 if(prefix == 'R') {
697 rman_do(event.dev,number.has,number.val);
698 break;
699 } else if(prefix != 0) { /* No such command? */
700 prefix = 0;
701 goto rescan; /* Try same char without prefix */
702 }
703 keymode = KEYNONE;
704 }
705 number.has = number.expon = onum.has = onum.expon = 0;
706 prefix = 0;
707 prefixid = NOID;
708 ui_keyboard(err); /* 0 = OK, EOF = -1 = error */
709 keepnumber:
710 keymode = KEYNONE;
711 keepmode:
712 ;
713 }
714
715 return Lt;
716 }
717
718 /*
719 * Interpret a g[N] or c[N] prefix; return the id.
720 */
721 static int
retarget(int defindex)722 retarget(int defindex)
723 {
724 int t;
725 static int allid[2] = { ALLGEOMS, ALLCAMS };
726 static char ch[2] = { 'g', 'c' };
727 char code[12];
728
729 if(keymode == KEYNONE) {
730 if(number.expon && !number.has) { /* a "." prefix, sans number */
731 number.expon = 0;
732 return TARGETID;
733 }
734 return prefixid != NOID ?
735 prefixid : defindex; /* No prefix, or just numeric */
736 }
737
738 sprintf(code, "%c%d", ch[keymode], number.val);
739 if(number.has > 0) t = drawer_idbyname(code);
740 else if(number.has < 0) t = allid[keymode];
741 else t = (keymode == KEYGEOM) ? WORLDGEOM
742 : FOCUSID;
743 number = onum;
744 onum.has = onum.expon = 0;
745 keymode = KEYNONE;
746 return t;
747 }
748
749 /* Return current number if any; otherwise return given default value. */
750
751 static float
getreal(float defval)752 getreal(float defval)
753 {
754 float v = number.has * number.val;
755
756 if(!number.has) return defval;
757 while(--number.expon > 0)
758 v *= 0.1;
759 return v;
760 }
761
762 static int
getint(int defaultvalue)763 getint(int defaultvalue)
764 {
765 return number.has ? number.val*number.has : defaultvalue;
766 }
767
768 #if 0
769 static int
770 toggle(int val)
771 {
772 return number.has ? (number.has = number.expon = 0, number.val) : !val;
773 }
774 #endif
775
776 static void
tog_ap_flag(int id,int flagbit)777 tog_ap_flag( int id, int flagbit )
778 {
779 ApStruct as;
780 int val;
781
782 memset(&as, 0, sizeof(as));
783
784 if(number.has) {
785 val = number.val;
786 } else {
787 as.ap = drawer_get_ap(id);
788 val = as.ap ? !(as.ap->flag & flagbit) : 1;
789 }
790 as.ap = ApCreate(val ? AP_DO : AP_DONT, flagbit,
791 AP_OVERRIDE, uistate.apoverride & flagbit, AP_END);
792 gv_merge_ap(id, &as);
793 ApDelete(as.ap);
794 }
795
796
797
798 /* NB - I've put in a total hack to avoid calling gvpick more than
799 * once - I think it's pretty stable (so when it stays in for years
800 * and years it might(?) keep working!) -cf 10/29/92 */
801 static int
view_pick(DView * dv,int x,int y,Pick * pick)802 view_pick( DView *dv, int x, int y, Pick *pick )
803 {
804 Transform V, T, Tnet, Tt, Tmodel, Tnorm, oldTw, Tworld;
805 int i;
806 int chosen = NOID;
807 float xpick, ypick;
808 DGeom *dg;
809 Appearance *ap;
810 WnPosition wp;
811
812 if(dv == NULL)
813 return NOID;
814
815 if(dv->stereo == NO_KEYWORD) {
816 wp = drawerstate.winpos;
817 mousemap(x, y, &xpick, &ypick, &wp);
818 } else {
819 /* Map screen -> view position in a stereo window.
820 */
821 for(i = 0; i < 2; i++) {
822 wp.xmin = drawerstate.winpos.xmin + dv->vp[i].xmin;
823 wp.xmax = wp.xmin + dv->vp[i].xmax - dv->vp[i].xmin;
824 wp.ymin = drawerstate.winpos.ymin + dv->vp[i].ymin;
825 wp.ymax = wp.ymin + dv->vp[i].ymax - dv->vp[i].ymin;
826 mousemap(x, y, &xpick, &ypick, &wp);
827 if(fabs(xpick) <= 1 && fabs(ypick) <= 1)
828 break;
829 }
830 }
831
832 { /* Hacks for setting up transforms so INST ... location / origin works.
833 * This'll be unnecessary if we switch over to using an mg "pick" device.
834 * slevy 96.10.26.
835 */
836 Transform Tc2n, Tw2n, Ts2n;
837 CamViewProjection( dv->cam, Tc2n );
838 CamView( dv->cam, Tw2n );
839 TmTranslate(Ts2n, -1.0, -1.0, 0.0);
840 CtmScale(Ts2n, 2.0/(wp.xmax-wp.xmin+1), 2.0/(wp.ymax-wp.ymin+1), 1.0);
841 PickSet( pick, PA_TC2N, Tc2n, PA_TW2N, Tw2n, PA_TS2N, Ts2n, PA_END );
842 }
843
844 if (drawerstate.NDim > 0) {
845 TransformN *V, *W, *Tnet, *Tmodel, *Tworld;
846
847 V = drawer_ND_CamView(dv, NULL);
848
849 if (dv->Item != drawerstate.universe) {
850 /* Picking in a window with a dedicated Scene */
851 /* We yield results in the Scene's coordinate system */
852 if (GeomMousePick(dv->Item, pick, (Appearance *)NULL,
853 NULL, V, dv->NDPerm, xpick, ypick)) {
854 chosen = dv->id;
855 }
856 return chosen;
857 }
858
859 /* Picking in the real world */
860 Tworld = drawer_get_ND_transform(WORLDGEOM, UNIVERSE);
861 TmNConcat(Tworld, V, V); /* world -> screen */
862 TmNDelete(Tworld);
863
864 /* Now V contains the world -> screen projection */
865 LOOPSOMEGEOMS(i,dg,ORDINARY) {
866 if (dg->pickable) {
867 Geom *g = NULL;
868 int id = GEOMID(i);
869
870 if (dg->Lgeom) {
871 Tmodel = drawer_get_ND_transform(id, WORLDGEOM);
872 Tnet = TmNConcat(Tmodel, V, NULL); /* Now Tnet geom -> screen */
873 GeomGet(dg->Lgeom, CR_GEOM, &g);
874 ap = drawer_get_ap(dg->id);
875 if (GeomMousePick(g, pick, ap, NULL,
876 Tnet, dv->NDPerm, xpick, ypick)) {
877 chosen = id;
878 /* Arrange for things to be in world coords not Dgeom coords. */
879 pick->TwN = TmNConcat(pick->TwN, Tmodel, pick->TwN);
880 W = drawer_get_ND_transform(WORLDGEOM, id);
881 pick->TselfN = TmNConcat(pick->TwN, W, pick->TselfN);
882 TmNDelete(W);
883 }
884 TmNDelete(Tmodel);
885 TmNDelete(Tnet);
886 ApDelete(ap);
887 }
888 }
889 }
890
891 TmNDelete(V);
892
893 return chosen;
894 }
895
896 CamView( dv->cam, V ); /* V = camera-to-screen matrix
897 * cH: wrong. V = universe-to-screen matrix
898 */
899
900 if(dv->Item != drawerstate.universe) {
901 /* Picking in a window with a dedicated Scene */
902 /* We yield results in the Scene's coordinate system */
903 /* Is this really correct? Why should we call GeomPosition() here?
904 * dv->Item is just a normal geometry, only by chance a
905 * single-element INST.
906 */
907 #if 0
908 GeomPosition( dv->Item, T );
909 TmConcat(T,V, T); /* T = Scene to screen projection */
910 if(GeomMousePick( dv->Item, pick, (Appearance *)NULL, T, xpick, ypick )) {
911 chosen = dv->id;
912 }
913 #else
914 if (GeomMousePick( dv->Item, pick, (Appearance *)NULL,
915 V, NULL, NULL, xpick, ypick )) {
916 chosen = dv->id;
917 }
918 #endif
919 return chosen;
920 }
921
922 /* Picking in the real world */
923 GeomPosition(drawerstate.world, Tworld); /* world -> universe */
924 TmConcat(Tworld, V, T); /* world -> screen */
925 /*
926 * We now assume the complete screen -> DGeom transform is in T.
927 * This is true only if we have just a single level of DGeom's in the world.
928 *
929 * cH: this is wrong, T is the World -> screen projection, not the
930 * other way round. (Transforms operate from the right!).
931 */
932 LOOPSOMEGEOMS(i,dg,ORDINARY) {
933 if (dg->pickable) {
934 Geom *g = NULL;
935 int id = GEOMID(i);
936
937 if (dg->Lgeom) {
938 GeomPosition( dg->Item, Tmodel );
939 GeomPosition( dg->Inorm, Tnorm );
940 TmConcat( Tnorm, Tmodel, Tt );
941 TmConcat( Tt, T, Tnet ); /* Now Tnet = complete geom-to-screen proj'n */
942 GeomGet( dg->Lgeom, CR_GEOM, &g );
943 ap = drawer_get_ap(dg->id);
944 if (GeomMousePick( g, pick, ap, Tnet, NULL, NULL, xpick, ypick )) {
945 chosen = id;
946 /* We remember oldTw to print out info below for debugging only */
947 TmCopy(pick->Tw, oldTw);
948 /* This is necessary! Arranges for things to be in world
949 * coords not Dgeom coords. Tt is the dgeom-to-world
950 * transform, Tw is (more or less) screen -> dgeom
951 */
952 TmConcat(pick->Tw, Tt, pick->Tw);
953 drawer_get_transform(WORLDGEOM, pick->Tself, id);
954 TmConcat(pick->Tw, pick->Tself, pick->Tself);
955 }
956 ApDelete(ap);
957 }
958 }
959 }
960
961 /* Ok, everything below is just debugging stuff */
962 if (chosen == NOID) {
963 /* printf("Picked nothing.\n"); */
964 } else {
965 /* printf("Picked dgeom #%d\n", INDEXOF(chosen)); */
966
967 /* pick->got is in mouse coords.
968 wgot is world coords.
969 old world is really dgeom coords. (maybe...)
970 got is raw object coords. (the kind of numbers in geom data file!)
971
972 */
973 if (pick && getenv("VERBOSE_PICK")) {
974 Point3 got, v, e[2], wgot, wv, we[2], owgot, owv, owe[2];
975
976 Pt3Transform(pick->Tmirp, &(pick->got), &got);
977 Pt3Transform(pick->Tw, &(pick->got), &wgot);
978 Pt3Transform(oldTw, &(pick->got), &owgot);
979
980 printf("pick->\n");
981 printf(" got = (%f %f %f)\n",
982 pick->got.x, pick->got.y, pick->got.z);
983 if (pick->found&PW_VERT)
984 printf(" v = (%f %f %f)\n",
985 pick->v.x, pick->v.y, pick->v.z);
986 if (pick->found&PW_EDGE) {
987 printf(" e[0] = (%f %f %f)\n",
988 pick->e[0].x, pick->e[0].y, pick->e[0].z);
989 printf(" e[1] = (%f %f %f)\n",
990 pick->e[1].x, pick->e[1].y, pick->e[1].z);
991 }
992
993 printf("Transformed pick [raw]->\n");
994 printf(" got = (%f %f %f)\n", got.x, got.y, got.z);
995 if (pick->found&PW_VERT) {
996 HPt3TransPt3(pick->Tmirp, &(pick->v), &v);
997 printf(" v = (%f %f %f)\n", v.x, v.y, v.z);
998 }
999 if (pick->found&PW_EDGE) {
1000 HPt3TransPt3(pick->Tmirp, &(pick->e[0]), &(e[0]));
1001 HPt3TransPt3(pick->Tmirp, &(pick->e[1]), &(e[1]));
1002 printf(" e[0] = (%f %f %f)\n", e[0].x, e[0].y, e[0].z);
1003 printf(" e[1] = (%f %f %f)\n", e[1].x, e[1].y, e[1].z);
1004 }
1005
1006 printf("Transformed pick [old world]->\n");
1007 printf(" got = (%f %f %f)\n", owgot.x, owgot.y, owgot.z);
1008 if (pick->found&PW_VERT) {
1009 HPt3TransPt3(oldTw, &(pick->v), &owv);
1010 printf(" v = (%f %f %f)\n", owv.x, owv.y, owv.z);
1011 }
1012 if (pick->found&PW_EDGE) {
1013 HPt3TransPt3(oldTw, &(pick->e[0]), &(owe[0]));
1014 HPt3TransPt3(oldTw, &(pick->e[1]), &(owe[1]));
1015 printf(" e[0] = (%f %f %f)\n", owe[0].x, owe[0].y, owe[0].z);
1016 printf(" e[1] = (%f %f %f)\n", owe[1].x, owe[1].y, owe[1].z);
1017
1018 printf("Transformed pick [world]->\n");
1019 printf(" got = (%f %f %f)\n", wgot.x, wgot.y, wgot.z);
1020 if (pick->found&PW_VERT) {
1021 HPt3TransPt3(pick->Tw, &(pick->v), &wv);
1022 printf(" v = (%f %f %f)\n", wv.x, wv.y, wv.z);
1023 }
1024 if (pick->found&PW_EDGE) {
1025 HPt3TransPt3(pick->Tw, &(pick->e[0]), &(we[0]));
1026 HPt3TransPt3(pick->Tw, &(pick->e[1]), &(we[1]));
1027 printf(" e[0] = (%f %f %f)\n", we[0].x, we[0].y, we[0].z);
1028 printf(" e[1] = (%f %f %f)\n", we[1].x, we[1].y, we[1].z);
1029 }
1030
1031
1032 }
1033 }
1034 }
1035 return chosen;
1036 }
1037
1038 LDEFINE(rawpick, LINT,
1039 "(rawpick CAMID X Y)\n\
1040 Process a pick event in camera CAMID at location (X,Y) given in\n\
1041 integer pixel coordinates. This is a low-level procedure not\n\
1042 intended for external use.")
1043 {
1044 int pickedid, id, x, y;
1045 Pick *pick;
1046
1047 LDECLARE(("rawpick", LBEGIN,
1048 LID, &id,
1049 LINT, &x,
1050 LINT, &y,
1051 LEND));
1052 if (TYPEOF(id) != T_CAM) {
1053 fprintf(stderr, "rawpick: first arg must be a camera id\n");
1054 return Lnil;
1055 }
1056
1057 pick = PickSet(NULL, PA_WANT, PW_EDGE|PW_VERT|PW_FACE, PA_END);
1058 pickedid= view_pick( (DView *)drawer_get_object(id), x, y, pick );
1059
1060 if (pickedid != NOID) {
1061 emit_pick(pickedid, pick);
1062 }
1063 PickDelete(pick);
1064 return LNew(LINT, &pickedid);
1065 }
1066
1067 void
emit_pick(int pickedid,Pick * pick)1068 emit_pick(int pickedid, Pick *pick)
1069 {
1070 /* Variables for total hack */
1071 vvec done;
1072 char donebits[512];
1073
1074 LInterest *interest = LInterestList("pick");
1075
1076 VVINIT(done, char, 128);
1077 if(interest) {
1078 vvuse(&done, donebits, COUNT(donebits));
1079 vvzero(&done);
1080 }
1081
1082 #define DONEID(id) *VVINDEX(done, char, id-CAMID(-20))
1083
1084 for ( ; interest != NULL; interest = interest->next) {
1085 int coordsysid;
1086
1087 /* extract the coord system to use from the interest filter;
1088 if none given, use world */
1089 if (interest->filter
1090 && interest->filter->car
1091 && (LFILTERVAL(interest->filter->car)->flag == VAL)) {
1092 if (!LFROMOBJ(LID)(LFILTERVAL(interest->filter->car)->value,
1093 &coordsysid)) {
1094 OOGLError(0,"emit_pick: bad coord sys filter type");
1095 continue;
1096 }
1097 } else {
1098 coordsysid = WORLDGEOM;
1099 }
1100
1101 if (drawerstate.NDim > 0) {
1102 /* In an ND-context, we report the 3d-quantities picked in the
1103 * 3d sub-space of the camera where the pick occurred. The
1104 * interested parties still have access to the ND-co-ordinates
1105 * by means of the vertex/edge/face indices.
1106 *
1107 * We use TmNMap() to map the 3d vertices to the full Nd-space.
1108 */
1109 int dim = drawerstate.NDim;
1110 TransformN *T, *tmp;
1111 HPointN *got = NULL;
1112 HPointN *v = NULL;
1113 VARARRAY(e, HPtNCoord, 2*dim);
1114 VARARRAY(f, HPtNCoord, pick->found & PW_FACE ? pick->fn * dim : 0);
1115 int gn, vn, vi, en, ei[2], ein, fn, fi;
1116
1117 /* T = transform converting to the coord system of coordsysid */
1118 /* This section does the setup for the total hack */
1119
1120 /* Total hack gigantic if statement */
1121 if (!DONEID(coordsysid)) {
1122 DONEID(coordsysid) = 1;
1123 switch(coordsysid) {
1124 case WORLDGEOM:
1125 T = TmNMap(pick->TwN, pick->axes, NULL);
1126 break;
1127 case PRIMITIVE:
1128 T = TmNMap(pick->TmirpN, pick->axes, NULL);
1129 break;
1130 case SELF:
1131 T = TmNMap(pick->TselfN, pick->axes, NULL);
1132 break;
1133 default:
1134 tmp = drawer_get_ND_transform(WORLDGEOM, coordsysid);
1135 T = TmNConcat(pick->TwN, tmp, NULL);
1136 TmNDelete(tmp);
1137 TmNMap(T, pick->axes, T);
1138 break;
1139 }
1140
1141 if (pickedid != NOID) {
1142 HPoint3 gothpt4;
1143
1144 Pt3ToHPt3(&pick->got, &gothpt4, 1);
1145 got = HPt3NTransform(T, &gothpt4, NULL);
1146 gn = got->dim;
1147 #if 1
1148 {
1149 HPointN *gotn;
1150
1151 gotn = HPt3ToHPtN(&gothpt4, pick->axes, NULL);
1152 HPtNTransform(pick->TwN, gotn, gotn);
1153 HPtNDelete(gotn);
1154 }
1155 #endif
1156 } else {
1157 got = NULL;
1158 gn = 0;
1159 }
1160
1161 if (pick->found & PW_VERT) {
1162 v = HPt3NTransform(T, &pick->v, NULL);
1163 vn = v->dim;
1164 vi = pick->vi;
1165 } else {
1166 v = NULL;
1167 vn = 0;
1168 vi = -1;
1169 }
1170
1171 if (pick->found & PW_EDGE) {
1172 HPointN tmp;
1173
1174 tmp.dim = T->odim;
1175 tmp.flags = 0;
1176
1177 tmp.v = e+0; HPt3NTransform(T, &pick->e[0], &tmp);
1178 tmp.v = e+dim; HPt3NTransform(T, &pick->e[1], &tmp);
1179 en = 2*dim;
1180 ei[0] = pick->ei[0];
1181 ei[1] = pick->ei[1];
1182 ein = 2;
1183 } else {
1184 en = 0;
1185 ein = 0;
1186 }
1187 if (pick->found & PW_FACE) {
1188 HPointN tmp;
1189 int i;
1190
1191 tmp.dim = T->odim;
1192 tmp.flags = 0;
1193 for (i = 0; i < pick->fn; i++) {
1194 tmp.v = &f[i*dim];
1195 HPt3NTransform(T, pick->f+i, &tmp);
1196 }
1197 fi = pick->fi;
1198 fn = pick->fn * dim;
1199 } else {
1200 fn = 0;
1201 fi = -1;
1202 }
1203
1204 /* Cause of total hack.
1205 * This CANNOT be called once for every interested party - otherwise
1206 * every interested party will hear about it numerous times. */
1207 gv_pick(coordsysid, pickedid,
1208 got ? got->v : NULL, gn,
1209 v ? v->v : NULL, vn,
1210 e, en,
1211 f, fn,
1212 VVEC(pick->gpath, int), VVCOUNT(pick->gpath),
1213 vi,
1214 ei, ein,
1215 fi);
1216
1217 HPtNDelete(got);
1218 HPtNDelete(v);
1219 TmNDelete(T);
1220
1221 } /* End of total hack if statement */
1222
1223 } else {
1224 /* T = transform converting to the coord system of coordsysid */
1225 /* This section does the setup for the total hack */
1226 Transform T;
1227 HPoint3 got;
1228 HPoint3 v, e[2];
1229 HPoint3 *f;
1230 int gn, vn, vi, en, ei[2], ein, fn, fi;
1231
1232 /* Total hack gigantic if statement */
1233 if (!DONEID(coordsysid)) {
1234 DONEID(coordsysid) = 1;
1235 switch(coordsysid) {
1236 case WORLDGEOM:
1237 TmCopy(pick->Tw, T); break;
1238 case PRIMITIVE:
1239 TmCopy(pick->Tmirp, T); break;
1240 case SELF:
1241 TmCopy(pick->Tself, T); break;
1242 default:
1243 drawer_get_transform(WORLDGEOM, T, coordsysid);
1244 TmConcat(pick->Tw, T, T);
1245 break;
1246 }
1247
1248 if (pickedid != NOID) {
1249 Pt3Transform(T, &pick->got, HPoint3Point3(&got));
1250 got.w = 1;
1251 gn = 4;
1252 } else {
1253 gn = 0;
1254 }
1255
1256 if (pick->found & PW_VERT) {
1257 HPt3Transform(T, &(pick->v), &v);
1258 vn = 4;
1259 vi = pick->vi;
1260 } else {
1261 vn = 0;
1262 vi = -1;
1263 }
1264
1265 if (pick->found & PW_EDGE) {
1266 HPt3TransformN(T, pick->e, &e[0], 2);
1267 en = 8;
1268 ei[0] = pick->ei[0];
1269 ei[1] = pick->ei[1];
1270 ein = 2;
1271 } else {
1272 en = 0;
1273 ein = 0;
1274 }
1275 if (pick->found & PW_FACE) {
1276 f = OOGLNewNE(HPoint3, pick->fn, "rawpick");
1277 HPt3TransformN(T, pick->f, f, pick->fn);
1278 fi = pick->fi;
1279 fn = pick->fn * 4;
1280 } else {
1281 f = NULL;
1282 fn = 0;
1283 fi = -1;
1284 }
1285
1286 /* Cause of total hack.
1287 * This CANNOT be called once for every interested party - otherwise
1288 * every interested party will hear about it numerous times. */
1289 gv_pick(coordsysid, pickedid,
1290 HPoint3Data(&got), gn,
1291 &v.x, vn,
1292 &e[0].x, en,
1293 (float *)f, fn, /* f, fn, */
1294 VVEC(pick->gpath, int), VVCOUNT(pick->gpath),
1295 vi,
1296 ei, ein,
1297 fi);
1298
1299 if (f != NULL) OOGLFree(f);
1300
1301 } /* End of total hack if statement */
1302
1303 } /* End of 3d case */
1304 }
1305 vvfree(&done);
1306 }
1307
1308 LDEFINE(pick, LVOID,
1309 "(pick COORDSYS GEOMID G V E F P VI EI FI)\n"
1310 "The pick command is executed internally in response to pick\n"
1311 "events (right mouse double click).\n"
1312 "\n\n"
1313 "COORDSYS = coordinate system in which coordinates of the following\n"
1314 "arguments are specified. This can be:"
1315 "\n\n\tworld: world coord sys"
1316 "\n\n\tself: coord sys of the picked geom (GEOMID)"
1317 "\n\n\tprimitive: coord sys of the actual primitive within"
1318 "\n\n\t\tthe picked geom where the pick occurred."
1319 "\n\n\n\n"
1320 "GEOMID = id of picked geom"
1321 "\n\n\n\n"
1322 "G = picked point (actual intersection of pick ray with object)"
1323 "\n\n\n\n"
1324 "V = picked vertex, if any"
1325 "\n\n\n\n"
1326 "E = picked edge, if any"
1327 "\n\n\n\n"
1328 "F = picked face"
1329 "\n\n\n\n"
1330 "P = path to picked primitive [0 or more]"
1331 "\n\n\n\n"
1332 "VI = index of picked vertex in primitive"
1333 "\n\n\n\n"
1334 "EI = list of indices of endpoints of picked edge, if any"
1335 "\n\n\n\n"
1336 "FI = index of picked face"
1337 "\n\n\n\n"
1338 "External modules can find out about pick events by registering\n"
1339 "interest in calls to \"pick\" via the \"interest\" command."
1340 "\n\n\n\n"
1341 "In the ND-viewing context the co-ordinates are actually ND-points.\n"
1342 "They correspond to the 3D points of the pick relative to the\n"
1343 "sub-space defined by the viewport of the camera where the pick\n"
1344 "occurred. The co-ordinates are then padded with zeroes and\n"
1345 "transformed back to the co-ordinate system defined by\n"
1346 "\"COORDSYS\".")
1347 {
1348 float *got = NULL, *v = NULL, *e = NULL, *f = NULL;
1349 int vi, ei[2], fi, *p = NULL;
1350 int gn, vn, en, fn, pn;
1351 int ein = 2;
1352 int id, coordsys;
1353
1354 /* NOTE: If you change the lisp syntax of this function (which you
1355 shouldn't do), you must also update the DEFPICKFUNC macro in the
1356 file "pickfunc.h", which external modules use. */
1357 LDECLARE(("pick", LBEGIN,
1358 LID, &coordsys,
1359 LID, &id,
1360 LHOLD, LVARARRAY, LFLOAT, &got, &gn,
1361 LHOLD, LVARARRAY, LFLOAT, &v, &vn,
1362 LHOLD, LVARARRAY, LFLOAT, &e, &en,
1363 LHOLD, LVARARRAY, LFLOAT, &f, &fn,
1364 LHOLD, LVARARRAY, LINT, &p, &pn,
1365 LINT, &vi,
1366 LHOLD, LARRAY, LINT, ei, &ein,
1367 LINT, &fi,
1368 LEND));
1369
1370 if (got) OOGLFree(got);
1371 if (v) OOGLFree(v);
1372 if (e) OOGLFree(e);
1373 if (f) OOGLFree(f);
1374 if (p) OOGLFree(p);
1375
1376 return Lt;
1377 }
1378
1379 /*****************************************************************************/
1380
1381 LDEFINE(event_keys, LVOID,
1382 "(event-keys {on|off})\n\
1383 Turn keyboard events on or off to enable/disable keyboard shortcuts.")
1384 {
1385 int on;
1386
1387 LDECLARE(("event-keys", LBEGIN,
1388 LKEYWORD, &on,
1389 LEND));
1390
1391 if (on == ON_KEYWORD)
1392 keyshorts = 1;
1393 else
1394 if (on == OFF_KEYWORD)
1395 keyshorts = 0;
1396 else {
1397 OOGLError(0, "event-keys: expected \"on\" or \"off\" keyword");
1398 return Lnil;
1399 }
1400
1401 return Lt;
1402
1403 }
1404
1405 /*****************************************************************************/
1406
1407 LDEFINE(event_pick, LVOID,
1408 "(event-pick {on|off})\n\
1409 Turn picking on or off.")
1410 {
1411 int on;
1412
1413 LDECLARE(("event-pick", LBEGIN,
1414 LKEYWORD, &on,
1415 LEND));
1416
1417 if (on == ON_KEYWORD)
1418 pickon = 1;
1419 else
1420 if (on == OFF_KEYWORD)
1421 pickon = 0;
1422 else {
1423 OOGLError(0, "event-pick: expected \"on\" or \"off\" keyword");
1424 return Lnil;
1425 }
1426
1427 return Lt;
1428 }
1429
1430 /*****************************************************************************/
1431
1432 LDEFINE(dither, LVOID,
1433 "(dither CAM-ID {on|off|toggle})\n\
1434 Turn dithering on or off in that camera.")
1435 {
1436 DView *dv;
1437 int id, dither, i, on = -1;
1438
1439 LDECLARE(("dither", LBEGIN,
1440 LID, &id,
1441 LOPTIONAL,
1442 LKEYWORD, &on,
1443 LEND));
1444
MAYBE_LOOP(id,i,T_CAM,DView,dv)1445 MAYBE_LOOP(id, i, T_CAM, DView, dv) {
1446 if (dv->mgctx) {
1447 mgctxselect(dv->mgctx);
1448 mgctxget(MG_DITHER, &dither);
1449 }
1450 if (on == TOGGLE_KEYWORD)
1451 dither = !dither;
1452 else
1453 if (on == ON_KEYWORD)
1454 dither = 1;
1455 else
1456 if (on == OFF_KEYWORD)
1457 dither = 0;
1458 else {
1459 OOGLError(0, "dither: expected \"on\", \"off\" or \"toggle\" keyword");
1460 return Lnil;
1461 }
1462
1463 if (dv->mgctx) {
1464 mgctxset(MG_DITHER, dither, MG_END);
1465 gv_redraw(dv->id);
1466 }
1467 ui_maybe_refresh(dv->id);
1468 }
1469 return Lt;
1470
1471 }
1472
1473 /*****************************************************************************/
1474
1475 /*
1476 * Local Variables: ***
1477 * c-basic-offset: 2 ***
1478 * End: ***
1479 */
1480