1 /***********************************************************************/
2 /* Open Visualization Data Explorer */
3 /* (C) Copyright IBM Corp. 1989,1999 */
4 /* ALL RIGHTS RESERVED */
5 /* This code licensed under the */
6 /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */
7 /***********************************************************************/
8
9 #include <dxconfig.h>
10
11
12 /*---------------------------------------------------------------------------*\
13 $Source: /src/master/dx/src/exec/hwrender/hwNavigateInteractor.c,v $
14 Author: Mark Hood
15
16 The navigation interactor is a means for moving in 3D space. The user is
17 able to move back and forth along the current navigation vector (initially
18 the view vector) by using the left and right mouse buttons. At the same
19 time, the navigation vector is incrementally updated to rotate (pivot)
20 toward the mouse pointer position in the image window. The incremental
21 update speed may be adjusted by two parameters, rotate_speed_factor and
22 translate_speed_factor.
23
24 A separate parameter, look_at_direction, determines in what direction
25 relative to the navigation vector the user is looking. If the
26 look_at_direction is not LOOK_FORWARD, then pivoting is disabled, leaving
27 translation along the navigation vector the only possible movement.
28
29 \*---------------------------------------------------------------------------*/
30
31 #if defined(HAVE_STRINGS_H)
32 #include <strings.h>
33 #endif
34
35 #include <stdio.h>
36 #include <math.h>
37 #include "hwDeclarations.h"
38 #include "hwMatrix.h"
39 #include "hwInteractorEcho.h"
40
41 #include "hwPortLayer.h"
42
43 #include "hwDebug.h"
44
45 /*
46 * Navigator private data structure.
47 */
48
49 typedef struct {
50 /* which mouse button pressed */
51 int btn ;
52
53 /* direction of movement */
54 float nav_dir[3] ;
55 float nav_up[3] ;
56 int first_time ;
57
58 /* view direction relative to navigation vector */
59 int look_at_direction ; /* tdmLOOK_FORWARD, tdmLOOK_LEFT, tdmLOOK_UP, etc */
60
61 /* throttle factors for pivot and translation */
62 float rotate_speed_factor ; /* 0.0 .. 100.0 */
63 float translate_speed_factor ; /* 0.0 .. 100.0 */
64
65 /* radius of virtual sphere, center, and square of radius */
66 float gradius, gx, gy, g2 ;
67
68 /* update flag */
69 int nav_update ;
70 } NavigateData ;
71
72 /*
73 * Forward references
74 */
75
76 // static void DoubleClick (tdmInteractor, int, int, tdmInteractorReturn *) ;
77 static void StartStroke (tdmInteractor, int, int, int, int) ;
78 static void StrokePoint (tdmInteractor I, int x, int y, int flag, int state) ;
79 static void EndStroke (tdmInteractor, tdmInteractorReturnP) ;
80
81 /*
82 * Null functions
83 */
84
NullFunction(tdmInteractor I,tdmInteractorRedrawMode M)85 static void NullFunction (tdmInteractor I, tdmInteractorRedrawMode M)
86 {
87 }
88
89 static void
NullDoubleClick(tdmInteractor I,int x,int y,tdmInteractorReturn * R)90 NullDoubleClick (tdmInteractor I, int x, int y, tdmInteractorReturn *R)
91 {
92 R->change = 0 ;
93 }
94
95
96 /*
97 * Creation function
98 */
99
100 tdmInteractor
_dxfCreateNavigator(tdmInteractorWin W,tdmViewEchoFunc EchoFunc,void * udata)101 _dxfCreateNavigator (tdmInteractorWin W, tdmViewEchoFunc EchoFunc, void *udata)
102 {
103 /*
104 * Initialize and return a handle to a navigation interactor.
105 * The echo function is supplied by the application.
106 */
107
108 register tdmInteractor I ;
109
110 ENTRY(("_dxfCreateNavigator(0x%x, 0x%x, 0x%x)", W, EchoFunc, udata));
111
112 if (W && (I = _dxfAllocateInteractor(W, sizeof(NavigateData))))
113 {
114 DEFDATA(I,NavigateData) ;
115
116 /* instance initial interactor methods */
117 FUNC(I, DoubleClick) = NullDoubleClick ;
118 FUNC(I, StartStroke) = StartStroke ;
119 FUNC(I, StrokePoint) = StrokePoint ;
120 FUNC(I, EndStroke) = EndStroke ;
121 FUNC(I, EchoFunc) = EchoFunc ;
122 FUNC(I, ResumeEcho) = NullFunction ;
123 FUNC(I, Destroy) = _dxfDeallocateInteractor ;
124 FUNC(I, KeyStruck) = _dxfNullKeyStruck;
125
126
127 /* init data */
128 PDATA(look_at_direction) = tdmLOOK_FORWARD ;
129 PDATA(rotate_speed_factor) = 21 ;
130 PDATA(translate_speed_factor) = 11 ;
131 PDATA(nav_update) = 0 ;
132 PDATA(first_time) = 1 ;
133
134 /* copy pointer to user data */
135 UDATA(I) = udata ;
136
137 EXIT(("OK"));
138 return I ;
139 }
140 else
141 {
142 EXIT(("ERROR"));
143 return 0 ;
144 }
145 }
146
147
148 /*
149 * Utils
150 */
151
152 void
_dxfResetNavigateInteractor(tdmInteractor I)153 _dxfResetNavigateInteractor (tdmInteractor I)
154 {
155 ENTRY(("_dxfResetNavigateInteractor(0x%x)", I));
156
157 if (I)
158 {
159 DEFDATA(I, NavigateData) ;
160 PDATA(look_at_direction) = tdmLOOK_FORWARD ;
161 }
162
163 EXIT((""));
164 }
165
166 void
_dxfSetNavigateLookAt(tdmInteractor I,int direction,float angle,float current_view[3][3],float viewDirReturn[3],float viewUpReturn[3])167 _dxfSetNavigateLookAt (tdmInteractor I,
168 int direction, float angle, float current_view[3][3],
169 float viewDirReturn[3], float viewUpReturn[3])
170 {
171 /*
172 * Set view vectors relative to navigation vectors.
173 */
174
175 ENTRY(("_dxfSetNavigateLookAt(0x%x, %d, %f, 0x%x, 0x%x, 0x%x)",
176 I, direction, angle, current_view, viewDirReturn, viewUpReturn));
177
178 if (I)
179 {
180 DEFDATA(I, NavigateData) ;
181 float mat[4][4], r[3], c, s, t ;
182
183 PDATA(look_at_direction) = direction ;
184 if (PDATA(first_time) || direction == tdmLOOK_ALIGN)
185 {
186 /* set navigation vectors to current view basis vectors */
187 PDATA(nav_dir[0]) = -current_view[0][2] ;
188 PDATA(nav_dir[1]) = -current_view[1][2] ;
189 PDATA(nav_dir[2]) = -current_view[2][2] ;
190
191 PDATA(nav_up[0]) = current_view[0][1] ;
192 PDATA(nav_up[1]) = current_view[1][1] ;
193 PDATA(nav_up[2]) = current_view[2][1] ;
194
195 if (PDATA(first_time))
196 {
197 PRINT(("first time"));
198 PDATA(first_time) = 0 ;
199 }
200 if (direction == tdmLOOK_ALIGN)
201 {
202 PRINT(("LOOK_ALIGN"));
203 PDATA(look_at_direction) = tdmLOOK_FORWARD ;
204 }
205 }
206
207 /* derive matrix to rotate `angle' radians around axis `r' */
208 switch (direction)
209 {
210 case tdmLOOK_ALIGN:
211 case tdmLOOK_FORWARD:
212 PRINT(("%s", direction == tdmLOOK_ALIGN ? "" : "LOOK_FORWARD"));
213 VCOPY (r, PDATA(nav_up)) ;
214 angle = 0 ;
215 break ;
216 case tdmLOOK_BACKWARD:
217 PRINT(("LOOK_BACKWARD"));
218 VCOPY (r, PDATA(nav_up)) ;
219 angle = M_PI ;
220 break ;
221 case tdmLOOK_LEFT:
222 PRINT(("LOOK_LEFT %f", RAD2DEG(angle)));
223 VCOPY (r, PDATA(nav_up)) ;
224 break ;
225 case tdmLOOK_RIGHT:
226 PRINT(("LOOK_RIGHT %f", RAD2DEG(angle)));
227 r[0] = -PDATA(nav_up[0]) ;
228 r[1] = -PDATA(nav_up[1]) ;
229 r[2] = -PDATA(nav_up[2]) ;
230 break ;
231 case tdmLOOK_UP:
232 PRINT(("LOOK_UP %f", RAD2DEG(angle)));
233 CROSS (r, PDATA(nav_dir), PDATA(nav_up)) ;
234 break ;
235 case tdmLOOK_DOWN:
236 PRINT(("LOOK_DOWN %f", RAD2DEG(angle)));
237 CROSS (r, PDATA(nav_up), PDATA(nav_dir)) ;
238 break ;
239 default:
240 PRINT(("unknown look-at direction"));
241 break ;
242 }
243
244 s = (float) sin((double)angle) ;
245 c = (float) cos((double)angle) ;
246 t = 1 - c ;
247
248 ROTXYZ (mat, r[0], r[1], r[2], s, c, t) ;
249 XFORM_VECTOR (mat, PDATA(nav_dir), viewDirReturn) ;
250 XFORM_VECTOR (mat, PDATA(nav_up), viewUpReturn) ;
251
252 PRINT(("viewDirReturn:"));
253 VPRINT(viewDirReturn) ;
254 PRINT(("viewUpReturn:"));
255 VPRINT(viewUpReturn) ;
256 }
257
258 EXIT((""));
259 }
260
261 void
_dxfSetNavigateTranslateSpeed(tdmInteractor I,float speed)262 _dxfSetNavigateTranslateSpeed (tdmInteractor I, float speed)
263 {
264 ENTRY(("_dxfSetNavigateTranslateSpeed(0x%x, %f)", I, speed));
265
266 if (I)
267 {
268 DEFDATA(I, NavigateData) ;
269 PDATA(translate_speed_factor) = speed ;
270 }
271
272 EXIT((""));
273 }
274
275 void
_dxfSetNavigateRotateSpeed(tdmInteractor I,float speed)276 _dxfSetNavigateRotateSpeed (tdmInteractor I, float speed)
277 {
278 ENTRY(("_dxfSetNavigateRotateSpeed(0x%x, %f)", I, speed));
279
280 if (I)
281 {
282 DEFDATA(I, NavigateData) ;
283 PDATA(rotate_speed_factor) = speed ;
284 }
285
286 EXIT((""));
287 }
288
289
290 /*
291 * Method implementations
292 */
293
294 static void
StartStroke(tdmInteractor I,int x,int y,int btn,int s)295 StartStroke (tdmInteractor I, int x, int y, int btn, int s)
296 {
297 float Near, Far, from[3], zaxis[3];
298 DEFDATA(I,NavigateData) ;
299 DEFPORT(I_PORT_HANDLE) ;
300
301 ENTRY(("StartStroke(0x%x, %d, %d, %d)", I, x, y, btn));
302
303 /* get current view matrix */
304 _dxfGetViewMatrix (CDATA(stack), CDATA(viewXfm)) ;
305 PRINT(("viewXfm:"));
306 MPRINT(CDATA(viewXfm)) ;
307
308 /* define a virtual sphere with radius half of smallest window dimension */
309 if (CDATA(h) > CDATA(w))
310 {
311 PDATA(gradius) = CDATA(w)/2 ;
312 PDATA(gx) = PDATA(gradius) ;
313 PDATA(gy) = PDATA(gradius) + (CDATA(h) - CDATA(w))/2 ;
314 }
315 else
316 {
317 PDATA(gradius) = CDATA(h)/2 ;
318 PDATA(gy) = PDATA(gradius) ;
319 PDATA(gx) = PDATA(gradius) + (CDATA(w) - CDATA(h))/2 ;
320 }
321
322 /* record square of radius */
323 PDATA(g2) = PDATA(gradius)*PDATA(gradius) ;
324
325 /* record button in use */
326 PDATA(btn) = btn ;
327
328 if (! CDATA(projection))
329 {
330 /* force perspective projection */
331 PRINT(("forcing perspective..."));
332
333 CDATA(projection) = 1 ;
334 CDATA(fov) = CDATA(width)/CDATA(vdist) ;
335 GET_VC_ORIGIN(CDATA(viewXfm), from) ;
336 GET_VIEW_DIRECTION(CDATA(viewXfm), zaxis) ;
337
338 /* compute appropriate clip planes */
339 _dxfGetNearFar(1, CDATA(w), CDATA(fov),
340 from, zaxis, CDATA(box), &Near, &Far);
341
342 /* set up projection */
343 _dxfSetProjectionInfo(CDATA(stack), 1, CDATA(fov),
344 CDATA(aspect), Near, Far);
345 _dxf_SET_PERSPECTIVE_PROJECTION (PORT_CTX, CDATA(fov),
346 CDATA(aspect), Near, Far) ;
347 CDATA(view_state)++ ;
348 }
349
350 EXIT((""));
351 }
352
353
354 static void
StrokePoint(tdmInteractor I,int x,int y,int flag,int state)355 StrokePoint (tdmInteractor I, int x, int y, int flag, int state)
356 {
357 DEFDATA(I,NavigateData) ;
358 DEFPORT(I_PORT_HANDLE) ;
359 float rot[4][4], trn[4][4], mat[4][4] ;
360 float Near, Far, from[3], zaxis[3];
361 double a, len, square ;
362 register float dx, dy, rx, ry, t, s, c ;
363
364 ENTRY(("StrokePoint(0x%x, %d, %d)", I, x, y));
365
366 if (PDATA(look_at_direction) == tdmLOOK_FORWARD)
367 {
368 /*
369 * Compute pivot axis and rotation angle.
370 *
371 * Project mouse pointer onto a virtual sphere. The pivot axis
372 * [rx,ry,rz] is the cross product of the projection [x,y,z] with
373 * the view vector [0,0,-1]. Since these are unit vectors the
374 * length of the cross product is also the sine of the angle
375 * between them.
376 *
377 * rz is always zero so it is ignored here.
378 */
379
380 y = YFLIP(y) ;
381 dx = x - PDATA(gx) ;
382 dy = y - PDATA(gy) ;
383
384 if ((square = dx*dx + dy*dy) > PDATA(g2))
385 {
386 /* off the edge of the virtual sphere */
387 len = sqrt(square) ;
388 rx = -dy/len ;
389 ry = dx/len ;
390 a = M_PI/2 ;
391 }
392 else if (square > 0)
393 {
394 /* project point onto virtual sphere */
395 rx = -(double)dy / (double)PDATA(gradius) ;
396 ry = (double)dx / (double)PDATA(gradius) ;
397 s = (float)sqrt ((double)rx*rx + (double)ry*ry) ;
398 rx /= s ;
399 ry /= s ;
400 a = asin(s) ;
401 }
402 else
403 {
404 /* don't divide by zero */
405 rx = 0 ;
406 ry = 0 ;
407 a = 0 ;
408 }
409
410 /* multiply angle `a' by pivot speed factor */
411 a *= PDATA(rotate_speed_factor) * 0.001 ;
412
413 /* construct pivot matrix */
414 s = sin(a) ; c = cos(a) ; t = 1 - c ;
415 ROTXY (rot, rx, ry, s, c, t) ;
416
417 /* compose with current view */
418 MULTMATRIX (mat, CDATA(viewXfm), rot) ;
419
420 /* update navigation vectors */
421 PDATA(nav_dir[0]) = -mat[0][2] ;
422 PDATA(nav_dir[1]) = -mat[1][2] ;
423 PDATA(nav_dir[2]) = -mat[2][2] ;
424
425 PDATA(nav_up[0]) = mat[0][1] ;
426 PDATA(nav_up[1]) = mat[1][1] ;
427 PDATA(nav_up[2]) = mat[2][1] ;
428 }
429 else
430 {
431 COPYMATRIX (mat, CDATA(viewXfm)) ;
432 }
433
434 if (PDATA(btn) == 1)
435 {
436 /* move view coordinate origin along navigate vector */
437 COPYMATRIX (trn, identity) ;
438
439 trn[3][0] = -PDATA(translate_speed_factor) * 0.0005 *
440 CDATA(vdist) * PDATA(nav_dir[0]) ;
441 trn[3][1] = -PDATA(translate_speed_factor) * 0.0005 *
442 CDATA(vdist) * PDATA(nav_dir[1]) ;
443 trn[3][2] = -PDATA(translate_speed_factor) * 0.0005 *
444 CDATA(vdist) * PDATA(nav_dir[2]) ;
445
446 MULTMATRIX (CDATA(viewXfm), trn, mat) ;
447 }
448 else if (PDATA(btn) == 3)
449 {
450 /* move view coordinate origin backwards along navigate vector */
451 COPYMATRIX (trn, identity) ;
452
453 trn[3][0] = PDATA(translate_speed_factor) * 0.0005 *
454 CDATA(vdist) * PDATA(nav_dir[0]) ;
455 trn[3][1] = PDATA(translate_speed_factor) * 0.0005 *
456 CDATA(vdist) * PDATA(nav_dir[1]) ;
457 trn[3][2] = PDATA(translate_speed_factor) * 0.0005 *
458 CDATA(vdist) * PDATA(nav_dir[2]) ;
459
460 MULTMATRIX (CDATA(viewXfm), trn, mat) ;
461 }
462 else
463 {
464 COPYMATRIX (CDATA(viewXfm), mat) ;
465 }
466
467 /* get the new clip planes */
468 GET_VC_ORIGIN(CDATA(viewXfm), from) ;
469 GET_VIEW_DIRECTION(CDATA(viewXfm), zaxis) ;
470 _dxfGetNearFar(1, CDATA(w), CDATA(fov), from, zaxis, CDATA(box), &Near, &Far);
471
472 /* setting new clip planes requires creating a new projection matrix */
473 _dxfSetProjectionInfo(CDATA(stack), 1, CDATA(fov), CDATA(aspect), Near, Far);
474 _dxf_SET_PERSPECTIVE_PROJECTION(PORT_CTX, CDATA(fov), CDATA(aspect),
475 Near, Far);
476
477 /* load the new view matrix */
478 _dxf_LOAD_MATRIX (PORT_CTX, CDATA(viewXfm)) ;
479 _dxfLoadViewMatrix (CDATA(stack), CDATA(viewXfm)) ;
480 CDATA(view_state)++ ;
481
482 /* draw the echo */
483 CALLFUNC(I,EchoFunc) (I, UDATA(I), CDATA(viewXfm), 0) ;
484 CALLFUNC(I,EchoFunc) (I, UDATA(I), CDATA(viewXfm), 1) ;
485
486 PDATA(nav_update) = 1 ;
487
488 EXIT((""));
489 }
490
491
492 static void
EndStroke(tdmInteractor I,tdmInteractorReturnP R)493 EndStroke (tdmInteractor I, tdmInteractorReturnP R)
494 {
495 /*
496 * End stroke and return appropriate info.
497 */
498
499 DEFDATA(I,NavigateData) ;
500
501 ENTRY(("EndStroke(0x%x, 0x%x)", I, R));
502
503 if (PDATA(nav_update))
504 {
505 PRINT(("current viewXfm:"));
506 MPRINT(CDATA(viewXfm)) ;
507
508 _dxfRenormalizeView(CDATA(viewXfm)) ;
509 PRINT(("renormalized viewXfm:"));
510 MPRINT(CDATA(viewXfm)) ;
511
512 /*
513 * Convert VC origin to world coordinates to get `from' point. Use
514 * transpose of orthogonal view transform (W->V) to get V->W.
515 */
516 R->from[0] = -(CDATA(viewXfm[3][0])*CDATA(viewXfm[0][0]) +
517 CDATA(viewXfm[3][1])*CDATA(viewXfm[0][1]) +
518 CDATA(viewXfm[3][2])*CDATA(viewXfm[0][2])) ;
519 R->from[1] = -(CDATA(viewXfm[3][0])*CDATA(viewXfm[1][0]) +
520 CDATA(viewXfm[3][1])*CDATA(viewXfm[1][1]) +
521 CDATA(viewXfm[3][2])*CDATA(viewXfm[1][2])) ;
522 R->from[2] = -(CDATA(viewXfm[3][0])*CDATA(viewXfm[2][0]) +
523 CDATA(viewXfm[3][1])*CDATA(viewXfm[2][1]) +
524 CDATA(viewXfm[3][2])*CDATA(viewXfm[2][2])) ;
525
526 /* `to' point is `vdist' from `from' along view vector */
527 R->to[0] = R->from[0] - CDATA(vdist)*CDATA(viewXfm[0][2]) ;
528 R->to[1] = R->from[1] - CDATA(vdist)*CDATA(viewXfm[1][2]) ;
529 R->to[2] = R->from[2] - CDATA(vdist)*CDATA(viewXfm[2][2]) ;
530
531 /* `up' vector is 2nd column of orthogonal view matrix */
532 R->up[0] = CDATA(viewXfm[0][1]) ;
533 R->up[1] = CDATA(viewXfm[1][1]) ;
534 R->up[2] = CDATA(viewXfm[2][1]) ;
535
536 R->dist = CDATA(vdist) ;
537
538 MCOPY(R->view, CDATA(viewXfm)) ;
539 R->change = 1 ;
540 }
541 else
542 /* no change */
543 R->change = 0 ;
544
545 PDATA(nav_update) = 0 ;
546
547 EXIT((""));
548 }
549