fc80cdfe93aa8ef612ded37bc3691f525b833b79
[alexxy/gromacs.git] / src / programs / view / nmol.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2013, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include "config.h"
40
41 #include <math.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "gromacs/legacyheaders/macros.h"
46 #include "gromacs/legacyheaders/txtdump.h"
47 #include "gromacs/pbcutil/pbc.h"
48
49 #include "gromacs/math/vec.h"
50 #include "gromacs/utility/fatalerror.h"
51 #include "gromacs/utility/smalloc.h"
52
53 #include "3dview.h"
54 #include "buttons.h"
55 #include "manager.h"
56 #include "nmol.h"
57 #include "xutil.h"
58
59 #define MSIZE 4
60
61 static bool MWCallBack(t_x11 *x11, XEvent *event, Window /*w*/, void *data)
62 {
63     t_molwin *mw;
64     Window    To;
65     XEvent    letter;
66
67     mw                          = (t_molwin *)data;
68     To                          = mw->wd.Parent;
69     letter.type                 = ClientMessage;
70     letter.xclient.display      = x11->disp;
71     letter.xclient.window       = To;
72     letter.xclient.message_type = 0;
73     letter.xclient.format       = 32;
74     switch (event->type)
75     {
76         case Expose:
77             /* Do Not draw anything, but signal parent instead, he will
78              * coordinate drawing.
79              */
80             letter.xclient.data.l[0] = IDDRAWMOL;
81             letter.xclient.data.l[1] = Button1;
82             XSendEvent(x11->disp, To, True, 0, &letter);
83             break;
84         case ButtonPress:
85 #ifdef DEBUG
86             printf("Molwindow: Buttonpress\n");
87 #endif
88             letter.xclient.data.l[0] = IDLABEL;
89             letter.xclient.data.l[1] = (long)event->xbutton.button;
90             letter.xclient.data.l[2] = event->xbutton.x;
91             letter.xclient.data.l[3] = event->xbutton.y;
92             XSendEvent(x11->disp, To, True, 0, &letter);
93             break;
94         case ConfigureNotify:
95             mw->wd.width  = event->xconfigure.width;
96             mw->wd.height = event->xconfigure.height;
97             break;
98         default:
99             break;
100     }
101     return false;
102 }
103
104 void set_def (t_molwin *mw, int ePBC, matrix box)
105 {
106     mw->bShowHydrogen = true;
107     mw->bond_type     = eBFat;
108     mw->ePBC          = ePBC;
109     mw->boxtype       = esbRect;
110     mw->realbox       = TRICLINIC(box) ? esbTri : esbRect;
111 }
112
113 t_molwin *init_mw(t_x11 *x11, Window Parent,
114                   int x, int y, int width, int height,
115                   unsigned long fg, unsigned long bg,
116                   int ePBC, matrix box)
117 {
118     t_molwin *mw;
119
120     snew(mw, 1);
121     set_def(mw, ePBC, box);
122
123     InitWin(&mw->wd, x, y, width, height, 1, "Mol Window");
124
125     mw->wd.Parent = Parent;
126     mw->wd.self   = XCreateSimpleWindow(x11->disp, Parent, x, y, width, height, 1, fg, bg);
127     x11->RegisterCallback(x11, mw->wd.self, Parent, MWCallBack, mw);
128     x11->SetInputMask(x11, mw->wd.self,
129                       ExposureMask | StructureNotifyMask |
130                       ButtonPressMask);
131     return mw;
132 }
133
134 void map_mw(t_x11 *x11, t_molwin *mw)
135 {
136     XMapWindow(x11->disp, mw->wd.self);
137 }
138
139 bool toggle_hydrogen(t_x11 *x11, t_molwin *mw)
140 {
141     mw->bShowHydrogen = !mw->bShowHydrogen;
142     ExposeWin(x11->disp, mw->wd.self);
143
144     return mw->bShowHydrogen;
145 }
146
147 void set_bond_type(t_x11 *x11, t_molwin *mw, int bt)
148 {
149     if (bt != mw->bond_type)
150     {
151         mw->bond_type = bt;
152         ExposeWin(x11->disp, mw->wd.self);
153     }
154 }
155
156 void set_box_type (t_x11 *x11, t_molwin *mw, int bt)
157 {
158 #ifdef DEBUG
159     fprintf(stderr, "mw->boxtype = %d, bt = %d\n", mw->boxtype, bt);
160 #endif
161     if (bt != mw->boxtype)
162     {
163         if ((bt == esbTrunc && mw->realbox == esbTri) || bt == esbTri || bt == esbNone)
164         {
165             mw->boxtype = bt;
166             ExposeWin(x11->disp, mw->wd.self);
167         }
168         else
169         {
170             fprintf(stderr, "Can not change rectangular box to truncated octahedron\n");
171         }
172     }
173 }
174
175 void done_mw(t_x11 *x11, t_molwin *mw)
176 {
177     x11->UnRegisterCallback(x11, mw->wd.self);
178     sfree(mw);
179 }
180
181 static void draw_atom(Display *disp, Window w, GC gc,
182                       atom_id ai, iv2 vec2[], unsigned long col[], int size[],
183                       bool bBall, bool bPlus)
184 {
185     int xi, yi;
186
187     xi = vec2[ai][XX];
188     yi = vec2[ai][YY];
189     XSetForeground(disp, gc, col[ai]);
190     if (bBall)
191     {
192         XFillCircle(disp, w, gc, xi, yi, size[ai]-1);
193         XSetForeground(disp, gc, BLACK);
194         XDrawCircle(disp, w, gc, xi, yi, size[ai]);
195         /*    XSetForeground(disp,gc,WHITE);
196            XFillCircle(disp,w,gc,xi+4,yi-4,4); */
197     }
198     else if (bPlus)
199     {
200         XDrawLine(disp, w, gc, xi-MSIZE, yi, xi+MSIZE+1, yi);
201         XDrawLine(disp, w, gc, xi, yi-MSIZE, xi, yi+MSIZE+1);
202     }
203     else
204     {
205         XDrawLine(disp, w, gc, xi-1, yi, xi+1, yi);
206     }
207
208 }
209
210 /* Global variables */
211 static rvec gl_fbox, gl_hbox, gl_mhbox;
212
213 static void my_init_pbc(matrix box)
214 {
215     int i;
216
217     for (i = 0; (i < DIM); i++)
218     {
219         gl_fbox[i]  =  box[i][i];
220         gl_hbox[i]  =  gl_fbox[i]*0.5;
221         gl_mhbox[i] = -gl_hbox[i];
222     }
223 }
224
225 static bool local_pbc_dx(rvec x1, rvec x2)
226 {
227     int  i;
228     real dx;
229
230     for (i = 0; (i < DIM); i++)
231     {
232         dx = x1[i]-x2[i];
233         if (dx > gl_hbox[i])
234         {
235             return false;
236         }
237         else if (dx <= gl_mhbox[i])
238         {
239             return false;
240         }
241     }
242     return true;
243 }
244
245 static void draw_bond(Display *disp, Window w, GC gc,
246                       atom_id ai, atom_id aj, iv2 vec2[],
247                       rvec x[], unsigned long col[], int size[], bool bBalls)
248 {
249     unsigned long   ic, jc;
250     int             xi, yi, xj, yj;
251     int             xm, ym;
252
253     if (bBalls)
254     {
255         draw_atom(disp, w, gc, ai, vec2, col, size, true, false);
256         draw_atom(disp, w, gc, aj, vec2, col, size, true, false);
257     }
258     else
259     {
260         if (local_pbc_dx(x[ai], x[aj]))
261         {
262             ic = col[ai];
263             jc = col[aj];
264             xi = vec2[ai][XX];
265             yi = vec2[ai][YY];
266             xj = vec2[aj][XX];
267             yj = vec2[aj][YY];
268
269             if (ic != jc)
270             {
271                 xm = (xi+xj) >> 1;
272                 ym = (yi+yj) >> 1;
273
274                 XSetForeground(disp, gc, ic);
275                 XDrawLine(disp, w, gc, xi, yi, xm, ym);
276                 XSetForeground(disp, gc, jc);
277                 XDrawLine(disp, w, gc, xm, ym, xj, yj);
278             }
279             else
280             {
281                 XSetForeground(disp, gc, ic);
282                 XDrawLine(disp, w, gc, xi, yi, xj, yj);
283             }
284         }
285     }
286 }
287
288 int compare_obj(const void *a, const void *b)
289 {
290     t_object *oa, *ob;
291     real      z;
292
293     oa = (t_object *)a;
294     ob = (t_object *)b;
295
296     z = oa->z-ob->z;
297
298     if (z < 0)
299     {
300         return 1;
301     }
302     else if (z > 0)
303     {
304         return -1;
305     }
306     else
307     {
308         return 0;
309     }
310 }
311
312 void z_fill(t_manager *man, real *zz)
313 {
314     t_object *obj;
315     int       i;
316
317     for (i = 0, obj = man->obj; (i < man->nobj); i++, obj++)
318     {
319         switch (obj->eO)
320         {
321             case eOSingle:
322                 obj->z = zz[obj->ai];
323                 break;
324             case eOBond:
325             case eOHBond:
326                 obj->z = (zz[obj->ai] + zz[obj->aj]) * 0.5;
327                 break;
328             default:
329                 break;
330         }
331     }
332 }
333
334 int filter_vis(t_manager *man)
335 {
336     int          i, nobj, nvis, nhide;
337     atom_id      ai;
338     bool         bAdd, *bVis;
339     t_object    *obj;
340     t_object    *newobj;
341
342     nobj = man->nobj;
343     snew(newobj, nobj);
344     obj   = man->obj;
345     bVis  = man->bVis;
346     nvis  = 0;
347     nhide = nobj-1;
348     for (i = 0; (i < nobj); i++, obj++)
349     {
350         ai   = obj->ai;
351         bAdd = bVis[ai];
352         if (bAdd)
353         {
354             if (obj->eO != eOSingle)
355             {
356                 bAdd = bVis[obj->aj];
357             }
358         }
359         if (bAdd)
360         {
361             newobj[nvis++] = *obj;
362         }
363         else
364         {
365             newobj[nhide--] = *obj;
366         }
367     }
368     sfree(man->obj);
369     man->obj = newobj;
370
371     return nvis;
372 }
373
374 void draw_objects(Display *disp, Window w, GC gc, int nobj,
375                   t_object objs[], iv2 vec2[], rvec x[],
376                   unsigned long col[], int size[], bool bShowHydro, int bond_type,
377                   bool bPlus)
378 {
379     bool         bBalls;
380     int          i;
381     t_object    *obj;
382
383     bBalls = false;
384     switch (bond_type)
385     {
386         case eBThin:
387             XSetLineAttributes(disp, gc, 1, LineSolid, CapNotLast, JoinRound);
388             break;
389         case eBFat:
390             XSetLineAttributes(disp, gc, 3, LineSolid, CapNotLast, JoinRound);
391             break;
392         case eBVeryFat:
393             XSetLineAttributes(disp, gc, 5, LineSolid, CapNotLast, JoinRound);
394             break;
395         case eBSpheres:
396             bBalls = true;
397             bPlus  = false;
398             break;
399         default:
400             gmx_fatal(FARGS, "Invalid bond_type selected: %d\n", bond_type);
401             break;
402     }
403     for (i = 0; (i < nobj); i++)
404     {
405         obj = &(objs[i]);
406         switch (obj->eO)
407         {
408             case eOSingle:
409                 draw_atom(disp, w, gc, obj->ai, vec2, col, size, bBalls, bPlus);
410                 break;
411             case eOBond:
412                 draw_bond(disp, w, gc, obj->ai, obj->aj, vec2, x, col, size, bBalls);
413                 break;
414             case eOHBond:
415                 if (bShowHydro)
416                 {
417                     draw_bond(disp, w, gc, obj->ai, obj->aj, vec2, x, col, size, bBalls);
418                 }
419                 break;
420             default:
421                 break;
422         }
423     }
424     XSetLineAttributes(disp, gc, 1, LineSolid, CapNotLast, JoinRound);
425 }
426
427 static void v4_to_iv2(vec4 x4, iv2 v2, int x0, int y0, real sx, real sy)
428 {
429     real inv_z;
430
431     inv_z  = 1.0/x4[ZZ];
432     v2[XX] = x0+sx*x4[XX]*inv_z;
433     v2[YY] = y0-sy*x4[YY]*inv_z;
434 }
435
436 static void draw_box(t_x11 *x11, Window w, t_3dview *view, matrix box,
437                      int x0, int y0, real sx, real sy, int boxtype)
438 {
439     rvec        rect_tri[8] =  {
440         { 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 },
441         { 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 }
442     };
443     int         tr_bonds[12][2] = {
444         { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 },
445         { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 },
446         { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }
447     };
448     static int *edge = NULL;
449     int         i, j, k, i0, i1;
450     rvec        corner[NCUCEDGE], box_center;
451     vec4        x4;
452     iv2         vec2[NCUCEDGE];
453
454     calc_box_center(view->ecenter, box, box_center);
455     if (boxtype == esbTrunc)
456     {
457         calc_compact_unitcell_vertices(view->ecenter, box, corner);
458         if (edge == NULL)
459         {
460             edge = compact_unitcell_edges();
461         }
462
463         for (i = 0; (i < NCUCEDGE); i++)
464         {
465             gmx_mat4_transform_point(view->proj, corner[i], x4);
466             v4_to_iv2(x4, vec2[i], x0, y0, sx, sy);
467         }
468         XSetForeground(x11->disp, x11->gc, YELLOW);
469         for (i = 0; i < NCUCEDGE; i++)
470         {
471             i0 = edge[2*i];
472             i1 = edge[2*i+1];
473             XDrawLine(x11->disp, w, x11->gc,
474                       vec2[i0][XX], vec2[i0][YY], vec2[i1][XX], vec2[i1][YY]);
475         }
476     }
477     else
478     {
479         if (boxtype == esbRect)
480         {
481             for (j = 0; (j < DIM); j++)
482             {
483                 box_center[j] -= 0.5*box[j][j];
484             }
485         }
486         else
487         {
488             for (i = 0; (i < DIM); i++)
489             {
490                 for (j = 0; (j < DIM); j++)
491                 {
492                     box_center[j] -= 0.5*box[i][j];
493                 }
494             }
495         }
496         for (i = 0; (i < 8); i++)
497         {
498             clear_rvec(corner[i]);
499             for (j = 0; (j < DIM); j++)
500             {
501                 if (boxtype == esbTri)
502                 {
503                     for (k = 0; (k < DIM); k++)
504                     {
505                         corner[i][k] += rect_tri[i][j]*box[j][k];
506                     }
507                 }
508                 else
509                 {
510                     corner[i][j] = rect_tri[i][j]*box[j][j];
511                 }
512             }
513             rvec_inc(corner[i], box_center);
514             gmx_mat4_transform_point(view->proj, corner[i], x4);
515             v4_to_iv2(x4, vec2[i], x0, y0, sx, sy);
516         }
517         if (debug)
518         {
519             pr_rvecs(debug, 0, "box", box, DIM);
520             pr_rvecs(debug, 0, "corner", corner, 8);
521         }
522         XSetForeground(x11->disp, x11->gc, YELLOW);
523         for (i = 0; (i < 12); i++)
524         {
525             i0 = tr_bonds[i][0];
526             i1 = tr_bonds[i][1];
527             XDrawLine(x11->disp, w, x11->gc,
528                       vec2[i0][XX], vec2[i0][YY], vec2[i1][XX], vec2[i1][YY]);
529         }
530     }
531 }
532
533 void set_sizes(t_manager *man)
534 {
535     for (int i = 0; i < man->natom; i++)
536     {
537         if (man->bVis[i])
538         {
539             man->size[i] = 180*man->vdw[i];
540         }
541     }
542 }
543
544 void draw_mol(t_x11 *x11, t_manager *man)
545 {
546     static char tstr[2][20];
547     static int  ntime = 0;
548     t_windata  *win;
549     t_3dview   *view;
550     t_molwin   *mw;
551     int         i, x0, y0, nvis;
552     iv2        *vec2;
553     real        sx, sy;
554     vec4        x4;
555
556     if (!man->status)
557     {
558         return;
559     }
560
561     view = man->view;
562     mw   = man->molw;
563
564     win = &(mw->wd);
565
566     vec2 = man->ix;
567     x0   = win->width/2;
568     y0   = win->height/2;
569     sx   = win->width/2*view->sc_x;
570     sy   = win->height/2*view->sc_y;
571
572     my_init_pbc(man->box);
573
574     for (i = 0; (i < man->natom); i++)
575     {
576         if (man->bVis[i])
577         {
578             gmx_mat4_transform_point(view->proj, man->x[i], x4);
579             man->zz[i] = x4[ZZ];
580             v4_to_iv2(x4, vec2[i], x0, y0, sx, sy);
581         }
582     }
583     set_sizes(man);
584
585     z_fill (man, man->zz);
586
587     /* Start drawing */
588     XClearWindow(x11->disp, win->self);
589
590     /* Draw Time */
591     sprintf(tstr[ntime], "Time: %.3f ps", man->time);
592     if (strcmp(tstr[ntime], tstr[1-ntime]) != 0)
593     {
594         set_vbtime(x11, man->vbox, tstr[ntime]);
595         ntime = 1-ntime;
596     }
597
598     if (mw->boxtype != esbNone)
599     {
600         draw_box(x11, win->self, view, man->box, x0, y0, sx, sy, mw->boxtype);
601     }
602
603     /* Should sort on Z-Coordinates here! */
604     nvis = filter_vis(man);
605     if (nvis && man->bSort)
606     {
607         qsort(man->obj, nvis, sizeof(man->obj[0]), compare_obj);
608     }
609
610     /* Draw the objects */
611     draw_objects(x11->disp, win->self, x11->gc,
612                  nvis, man->obj, man->ix, man->x, man->col, man->size,
613                  mw->bShowHydrogen, mw->bond_type, man->bPlus);
614
615     /* Draw the labels */
616     XSetForeground(x11->disp, x11->gc, WHITE);
617     for (i = 0; (i < man->natom); i++)
618     {
619         if (man->bLabel[i] && man->bVis[i])
620         {
621             XDrawString(x11->disp, win->self, x11->gc, vec2[i][XX]+2, vec2[i][YY]-2,
622                         man->szLab[i], strlen(man->szLab[i]));
623         }
624     }
625
626     XSetForeground(x11->disp, x11->gc, x11->fg);
627 }