1 /*
2 
3 *****************************************************************************
4 * Author:                                                                   *
5 * ------                                                                    *
6 *  Anton Kokalj                                  Email: Tone.Kokalj@ijs.si  *
7 *  Department of Physical and Organic Chemistry  Phone: x 386 1 477 3523    *
8 *  Jozef Stefan Institute                          Fax: x 386 1 477 3811    *
9 *  Jamova 39, SI-1000 Ljubljana                                             *
10 *  SLOVENIA                                                                 *
11 *                                                                           *
12 * Source: $XCRYSDEN_TOPDIR/C/xcTogl.c
13 * ------                                                                    *
14 * Copyright (c) 1996-2003 by Anton Kokalj                                   *
15 *****************************************************************************
16 
17 */
18 
19 #include <togl.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <math.h>
23 #include "struct.h"
24 #include "xcfunc.h"
25 
26 
27 struct Togl *mesa_togl = NULL;
28 extern void (*xcDisplay)(struct Togl *togl);
29 extern NEW_WIN_CONTEXT *FindWinContextByTogl(struct Togl *togl);
30 extern OrthoProj ort;
31 extern realTimeMove makeMovie;
32 
33 extern int togl_exists;
34 
35 /*
36  * Togl widget create callback.  This is called by Tcl/Tk when the widget has
37  * been realized.  Here's where one may do some one-time context setup or
38  * initializations.
39  */
40 int
xcToglCreateFunc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)41 xcToglCreateFunc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
42 {
43   Togl *togl;
44   static int first_time = 1;
45 
46   if (objc != 2) {
47     Tcl_WrongNumArgs(interp, 1, objv, "pathName");
48     return TCL_ERROR;
49   }
50   if ( Togl_GetToglFromObj(interp, objv[1], &togl) != TCL_OK ) {
51     return TCL_ERROR;
52   }
53   //Togl_GetToglFromName(interp, ".mesa", &mesa);
54 
55   /***************************************************************/
56   /*   made atomic labels (only when first structure is opened   */
57   /***************************************************************/
58   /* first make font */
59   if (first_time) {
60     makeRasterFont();
61     makeAtomLabels();
62     makeXYZLabels();
63     makeTemp3D2DList();
64     /* make coor-sist */
65     makeCrdList();
66     xcGenDispList();
67     first_time = 0;
68   }
69 
70   if ( strcmp(Togl_Ident(togl), ".mesa") == 0 ) {
71     xcDisplayFunc(xcDummyDisplay);
72     mesa_togl = togl;
73   }
74   else {
75     NEW_WIN_CONTEXT *wc;
76     cryNewToglInit( togl );
77     wc = FindWinContextByTogl( togl );
78     wc->xcDisplay = xcDummyDisplay;
79   }
80 
81   togl_exists = XC_TRUE;
82   LoadLights();
83   LoadStructMaterial();
84 
85   return TCL_OK;
86 }
87 
88 
89 /*
90  * Togl widget reshape callback.  This is called by Tcl/Tk when the widget
91  * has been resized.  Typically, we call glViewport and perhaps setup the
92  * projection matrix.
93  */
94 int
xcToglReshapeFunc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)95 xcToglReshapeFunc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
96 {
97   Togl *togl;
98   int w, h;
99 
100   if (objc != 2) {
101     Tcl_WrongNumArgs(interp, 1, objv, "pathName");
102     return TCL_ERROR;
103   }
104   if (Togl_GetToglFromObj(interp, objv[1], &togl) != TCL_OK) {
105     return TCL_ERROR;
106   }
107 
108   w = Togl_Width (togl);
109   h = Togl_Height(togl);
110 
111   if ( togl == mesa_togl ) {
112     /* old fashion style */
113     VPf.width  = w;
114     VPf.height = h;
115     VPf.canvassize = VPf.height;
116     if ( VPf.width < VPf.height) VPf.canvassize = VPf.width;
117 
118     if (VPf.stropened) {
119       xcViewPort();
120     } else {
121       float aspect = (float) VPf.width / (float) VPf.height;
122       glViewport( 0, 0, VPf.width, VPf.height );
123       glMatrixMode(GL_PROJECTION);
124       glLoadIdentity();
125       glFrustum(-aspect, aspect, -1.0, 1.0, 1.0, 10.0);
126       glMatrixMode(GL_MODELVIEW);
127     }
128   } else {
129     /* new nicer fashion */
130     NEW_WIN_CONTEXT *wc;
131 
132     /*glViewport( 0, 0, VPf.width, VPf.height );*/
133     wc = FindWinContextByTogl( togl );
134     if (!wc) return TCL_ERROR;
135     wc->VPf.height = w;
136     wc->VPf.width  = h;
137 
138     crySetProjection( wc, togl );
139   }
140   return TCL_OK;
141 }
142 
143 /*
144  * this function is called when togle widget is destroyed
145  */
146 int
xcToglDestroyFunc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)147 xcToglDestroyFunc(ClientData clientData, Tcl_Interp *interp,
148 		  int objc, Tcl_Obj *const *objv)
149 {
150   Togl *togl;
151 
152   if (objc != 2) {
153     Tcl_WrongNumArgs(interp, 1, objv, "pathName");
154     return TCL_ERROR;
155   }
156   if (Togl_GetToglFromObj(interp, objv[1], &togl) != TCL_OK) {
157     return TCL_ERROR;
158   }
159 
160   if ( togl == mesa_togl ) {
161     /* if this happends, then XCRYSDEN is about to exit. Do nothing. */
162     /*
163       xcMaybeDestroyLists();
164       FreeAllVariables();
165       mesa_togl = NULL;
166     */
167     xcTkFontFreeAll();
168   } else {
169     xcDisplay = NULL;
170     DestroyWinContext( togl );
171   }
172 
173   return TCL_OK;
174 }
175 
176 /*
177  * after user has stooped rotating/translating redisplay in nicer fashion
178  */
179 int
xcToglTimerFunc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)180 xcToglTimerFunc(ClientData clientData, Tcl_Interp *interp,
181 		int objc, Tcl_Obj *const *objv)
182 {
183   Togl *togl;
184   static int toglIsInteractive = 0;
185 
186   if (objc != 2) {
187     Tcl_WrongNumArgs(interp, 1, objv, "pathName");
188     return TCL_ERROR;
189   }
190   if (Togl_GetToglFromObj(interp, objv[1], &togl) != TCL_OK) {
191     return TCL_ERROR;
192   }
193 
194   if ( togl == mesa_togl ) {
195 
196     /* interative rotation - zooming */
197 
198     if ( !toglIsInteractive && (tr.b1motion || tr.b2motion || tr.shiftB1motion) ) {
199       toglIsInteractive = 1;
200     }
201     if ( toglIsInteractive && !(tr.b1motion || tr.b2motion || tr.shiftB1motion ) ) {
202       toglIsInteractive = 0;
203       Togl_PostRedisplay(togl);
204     }
205 
206     /* real-time movie making */
207     if ( makeMovie.doit && makeMovie.mode == MOVIE_MODE_REALTIME_INTERVAL ) {
208       createMoviePPMFrame(togl);
209     }
210   }
211   return TCL_OK;
212 }
213 
214 /*
215  * this function takes care <B1-Motion> binding -> that's ROTATION
216  */
217 int
XC_B1MotionCb(ClientData clientData,Tcl_Interp * interp,int argc,const char * argv[])218 XC_B1MotionCb(ClientData clientData, Tcl_Interp *interp,
219 	      int argc, const char *argv[])
220 {
221   int xrotnew, yrotnew; /* current <x> <y> mouse pointer position */
222   int dx, dy;
223   double fiX = 0, fiY = 0, ssize;
224   Togl *togl;
225 
226   if (argc != 4) {
227     Tcl_SetResult(interp,
228 		  "Usage: xc_B1motion <toglname> <x> <y>", TCL_STATIC);
229     return TCL_ERROR;
230   }
231 
232   if ( Togl_GetToglFromName(interp, argv[1], &togl) == TCL_ERROR ) {
233     char rss[1024];
234     snprintf(rss, sizeof(rss),
235 	     "couldn't find %s togl widget", argv[3]);
236     Tcl_SetResult(interp, rss, TCL_VOLATILE);
237     return TCL_ERROR;
238   }
239 
240   if ( Tcl_GetInt(interp, argv[2], &xrotnew) == TCL_ERROR ) {
241     char rss[1024];
242     snprintf(rss, sizeof(rss),"wanted integer for <x>, but got \"%s\" in \"<toglname> xc_B1motion <x> <y>\" command", argv[2]);
243     Tcl_SetResult(interp, rss, TCL_VOLATILE);
244     return TCL_ERROR;
245   }
246 
247   if ( Tcl_GetInt(interp, argv[3], &yrotnew) == TCL_ERROR ) {
248     char rss[1024];
249     snprintf(rss, sizeof(rss),"wanted integer for <y>, but got \"%s\" in \"<toglname> xc_B1motion <x> <y>\" command", argv[3]);
250     Tcl_SetResult(interp, rss, TCL_VOLATILE);
251     return TCL_ERROR;
252   }
253 
254   /********************************************/
255   /* .MESA ---------------------------------- */
256   /********************************************/
257   if ( togl == mesa_togl ) {
258     /* if structure is not opened, do noting, just return silently */
259     if (!VPf.stropened) return TCL_OK;
260 
261     /* if B1-Motion just started, then new:=old and return silently */
262     if (tr.b1motion == 0) {
263       tr.b1motion = 1;
264       tr.xrotold = xrotnew;
265       tr.yrotold = yrotnew;
266       return TCL_OK;
267     }
268 
269     /* how much did Xrot&Yrot differ from last time this routine was called */
270     dx = xrotnew - tr.xrotold;
271     dy = yrotnew - tr.yrotold;
272     tr.xrotold = xrotnew;
273     tr.yrotold = yrotnew;
274 
275     if ( xcr.lforce ) {
276       if ( MVf.structsize > 1e-5 ) ssize = MVf.structsize * VPf.VPfactor;
277       else ssize = ort.size * VPf.VPfactor;
278     } else {
279       ssize = ort.size * VPf.VPfactor;
280     }
281 
282     /* moving Mouse in X direction means rotation in Y dir !!!!! */
283     /*
284      * 4.0 is just some emperical factor to maximize performance of
285      * mouse rotation so, that it will follow mouse pointer
286      */
287     if ( dx != 0 ) fiY = atan( 4.0 * (double) dx / ssize);
288     if ( dy != 0 ) fiX = atan( 4.0 * (double) dy / ssize);
289     /* now rotate */
290     xcRotateXY( fiX, fiY );
291     /* if dimType == XC_2D, also orientate */
292     if ( dimType == XC_2D )
293       hpsort_index1(tmp_nobjects, zorient, iwksp);
294   } else {
295     /*********************/
296     /* NEW NICER FASHION */
297     /*********************/
298     NEW_WIN_CONTEXT *wc;
299     wc = FindWinContextByTogl( togl );
300 
301     /* if structure is not opened, do noting, just return silently */
302     if (!wc->VPf.stropened) return TCL_OK;
303 
304     /* if B1-Motion just started, then new:=old and return silently */
305     if (wc->tr.b1motion == 0) {
306       wc->tr.b1motion = 1;
307       wc->tr.xrotold = xrotnew;
308       wc->tr.yrotold = yrotnew;
309       return TCL_OK;
310     }
311 
312     /* how much did Xrot&Yrot differ from last time this routine was called */
313     dx = xrotnew - wc->tr.xrotold;
314     dy = yrotnew - wc->tr.yrotold;
315     wc->tr.xrotold = xrotnew;
316     wc->tr.yrotold = yrotnew;
317     ssize = wc->MVf.structsize * wc->VPf.VPfactor;
318 
319     /* moving Mouse in X direction means rotation in Y dir !!!!! */
320     /*
321      * 4.0 is just some emperical factor to maximize performance of
322      * mouse rotation so, that it will follow mouse pointer
323      */
324     if ( dx != 0 ) fiY = atan( 4.0 * (double) dx / ssize);
325     if ( dy != 0 ) fiX = atan( 4.0 * (double) dy / ssize);
326     cryRotateXY( wc, fiX, fiY );
327   }
328   Togl_PostRedisplay(togl);
329 
330   return TCL_OK;
331 }
332 
333 
334 /*
335  * this function takes care <Shift-B1-Motion> binding -> that's ZOOMING
336  *
337  * It only register Shift-B1-Motion, so that the display-mode goes to
338  * crude mode.
339  *
340  */
341 int
XC_ShiftB1MotionCb(ClientData clientData,Tcl_Interp * interp,int argc,const char * argv[])342 XC_ShiftB1MotionCb(ClientData clientData, Tcl_Interp *interp,
343 		   int argc, const char *argv[])
344 {
345   Togl *togl;
346 
347   if (argc != 2) {
348     Tcl_SetResult(interp,
349 		  "Usage: xc_ShiftB1motion <toglname> ", TCL_STATIC);
350     return TCL_ERROR;
351   }
352 
353   if ( Togl_GetToglFromName(interp, argv[1], &togl) == TCL_ERROR ) {
354     char rss[1024];
355     snprintf(rss, sizeof(rss),
356 	     "couldn't find %s togl widget", argv[3]);
357     Tcl_SetResult(interp, rss, TCL_VOLATILE);
358     return TCL_ERROR;
359   }
360 
361   /********************************************/
362   /* .MESA ---------------------------------- */
363   /********************************************/
364   if ( togl == mesa_togl ) {
365     /* if structure is not opened, do noting, just return silently */
366     if (!VPf.stropened) return TCL_OK;
367 
368     /* if Shift-B1-Motion just started register that */
369     if (tr.shiftB1motion == 0) {
370       tr.shiftB1motion = 1;
371     }
372   } else {
373     /*********************/
374     /* NEW NICER FASHION */
375     /*********************/
376     NEW_WIN_CONTEXT *wc;
377     wc = FindWinContextByTogl( togl );
378 
379     /* if structure is not opened, do noting, just return silently */
380     if (!wc->VPf.stropened) return TCL_OK;
381 
382     /* INSERT CODE FOR REGISTERING ShiftB1Motion HERE */
383   }
384 
385   Togl_PostRedisplay(togl);
386 
387   return TCL_OK;
388 }
389 
390 
391 /*
392  * this function takes care <B2-Motion> binding -> that's TRANSLATION
393  */
394 int
XC_B2MotionCb(ClientData clientData,Tcl_Interp * interp,int argc,const char * argv[])395 XC_B2MotionCb(ClientData clientData, Tcl_Interp *interp,
396 	      int argc, const char *argv[])
397 {
398   int trX, trY; /* cuurent <x> <y> mouse pointer position */
399   int dx, dy;
400   Togl *togl;
401 
402   if (argc != 4) {
403     Tcl_SetResult(interp,
404 		  "Usage: xc_B2motion <toglname> <x> <y>", TCL_STATIC);
405     return TCL_ERROR;
406   }
407 
408   if ( Togl_GetToglFromName(interp, argv[1], &togl) == TCL_ERROR ) {
409     char rss[1024];
410     snprintf(rss, sizeof(rss),
411 	     "couldn't find %s togl widget", argv[3]);
412     Tcl_SetResult(interp, rss, TCL_VOLATILE);
413     return TCL_ERROR;
414   }
415 
416   if ( Tcl_GetInt(interp, argv[2], &trX) == TCL_ERROR ) {
417     char rss[1024];
418     snprintf(rss, sizeof(rss),"wanted integer for <x>, but got \"%s\" in \"<toglname> xc_B2motion <x> <y>\" command", argv[2]);
419     Tcl_SetResult(interp, rss, TCL_VOLATILE);
420     return TCL_ERROR;
421   }
422 
423   if ( Tcl_GetInt(interp, argv[3], &trY) == TCL_ERROR ) {
424     char rss[1024];
425     snprintf(rss, sizeof(rss),"wanted integer for <y>, but got \"%s\" in \"<toglname> xc_B2motion <x> <y>\" command", argv[3]);
426     Tcl_SetResult(interp, rss, TCL_VOLATILE);
427     return TCL_ERROR;
428   }
429 
430 
431   /********************************************/
432   /* .MESA ---------------------------------- */
433   /********************************************/
434   if ( togl == mesa_togl ) {
435     /* if structure is not opened, do noting, just return silently */
436     if (!VPf.stropened) return TCL_OK;
437 
438     /* if B2-Motion just started, then new -> old and return silently */
439     if (tr.b2motion == 0) {
440       tr.b2motion = 1;
441       tr.trXold = trX;
442       tr.trYold = trY;
443       return TCL_OK;
444     }
445     /* how much did trX & trY differ from last time this routine was called */
446     dx = trX - tr.trXold;
447     dy = trY - tr.trYold;
448     tr.trXold = trX;
449     tr.trYold = trY;
450     /*printf("dx=%d, dy=%d", dx, dy);
451       fflush(stdout);*/
452 
453     tr.xtransl += dx; /* this is used in ViewPort to determine where the
454 		       * structure has been translated */
455     tr.ytransl -= dy; /* in X11 Y-coor is upside down */
456 
457     /* ViewPort */
458     xcViewPort();
459     /* Update a Display */
460   } else {
461     /*********************/
462     /* NEW NICER FASHION */
463     /*********************/
464     NEW_WIN_CONTEXT *wc;
465     wc = FindWinContextByTogl( togl );
466 
467     /* if structure is not opened, do noting, just return silently */
468     if (!wc->VPf.stropened) return TCL_OK;
469 
470     /* INSERT CODE FOR TRANSLATION HERE */
471   }
472 
473   Togl_PostRedisplay(togl);
474 
475   return TCL_OK;
476 }
477 
478 
479 /*
480  * this function takes care of BUTTON_RELEASE events
481  */
482 int
XC_ButtonReleaseCb(ClientData clientData,Tcl_Interp * interp,int argc,const char * argv[])483 XC_ButtonReleaseCb(ClientData clientData, Tcl_Interp *interp,
484 		   int argc, const char *argv[])
485 {
486   Togl *togl;
487 
488   if (argc != 3) {
489     Tcl_SetResult(interp,
490 		  "Usage: xc_Brelease <toglname> <B1|B2|Shift-B1>", TCL_STATIC);
491     return TCL_ERROR;
492   }
493 
494   if ( Togl_GetToglFromName(interp, argv[1], &togl) == TCL_ERROR ) {
495     char rss[1024];
496     snprintf(rss, sizeof(rss),
497 	     "couldn't find %s togl widget", argv[3]);
498     Tcl_SetResult(interp, rss, TCL_VOLATILE);
499     return TCL_ERROR;
500   }
501 
502   /********************************************/
503   /* .MESA ---------------------------------- */
504   /********************************************/
505   if ( togl == mesa_togl ) {
506     if ( strcmp(argv[2],"B1") == 0 || strcmp(argv[2],"Shift-B1") == 0 ) {
507       tr.b1motion = 0;
508       tr.shiftB1motion = 0;
509     }
510     else if ( strcmp(argv[2],"B2") == 0 ) tr.b2motion = 0;
511     /*else if ( strcmp(argv[2],"Shift-B1") == 0 ) tr.shiftB1motion = 0;*/
512     else {
513       Tcl_SetResult(interp,
514 		    "Usage: <toglname> xc_Brelease <B1|B2|Shift-B1>", TCL_STATIC);
515       return TCL_ERROR;
516     }
517   } else {
518     /*********************/
519     /* NEW NICER FASHION */
520     /*********************/
521     NEW_WIN_CONTEXT *wc;
522     wc = FindWinContextByTogl( togl );
523 
524     if ( strcmp(argv[2],"B1") == 0 || strcmp(argv[2],"Shift-B1") == 0 )  {
525       wc->tr.b1motion = 0;
526       wc->tr.shiftB1motion = 0;
527     }
528     else if ( strcmp(argv[2],"B2") == 0 )
529       wc->tr.b2motion = 0;
530     /* else if ( strcmp(argv[2],"Shift-B1") == 0 ) wc->tr.shiftB1motion = 0; */
531     else {
532       char rss[1024];
533       snprintf(rss, sizeof(rss), "wrong mode %s, must be B1, B2, or Shift-B1", argv[2]);
534       Tcl_SetResult(interp, rss, TCL_VOLATILE);
535       /* Tcl_SetResult(interp,
536 	 "Usage: <toglname> xc_Brelease <B1|B2|Shift-B1>", TCL_STATIC);*/
537       return TCL_ERROR;
538     }
539   }
540 
541   return TCL_OK;
542 }
543