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