Merge branch 'release-4-6'
[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, 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 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <math.h>
42 #include "sysstuff.h"
43 #include <string.h>
44 #include "smalloc.h"
45 #include "macros.h"
46 #include "xutil.h"
47 #include "3dview.h"
48 #include "gmx_fatal.h"
49 #include "buttons.h"
50 #include "manager.h"
51 #include "nmol.h"
52 #include "vec.h"
53 #include "txtdump.h"
54 #include "pbc.h"
55
56 #define MSIZE 4
57
58 static bool MWCallBack(t_x11 *x11, XEvent *event, Window /*w*/, void *data)
59 {
60     t_molwin *mw;
61     Window    To;
62     XEvent    letter;
63
64     mw                          = (t_molwin *)data;
65     To                          = mw->wd.Parent;
66     letter.type                 = ClientMessage;
67     letter.xclient.display      = x11->disp;
68     letter.xclient.window       = To;
69     letter.xclient.message_type = 0;
70     letter.xclient.format       = 32;
71     switch (event->type)
72     {
73         case Expose:
74             /* Do Not draw anything, but signal parent instead, he will
75              * coordinate drawing.
76              */
77             letter.xclient.data.l[0] = IDDRAWMOL;
78             letter.xclient.data.l[1] = Button1;
79             XSendEvent(x11->disp, To, True, 0, &letter);
80             break;
81         case ButtonPress:
82 #ifdef DEBUG
83             printf("Molwindow: Buttonpress\n");
84 #endif
85             letter.xclient.data.l[0] = IDLABEL;
86             letter.xclient.data.l[1] = (long)event->xbutton.button;
87             letter.xclient.data.l[2] = event->xbutton.x;
88             letter.xclient.data.l[3] = event->xbutton.y;
89             XSendEvent(x11->disp, To, True, 0, &letter);
90             break;
91         case ConfigureNotify:
92             mw->wd.width  = event->xconfigure.width;
93             mw->wd.height = event->xconfigure.height;
94             break;
95         default:
96             break;
97     }
98     return false;
99 }
100
101 void set_def (t_molwin *mw, int ePBC, matrix box)
102 {
103     mw->bShowHydrogen = true;
104     mw->bond_type     = eBFat;
105     mw->ePBC          = ePBC;
106     mw->boxtype       = esbRect;
107     mw->realbox       = TRICLINIC(box) ? esbTri : esbRect;
108 }
109
110 t_molwin *init_mw(t_x11 *x11, Window Parent,
111                   int x, int y, int width, int height,
112                   unsigned long fg, unsigned long bg,
113                   int ePBC, matrix box)
114 {
115     t_molwin *mw;
116
117     snew(mw, 1);
118     set_def(mw, ePBC, box);
119
120     InitWin(&mw->wd, x, y, width, height, 1, "Mol Window");
121
122     mw->wd.Parent = Parent;
123     mw->wd.self   = XCreateSimpleWindow(x11->disp, Parent, x, y, width, height, 1, fg, bg);
124     x11->RegisterCallback(x11, mw->wd.self, Parent, MWCallBack, mw);
125     x11->SetInputMask(x11, mw->wd.self,
126                       ExposureMask | StructureNotifyMask |
127                       ButtonPressMask);
128     return mw;
129 }
130
131 void map_mw(t_x11 *x11, t_molwin *mw)
132 {
133     XMapWindow(x11->disp, mw->wd.self);
134 }
135
136 bool toggle_hydrogen(t_x11 *x11, t_molwin *mw)
137 {
138     mw->bShowHydrogen = !mw->bShowHydrogen;
139     ExposeWin(x11->disp, mw->wd.self);
140
141     return mw->bShowHydrogen;
142 }
143
144 void set_bond_type(t_x11 *x11, t_molwin *mw, int bt)
145 {
146     if (bt != mw->bond_type)
147     {
148         mw->bond_type = bt;
149         ExposeWin(x11->disp, mw->wd.self);
150     }
151 }
152
153 void set_box_type (t_x11 *x11, t_molwin *mw, int bt)
154 {
155 #ifdef DEBUG
156     fprintf(stderr, "mw->boxtype = %d, bt = %d\n", mw->boxtype, bt);
157 #endif
158     if (bt != mw->boxtype)
159     {
160         if ((bt == esbTrunc && mw->realbox == esbTri) || bt == esbTri || bt == esbNone)
161         {
162             mw->boxtype = bt;
163             ExposeWin(x11->disp, mw->wd.self);
164         }
165         else
166         {
167             fprintf(stderr, "Can not change rectangular box to truncated octahedron\n");
168         }
169     }
170 }
171
172 void done_mw(t_x11 *x11, t_molwin *mw)
173 {
174     x11->UnRegisterCallback(x11, mw->wd.self);
175     sfree(mw);
176 }
177
178 static void draw_atom(Display *disp, Window w, GC gc,
179                       atom_id ai, iv2 vec2[], unsigned long col[], int size[],
180                       bool bBall, bool bPlus)
181 {
182     int xi, yi;
183
184     xi = vec2[ai][XX];
185     yi = vec2[ai][YY];
186     XSetForeground(disp, gc, col[ai]);
187     if (bBall)
188     {
189         XFillCircle(disp, w, gc, xi, yi, size[ai]-1);
190         XSetForeground(disp, gc, BLACK);
191         XDrawCircle(disp, w, gc, xi, yi, size[ai]);
192         /*    XSetForeground(disp,gc,WHITE);
193            XFillCircle(disp,w,gc,xi+4,yi-4,4); */
194     }
195     else if (bPlus)
196     {
197         XDrawLine(disp, w, gc, xi-MSIZE, yi, xi+MSIZE+1, yi);
198         XDrawLine(disp, w, gc, xi, yi-MSIZE, xi, yi+MSIZE+1);
199     }
200     else
201     {
202         XDrawLine(disp, w, gc, xi-1, yi, xi+1, yi);
203     }
204
205 }
206
207 /* Global variables */
208 static rvec gl_fbox, gl_hbox, gl_mhbox;
209
210 static void my_init_pbc(matrix box)
211 {
212     int i;
213
214     for (i = 0; (i < DIM); i++)
215     {
216         gl_fbox[i]  =  box[i][i];
217         gl_hbox[i]  =  gl_fbox[i]*0.5;
218         gl_mhbox[i] = -gl_hbox[i];
219     }
220 }
221
222 static bool local_pbc_dx(rvec x1, rvec x2)
223 {
224     int  i;
225     real dx;
226
227     for (i = 0; (i < DIM); i++)
228     {
229         dx = x1[i]-x2[i];
230         if (dx > gl_hbox[i])
231         {
232             return false;
233         }
234         else if (dx <= gl_mhbox[i])
235         {
236             return false;
237         }
238     }
239     return true;
240 }
241
242 static void draw_bond(Display *disp, Window w, GC gc,
243                       atom_id ai, atom_id aj, iv2 vec2[],
244                       rvec x[], unsigned long col[], int size[], bool bBalls)
245 {
246     unsigned long   ic, jc;
247     int             xi, yi, xj, yj;
248     int             xm, ym;
249
250     if (bBalls)
251     {
252         draw_atom(disp, w, gc, ai, vec2, col, size, true, false);
253         draw_atom(disp, w, gc, aj, vec2, col, size, true, false);
254     }
255     else
256     {
257         if (local_pbc_dx(x[ai], x[aj]))
258         {
259             ic = col[ai];
260             jc = col[aj];
261             xi = vec2[ai][XX];
262             yi = vec2[ai][YY];
263             xj = vec2[aj][XX];
264             yj = vec2[aj][YY];
265
266             if (ic != jc)
267             {
268                 xm = (xi+xj) >> 1;
269                 ym = (yi+yj) >> 1;
270
271                 XSetForeground(disp, gc, ic);
272                 XDrawLine(disp, w, gc, xi, yi, xm, ym);
273                 XSetForeground(disp, gc, jc);
274                 XDrawLine(disp, w, gc, xm, ym, xj, yj);
275             }
276             else
277             {
278                 XSetForeground(disp, gc, ic);
279                 XDrawLine(disp, w, gc, xi, yi, xj, yj);
280             }
281         }
282     }
283 }
284
285 int compare_obj(const void *a, const void *b)
286 {
287     t_object *oa, *ob;
288     real      z;
289
290     oa = (t_object *)a;
291     ob = (t_object *)b;
292
293     z = oa->z-ob->z;
294
295     if (z < 0)
296     {
297         return 1;
298     }
299     else if (z > 0)
300     {
301         return -1;
302     }
303     else
304     {
305         return 0;
306     }
307 }
308
309 void create_visibility(t_manager *man)
310 {
311     t_object *obj;
312     int       i;
313
314     for (i = 0, obj = man->obj; (i < man->nobj); i++, obj++)
315     {
316         if (obj->eV != eVHidden)
317         {
318             man->bVis[obj->ai] = true;
319             switch (obj->eO)
320             {
321                 case eOBond:
322                 case eOHBond:
323                     man->bVis[obj->aj] = true;
324                     break;
325                 default:
326                     break;
327             }
328         }
329     }
330 }
331
332 void z_fill(t_manager *man, real *zz)
333 {
334     t_object *obj;
335     int       i;
336
337     for (i = 0, obj = man->obj; (i < man->nobj); i++, obj++)
338     {
339         switch (obj->eO)
340         {
341             case eOSingle:
342                 obj->z = zz[obj->ai];
343                 break;
344             case eOBond:
345             case eOHBond:
346                 obj->z = (zz[obj->ai] + zz[obj->aj]) * 0.5;
347                 break;
348             default:
349                 break;
350         }
351     }
352 }
353
354 int filter_vis(t_manager *man)
355 {
356     int          i, nobj, nvis, nhide;
357     atom_id      ai;
358     bool         bAdd, *bVis;
359     t_object    *obj;
360     t_object    *newobj;
361
362     nobj = man->nobj;
363     snew(newobj, nobj);
364     obj   = man->obj;
365     bVis  = man->bVis;
366     nvis  = 0;
367     nhide = nobj-1;
368     for (i = 0; (i < nobj); i++, obj++)
369     {
370         ai   = obj->ai;
371         bAdd = bVis[ai];
372         if (bAdd)
373         {
374             if (obj->eO != eOSingle)
375             {
376                 bAdd = bVis[obj->aj];
377             }
378         }
379         if (bAdd)
380         {
381             newobj[nvis++] = *obj;
382         }
383         else
384         {
385             newobj[nhide--] = *obj;
386         }
387     }
388     sfree(man->obj);
389     man->obj = newobj;
390
391     return nvis;
392 }
393
394 void draw_objects(Display *disp, Window w, GC gc, int nobj,
395                   t_object objs[], iv2 vec2[], rvec x[],
396                   unsigned long col[], int size[], bool bShowHydro, int bond_type,
397                   bool bPlus)
398 {
399     bool         bBalls;
400     int          i;
401     t_object    *obj;
402
403     bBalls = false;
404     switch (bond_type)
405     {
406         case eBThin:
407             XSetLineAttributes(disp, gc, 1, LineSolid, CapNotLast, JoinRound);
408             break;
409         case eBFat:
410             XSetLineAttributes(disp, gc, 3, LineSolid, CapNotLast, JoinRound);
411             break;
412         case eBVeryFat:
413             XSetLineAttributes(disp, gc, 5, LineSolid, CapNotLast, JoinRound);
414             break;
415         case eBSpheres:
416             bBalls = true;
417             bPlus  = false;
418             break;
419         default:
420             gmx_fatal(FARGS, "Invalid bond_type selected: %d\n", bond_type);
421             break;
422     }
423     for (i = 0; (i < nobj); i++)
424     {
425         obj = &(objs[i]);
426         switch (obj->eO)
427         {
428             case eOSingle:
429                 draw_atom(disp, w, gc, obj->ai, vec2, col, size, bBalls, bPlus);
430                 break;
431             case eOBond:
432                 draw_bond(disp, w, gc, obj->ai, obj->aj, vec2, x, col, size, bBalls);
433                 break;
434             case eOHBond:
435                 if (bShowHydro)
436                 {
437                     draw_bond(disp, w, gc, obj->ai, obj->aj, vec2, x, col, size, bBalls);
438                 }
439                 break;
440             default:
441                 break;
442         }
443     }
444     XSetLineAttributes(disp, gc, 1, LineSolid, CapNotLast, JoinRound);
445 }
446
447 static void v4_to_iv2(vec4 x4, iv2 v2, int x0, int y0, real sx, real sy)
448 {
449     real inv_z;
450
451     inv_z  = 1.0/x4[ZZ];
452     v2[XX] = x0+sx*x4[XX]*inv_z;
453     v2[YY] = y0-sy*x4[YY]*inv_z;
454 }
455
456 static void draw_box(t_x11 *x11, Window w, t_3dview *view, matrix box,
457                      int x0, int y0, real sx, real sy, int boxtype)
458 {
459     rvec        rect_tri[8] =  {
460         { 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 },
461         { 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 }
462     };
463     int         tr_bonds[12][2] = {
464         { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 },
465         { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 },
466         { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }
467     };
468     static int *edge = NULL;
469     int         i, j, k, i0, i1;
470     rvec        corner[NCUCEDGE], box_center;
471     vec4        x4;
472     iv2         vec2[NCUCEDGE];
473
474     calc_box_center(view->ecenter, box, box_center);
475     if (boxtype == esbTrunc)
476     {
477         calc_compact_unitcell_vertices(view->ecenter, box, corner);
478         if (edge == NULL)
479         {
480             edge = compact_unitcell_edges();
481         }
482
483         for (i = 0; (i < NCUCEDGE); i++)
484         {
485             m4_op(view->proj, corner[i], x4);
486             v4_to_iv2(x4, vec2[i], x0, y0, sx, sy);
487         }
488         XSetForeground(x11->disp, x11->gc, YELLOW);
489         for (i = 0; i < NCUCEDGE; i++)
490         {
491             i0 = edge[2*i];
492             i1 = edge[2*i+1];
493             XDrawLine(x11->disp, w, x11->gc,
494                       vec2[i0][XX], vec2[i0][YY], vec2[i1][XX], vec2[i1][YY]);
495         }
496     }
497     else
498     {
499         if (boxtype == esbRect)
500         {
501             for (j = 0; (j < DIM); j++)
502             {
503                 box_center[j] -= 0.5*box[j][j];
504             }
505         }
506         else
507         {
508             for (i = 0; (i < DIM); i++)
509             {
510                 for (j = 0; (j < DIM); j++)
511                 {
512                     box_center[j] -= 0.5*box[i][j];
513                 }
514             }
515         }
516         for (i = 0; (i < 8); i++)
517         {
518             clear_rvec(corner[i]);
519             for (j = 0; (j < DIM); j++)
520             {
521                 if (boxtype == esbTri)
522                 {
523                     for (k = 0; (k < DIM); k++)
524                     {
525                         corner[i][k] += rect_tri[i][j]*box[j][k];
526                     }
527                 }
528                 else
529                 {
530                     corner[i][j] = rect_tri[i][j]*box[j][j];
531                 }
532             }
533             rvec_inc(corner[i], box_center);
534             m4_op(view->proj, corner[i], x4);
535             v4_to_iv2(x4, vec2[i], x0, y0, sx, sy);
536         }
537         if (debug)
538         {
539             pr_rvecs(debug, 0, "box", box, DIM);
540             pr_rvecs(debug, 0, "corner", corner, 8);
541         }
542         XSetForeground(x11->disp, x11->gc, YELLOW);
543         for (i = 0; (i < 12); i++)
544         {
545             i0 = tr_bonds[i][0];
546             i1 = tr_bonds[i][1];
547             XDrawLine(x11->disp, w, x11->gc,
548                       vec2[i0][XX], vec2[i0][YY], vec2[i1][XX], vec2[i1][YY]);
549         }
550     }
551 }
552
553 void set_sizes(t_manager *man)
554 {
555     for (int i = 0; i < man->natom; i++)
556     {
557         if (man->bVis[i])
558         {
559             man->size[i] = 180*man->vdw[i];
560         }
561     }
562 }
563
564 void draw_mol(t_x11 *x11, t_manager *man)
565 {
566     static char tstr[2][20];
567     static int  ntime = 0;
568     t_windata  *win;
569     t_3dview   *view;
570     t_molwin   *mw;
571     int         i, x0, y0, nvis;
572     iv2        *vec2;
573     real        sx, sy;
574     vec4        x4;
575
576     if (!man->status)
577     {
578         return;
579     }
580
581     view = man->view;
582     mw   = man->molw;
583
584     win = &(mw->wd);
585
586     vec2 = man->ix;
587     x0   = win->width/2;
588     y0   = win->height/2;
589     sx   = win->width/2*view->sc_x;
590     sy   = win->height/2*view->sc_y;
591
592     my_init_pbc(man->box);
593
594     /* create_visibility(man); */
595
596     for (i = 0; (i < man->natom); i++)
597     {
598         if (man->bVis[i])
599         {
600             m4_op(view->proj, man->x[i], x4);
601             man->zz[i] = x4[ZZ];
602             v4_to_iv2(x4, vec2[i], x0, y0, sx, sy);
603         }
604     }
605     set_sizes(man);
606
607     z_fill (man, man->zz);
608
609     /* Start drawing */
610     XClearWindow(x11->disp, win->self);
611
612     /* Draw Time */
613     sprintf(tstr[ntime], "Time: %.3f ps", man->time);
614     if (strcmp(tstr[ntime], tstr[1-ntime]) != 0)
615     {
616         set_vbtime(x11, man->vbox, tstr[ntime]);
617         ntime = 1-ntime;
618     }
619
620     if (mw->boxtype != esbNone)
621     {
622         draw_box(x11, win->self, view, man->box, x0, y0, sx, sy, mw->boxtype);
623     }
624
625     /* Should sort on Z-Coordinates here! */
626     nvis = filter_vis(man);
627     if (nvis && man->bSort)
628     {
629         qsort(man->obj, nvis, sizeof(man->obj[0]), compare_obj);
630     }
631
632     /* Draw the objects */
633     draw_objects(x11->disp, win->self, x11->gc,
634                  nvis, man->obj, man->ix, man->x, man->col, man->size,
635                  mw->bShowHydrogen, mw->bond_type, man->bPlus);
636
637     /* Draw the labels */
638     XSetForeground(x11->disp, x11->gc, WHITE);
639     for (i = 0; (i < man->natom); i++)
640     {
641         if (man->bLabel[i] && man->bVis[i])
642         {
643             XDrawString(x11->disp, win->self, x11->gc, vec2[i][XX]+2, vec2[i][YY]-2,
644                         man->szLab[i], strlen(man->szLab[i]));
645         }
646     }
647
648     XSetForeground(x11->disp, x11->gc, x11->fg);
649 }