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